Evaluate and bind one exact agent action to a single-use execution grant
Deprecated
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 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/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.

