Execution grants

Evaluate and bind one exact agent action to a single-use execution grant

Deprecated

POST
/v1/authority/enforce-and-bind

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.

Authorization

ApiKeyAuth
AuthorizationBearer <token>

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

Idempotency-Key?string

Request Body

application/json

protocol_version*string
tenant_id*string
Formatuuid
intent_id*string
Formatuuid
captured_at*string
Formatdate-time
expires_at*string

Must be later than captured_at and no more than five minutes after it.

Formatdate-time
actor*
action*
context*

The 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.

downstream_target*
policy_projection?

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.

expected_effect_digest?string

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.

Match^sha256:[0-9a-f]{64}$
intent_hash*string

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.

Match^sha256:[0-9a-f]{64}$
mode*string

Value in

  • "SHADOW"
  • "PARALLEL"
  • "ENFORCEMENT"
escalation?

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.

executor?

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.

evidence?

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/authority/enforce-and-bind" \  -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"}

Summarize shadow-mode decision evaluation reports GET

Returns aggregate protocol-owned shadow report metrics for saved evaluate-decision records.

Evaluate one exact agent action and bind it to a single-use execution grant POST

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.