Skip to content

Approvals

Read this page after Policies and Policy Sets. An Approval pauses a sensitive authority request until an eligible human decides it. Policy data maps scopes to risk tiers and identifies whether an operator, the application’s Federated user, or either may decide.

Approval is an optional security primitive. A zone that declares no approval_tiers data never creates a hold, and nothing in the platform requires one. One name carries through every surface: the SDKs call the decision’s identifier approvalId and the wire calls it approval_id under /approvals paths; only the audit stream keeps the historical step_up_ event-type prefix and challenge_id metadata key - see the wire-name mapping.

sequenceDiagram
  participant App as App or agent
  participant STS as STS
  participant Policy as Active policy set
  participant Approver as Console, Admin API, or Federated user

  App->>STS: Exchange for gated scope
  STS->>Policy: Evaluate request
  Policy-->>STS: allow, gated by matched approval tiers
  STS-->>App: interaction_required with approval_id + binding
  App->>STS: GET /approvals/{id} (long-poll)
  Approver->>STS: approve or reject the hold
  STS-->>App: state: approved
  App->>STS: Retry exchange with approval_id
  STS-->>App: Mandate (approval consumed)
PlaneApproverSurface
OperatorA control-plane admin holding an approve-capable token.Console Approvals page or POST /v1/zones/{zone}/approvals/{id}/approve / /reject.
Federated userThe requesting application’s own Federated user.POST /approvals/{id}/decision on the STS, requiring a user-type session mandate and the hold’s binding.

The tier’s approver declaration picks the plane: operator, subject (the Federated user plane’s wire value), or any. On the operator plane, approval authority is a distinct admin capability - a write token cannot decide a hold. An any hold admits either plane, so operators can always decide it.

An Approval reserved for the Federated user requires the Application to federate that user through a registered Federated user issuer. Without that federation, the Approval can only expire. This is a decision mechanism, not per-Subject Resource authorization: the original Application, policy, scopes, and Delegation still bound the resulting Mandate.

ComponentResponsibility
Policy dataDeclares risk tiers per scope and approval_tiers gates; the platform fixes no tier taxonomy.
STSCreates the hold, serves its state to long-polling agents, records decisions, verifies the binding, and consumes the approval at mint.
Console or Admin APILists, inspects, and decides operator-plane holds.
ApplicationRelays Federated user holds to its own user and posts the decision with that user’s session mandate.
SDK or OAuth clientSurfaces interaction_required and waits on the hold (waitForApproval); caracal run parks and retries automatically.
StateMeaning
pendingThe hold is live and awaiting a decision.
approvedAn approver granted the hold; the next matching exchange mints.
rejectedAn approver refused the hold; terminal.
expiredThe approval window closed without a decision, or an approval lapsed before consumption.
consumedThe approval released its one mandate; terminal.

An Approval releases at most one Mandate. Its binding covers the Application, Authority record, Session, Delegation, Resource, scopes, and active policy version. A policy rollout or execution-context change therefore requires a fresh decision. Because the Authority record is minted per client-credentials exchange, a requester that restarts before its hold is decided returns with a new Authority record: persisting the approval id lets it observe the final state through waitForApproval, but consumption stays bound to the run that asked, so a restarted requester raises a fresh hold rather than consuming one approved for the prior process. Consumed and rejected are terminal.

The tier’s privacy declaration controls what the decision record retains of a Federated user approver: identified stores the identity verbatim, pseudonymous a stable zone-scoped pseudonym, and anonymous a redaction marker. The approver’s Authority record ID is always kept as the forensic and revocation anchor. Operator-plane decisions always record the deciding admin identity. Caracal stores authorization facts, never business context.

  • Gate high-risk scopes, not everything: approval latency is a person, so reserve it for authority worth a pause.
  • Keep the decision outside policy; policy declares that a decision is needed, never performs it.
  • Use subject tiers when the risk belongs to the application’s Federated user and the application federates its users through a registered Federated user issuer; use operator tiers when the risk belongs to the zone.
  • Give automation credentials write without approve, so no pipeline can silently settle a hold.
  • Cross-check the binding: the agent prints it beside the approval id, and the web console shows it on the hold.

Read Session Delegation to understand how approved authority can be narrowed for another Session.