Evaluate one exact agent action and bind it to a single-use execution grant
The grant resource. Decionis recomputes the SHA-256 hash of the
canonical execution intent, evaluates policy, and issues a
short-lived Ed25519 execution grant only for an ALLOW evaluated in
ENFORCEMENT mode. This is the handler of the deprecated alias
POST /v1/authority/enforce-and-bind, registered twice: every rule
documented there (Idempotency-Key binding, Presence and attestation
evidence, signed signals, MANAGED escalation, the verification
envelope by client version, context.decionis_policy, the
decision:shadow confinement, the scopes each mode needs and the
workspace-owner rule, the EXPECTED_EFFECT_DIGEST_MISSING warning, and
the BLOCK when a held allowance cannot be reserved) applies here
unchanged.
The issued grant is addressed afterwards as
/v1/authority/grants/{grantId}, where grantId is the grant's
jti: the execution_token_jti of this response, the jti recorded
for the grant, and the jti claim of the signed execution token.
POST /v1/authority/evaluate and POST /v1/authority/enforce, which
the legacy @decionis/sdk-node 0.1.0 calls and this API never served,
answer 410 LEGACY_AUTHORITY_ROUTE_RETIRED naming this route and
@decionis/sdk, which replaces that package.
executor (optional) says who will carry the grant out. It is never
part of the canonical intent, its hash, or the grant's binding. When
executor.style is permit and a grant was issued, the decision also
carries permit_id (the grant's jti), intent_sha256 (the intent
hash as bare hex, written as dossier_sha256 is) and expires_at
(execution_token_expires_at). Without executor, or with
dispatch, the response is exactly the enforce-and-bind response. A
MANAGED escalation's locator-only summary carries no grant, so it never
carries permit fields.
Authorization
ApiKeyAuth An organization API key. Its scopes decide what it may call;
decision:shadow is the narrowest: SHADOW evaluations through
POST /v1/authority/grants or its alias
POST /v1/authority/enforce-and-bind only (DECISION_SHADOW_MODE_DENIED
in any other mode; DECISION_SHADOW_FIELD_DENIED for a body carrying
context.decionis_policy, evidence or escalation), with no
integration dispatch. A key whose only scope is decision:shadow
(an AgentSafe hosted gateway's) reaches exactly those two,
POST /v1/auth/credential-context, POST /v1/hosted-gateway/keys/confirm
and POST /v1/hosted-gateway/keys/revoke-self; every other route
answers 403 HOSTED_GATEWAY_KEY_PATH_DENIED.
decision:enforce is an enforced hosted gateway's key: evaluations
in SHADOW or ENFORCEMENT through POST /v1/authority/grants or its
alias (400 DECISION_ENFORCE_FIELD_DENIED for a body carrying
context.decionis_policy; it never holds policy:write), and the
grants and managed escalations they bind. A key whose scopes are
decision:enforce (with or without decision:shadow) reaches exactly
POST /v1/authority/enforce-and-bind, POST /v1/authority/grants,
GET /v1/authority/grants/{grant_id},
POST /v1/authority/grants/{grant_id}/claims,
POST /v1/authority/grants/{grant_id}/receipts,
POST /v1/authority/grants/{grant_id}/reconciliations,
POST /v1/execution/verify-token, POST /v1/execution/claim-token,
POST /v1/execution/consume-token, POST /v1/execution/finalize-token,
GET /v1/authority/escalations/{escalation_id},
DELETE /v1/authority/escalations/{escalation_id},
POST /v1/auth/credential-context, POST /v1/hosted-gateway/keys/confirm
and POST /v1/hosted-gateway/keys/revoke-self, each id a UUID; every
other route answers 403 HOSTED_GATEWAY_KEY_PATH_DENIED.
In: header
Header Parameters
Request Body
application/json
uuiduuiddate-timeMust be later than captured_at and no more than five minutes after it.
date-timeThe trusted runtime's context, entirely inside the canonical intent hash. Open by design, with four reserved keys. idempotency_key is required and carries the retry identity. enforcement_boundary and workload are what an enforcement boundary reports about itself and about the software that proposed the action; each is validated against its schema when present, and an absent signal is an answer rather than a placeholder, so a policy that requires provenance refuses on its absence. agent_safe is what the runtime states about the action's own circumstances; Decionis reads only the sub-keys named here, each validated when present, into the facts it writes under the same name. runtime_attribution is reserved for Decionis, which computes it from those reports, and a request that supplies it is refused.
Exact policy-routing vocabulary committed into the intent. Protocol adapters use it when their evaluated decision type differs from the executable action type; legacy generic callers may omit it.
Digest-only commitment to the expected downstream state. Part of the hashed intent, so the authority never adds one. A Protocol 1.1 grant bound without it accepts no effect evidence at finalization or reconciliation (EFFECT_EXPECTED_BINDING_UNAVAILABLE), and the grant is issued with x-decionis-warning: EXPECTED_EFFECT_DIGEST_MISSING.
^sha256:[0-9a-f]{64}$SHA-256 of the UTF-8 canonical JSON ExecutionIntentBinding. Objects are recursively key-sorted using ECMAScript's default UTF-16 code-unit ordering and serialized with JSON.stringify semantics.
^sha256:[0-9a-f]{64}$Value in
- "SHADOW"
- "PARALLEL"
- "ENFORCEMENT"
Requests Decionis-owned Presence orchestration. This object is outside the canonical ExecutionIntentBinding hash and is included in request replay validation. It is valid only in ENFORCEMENT mode.
Who will carry out a granted action, and how it takes authority: a dispatch executor presents the execution token at the claim; a permit executor runs inside a platform that holds a single-use permit and reports its own native receipt. Delivery information only: never part of the canonical intent, its hash, or the grant's binding, and it changes nothing about the decision.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/authority/grants" \ -H "Content-Type: application/json" \ -d '{ "protocol_version": "agent-safe.intent/1", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "intent_id": "05704cef-194d-4987-b2cb-c2c20c9cd88a", "captured_at": "2019-08-24T14:15:22Z", "expires_at": "2019-08-24T14:15:22Z", "actor": { "id": "string", "type": "string" }, "action": { "type": "string", "resource": "string", "parameters": {} }, "context": {}, "downstream_target": { "system": "string", "operation": "string" }, "intent_hash": "string", "mode": "SHADOW" }'{ "decision_id": "string", "chain_id": "string", "status": "ALLOW", "should_execute": true, "authority_classification": "AUTHORITATIVE", "execution_eligible": true, "execution_binding_digest": "string", "reason_codes": [ "string" ], "action_hash": "string", "policy_version": "string", "mode": "SHADOW", "execution_token": "string", "execution_token_jti": "string", "execution_token_key_id": "string", "execution_token_expires_at": "2019-08-24T14:15:22Z", "dossier_id": "string", "dossier_sha256": "string", "dossier_url": "string", "approval_request_id": "string", "ledger_entry_id": "string", "managed_escalation": { "outcome": "ESCALATE_PENDING", "escalation_id": "stringst", "intent_id": "05704cef-194d-4987-b2cb-c2c20c9cd88a", "status": "PENDING_PRESENCE", "expires_at": "2019-08-24T14:15:22Z", "reason_codes": [ "string" ] }, "verification": { "verification_page_url": "http://example.com", "verification_url": "http://example.com", "verification_api_path": "string", "signature": "string", "signature_algorithm": "HMAC-SHA256", "signature_scheme": "hmac-v1", "link_expires_at": "2019-08-24T14:15:22Z", "link_exp": "string", "asymmetric_proof": {}, "asymmetric_proof_bundle": {} }, "escalation_requirement": { "requirement_id": "string", "expires_at": "2019-08-24T14:15:22Z", "selector": { "kind": "ROLE", "role_id": "string" }, "verification": { "provider": "string", "methods": [ "WEBAUTHN" ], "level": "STANDARD" } }, "action_manifest_digest": "string", "permit_id": "7af9675c-40fb-44dd-8fe4-46923dd592e6", "intent_sha256": "string", "expires_at": "2019-08-24T14:15:22Z"}Evaluate and bind one exact agent action to a single-use execution grant POST
Deprecated alias of `POST /v1/authority/grants`: the same handler, kept registered so AgentSafe 0.3.8, the Go govern client and installed SDKs keep working byte-for-byte. Removal is a later, separate decision. Agent-Safe enforcement route. Decionis recomputes the SHA-256 hash of the canonical execution intent, evaluates policy in ENFORCEMENT mode, and issues a short-lived Ed25519 execution grant only for an ALLOW evaluated in ENFORCEMENT mode. SHADOW records the same exact intent and dossier evidence without issuing an execution grant. When Idempotency-Key is supplied, it must exactly match the intent_id embedded in the signed intent; intent_id is the grant issuance boundary. The intent's own `context.idempotency_key` is hashed into the intent and is not this header: any other header value answers 400 AUTHORITY_IDEMPOTENCY_KEY_MISMATCH, with a `message` saying so. ESCALATE, REVIEW_REQUIRED, BLOCK, dependency errors, and binding errors never receive a grant. A Presence approval is accepted only after Decionis verifies the action-bound request and sealed receipt directly with Presence; approval then becomes evidence for a fresh policy evaluation, not authority by itself. A presence attestation (`evidence.humanApproval.provider` `attestation`) is a presence provider's compact JWS, typ `decionis-presence-attestation+jwt`, verified against a key the organisation registered at `/v1/execution/provider-keys` with purpose `presence_attestation` (see PresenceAttestationClaims). On success its facts are written to `agent_safe.human_approval` (provider, verified, method, approver_role, subject, attestation_id, key_id, approved_action_digest, approved_at, expires_at, and token_match / network_validated when the provider sent them) for policy rules to match, and the resulting ExecutionBinding records the attested approval. An ESCALATE becomes an ALLOW only when the organisation's policy has a rule matching those facts. A refused attestation is a 409 with one of the PresenceAttestationRefusal codes, and no evaluation runs. A signed producer signal (`evidence.signals[].token`) is a producer's compact JWS, typ `decionis-signed-signal+jwt`, standing for up to thirty days and bound to no intent: a cardholder's delegation to an agent, say (see SignedSignalClaims). Each is verified before any evaluation against a key the organisation registered at `/v1/execution/provider-keys` with purpose `signed_signal`, or against the JWKS of an admitted presence provider, and its facts are written to `agent_safe.signed_signals.<name>` (verified, producer, method, subject, signal_id, key_id, issued_at, expires_at, evidence_digest, what it asserted, and `covers` / `covers_intent`: what of this intent it covers). A policy names the fact with `required_signals` as a dotted path (`agent_safe.signed_signals.card_delegation`) and decides with rules; the ExecutionBinding records the facts, and the claim rebuilds them and re-checks the key and the expiry. Signals may come with or without a human approval, in any mode, and beside a managed escalation. A refused signal is a 409 with one of the SignedSignalRefusal codes; no evaluation runs and no event fires. The token itself is never recorded, only its SHA-256 as `evidence_digest`. A runtime that read the target before asking states what it read in `context.agent_safe.precondition` (ExecutionTargetPrecondition: `version`, optional `state`, `source`, `read_at`), inside the intent hash. Policy reads it as `agent_safe.precondition`, and a bundle's `allowed_transitions` read its `state`; the grant binds `version` as `binding.expected_state_version`, and the claim's attestation carries it to a verifying provider, which refuses the write (`TARGET_STATE_CHANGED`) once the target has moved on. A malformed precondition is a 400 `INTENT_PRECONDITION_INVALID`, and nothing is evaluated. An ESCALATE in ENFORCEMENT, direct or managed, is queued to the organisation's event subscribers as `authority.escalation_required`, carrying the policy's selector and who proves the person (the `verification` block: `provider`, `methods`, `level`); and once the intent is presented again with human approval evidence, the answer is queued as `authority.escalation_resolved` (the fresh decision's outcome, or REFUSED with the refusal code), with the proof's facts and never the proof. Delivery never affects a decision. The same routing rides on an ESCALATE decision as `escalation_requirement` for the runtime from agent-safe-pipeline/0.3.7 on, and for no other client. The action space the deciding policy release was checked against rides on the decision as `action_manifest_digest` (the release's `actionManifestDigest`, or null when no governed release decided or its release attached no ActionManifest) for the runtime from agent-safe-pipeline/0.5.1 on, and for no other client. The runtime compares it with the action space it loaded and reports a difference; it changes nothing about the decision. When escalation.mode is MANAGED and policy returns ESCALATE, Decionis persists the original exact intent and orchestrates the policy-issued Presence ceremony. The response remains non-authorizing and carries a managed_escalation status locator; no ExecutionBinding or execution token exists until signed Presence evidence is independently verified and current policy is evaluated again. The decision carries the dossier's `verification` envelope (the same share-safe page `evaluate-decision` attaches) for a client that can read it: one identifying itself as `agent-safe-pipeline/0.2.0` or later, or as `govern/2.0.0` or later (the workflow gate), in its User-Agent. Every other client receives the decision exactly as before, so a gate that mirrors this schema strictly never meets a field it does not know. On this operation, a `context.decionis_policy` is treated as on evaluate-decision: its `sha256` must be the SHA-256 of its `content`, and one that asks for `enforce: true` has its policy file published as the workspace's enforced policy only when the key holds `policy:write`, `policy:*` or `org:*`; for any other key the decision is unaffected, nothing is published, and the response carries `x-decionis-warning: POLICY_ENFORCE_SCOPE_MISSING`. No other operation whose body or answer carries an execution intent (verify-token, consume-token, claim-token, or the execution grant of /v1/ucp/checkout/authorize) publishes a policy file or sends that header, whatever the key holds. A key whose only evaluation scope is `decision:shadow` (an AgentSafe hosted gateway's) runs SHADOW and nothing else: any other mode answers 403 DECISION_SHADOW_MODE_DENIED, a body carrying `context.decionis_policy`, `evidence` or `escalation` answers 400 DECISION_SHADOW_FIELD_DENIED (naming the `field`), and the evaluation is recorded and signed without dispatching to the organization's integrations. A key whose evaluation scope is `decision:enforce` (an enforced AgentSafe hosted gateway's) runs SHADOW or ENFORCEMENT, may open a managed escalation, and answers 400 DECISION_ENFORCE_FIELD_DENIED for a body carrying `context.decionis_policy`: it never publishes policy. The scopes each mode needs: SHADOW takes one of `decision:write`, `decision:run`, `decision:*`, `org:*`, `decision:shadow` or `decision:enforce`; PARALLEL and ENFORCEMENT take one of `decision:write`, `decision:*`, `org:*` or `decision:enforce`, so a key that runs SHADOW can still answer 403 FORBIDDEN in ENFORCEMENT. The workspace counts too: a provisional workspace that no owner has claimed, or an AWS service workspace, runs SHADOW only, and any other mode answers 403 PROVISIONAL_WORKSPACE_SHADOW_ONLY (or SERVICE_WORKSPACE_SHADOW_ONLY) whatever the key holds, with a `message`. An owner claims a provisional workspace through `POST /v1/orgs/{org_id}/claim/start` and `/claim/verify`; claiming does not widen a key's scopes. A SHADOW success therefore says nothing about whether ENFORCEMENT will be accepted. When a grant is issued under Protocol 1.1 and the intent named no `expected_effect_digest`, the decision is unchanged and `x-decionis-warning` carries EXPECTED_EFFECT_DIGEST_MISSING: that grant accepts no effect evidence at finalization or reconciliation, so its effect can never be confirmed. A held allowance (`/v1/authority/allowances`) is a named, organisation-scoped counter that policy draws on: the rule that approved the action names it in `then.allowance`, with the amount fixed or read from `action.parameters` by JSON pointer, and the ExecutionBinding records the draw as `allowance_draws`. A grant is issued only once that amount is reserved against the allowance, tied to the grant. When it cannot be, no grant is issued and the decision is `BLOCK` (`should_execute` false, `execution_eligible` false, `execution_binding_digest` null) with one of these reason codes after the evaluation's own: `ALLOWANCE_EXHAUSTED` (what remains, the limit less what unclaimed grants reserve, what open or unknown claims hold, and what committed claims consumed within the window, is less than the amount), `ALLOWANCE_UNDEFINED` (the organisation defines no allowance under that key) or `ALLOWANCE_AMOUNT_INVALID` (the parameters give no positive whole amount). It is a refusal and not an ESCALATE because no approval creates capacity: only raising the allowance's own limit does (a top-up, which needs `policy:write` or `org:*` and so never an agent's key), after which the action may be proposed again. The dossier keeps the evaluation as it was decided; the refusal is the authority's, at the moment of the grant. The claim converts the reservation in the transaction that consumes the grant, and finalization settles it: COMMITTED keeps it, FAILED releases it, INDETERMINATE holds it until reconciliation, and a grant never claimed releases it when it expires. An allowance is a counter and never a concurrency scope: grants drawing on one allowance are claimed and carried out concurrently while it has room.
Read and verify an execution grant without consuming it GET
The handler of the deprecated alias `POST /v1/execution/verify-token`. A GET carries no body, so the execution token is presented in the `x-decionis-execution-token` header (required; never logged) and the intent hash, optionally, as the `intent_hash` query parameter. Verifies the token's Ed25519 signature, issuer and lifetime; that the caller is authorised for the organisation the grant names (the caller's own organisation for an organisation API key); that the token's `jti` is `{grantId}` (otherwise 409 `GRANT_ID_MISMATCH`); and, when `intent_hash` is supplied, that the grant is bound to it (otherwise 409 `GRANT_BINDING_MISMATCH`). The intent hash commits to the tenant, actor, action and downstream target the alias checks field by field. Reading a grant never authorizes execution: only a claim does.

