Skip to main content
An ActionApproval is a durable human decision about one exact frozen action. Checkfu creates one when an ActionPolicy returns require_approval; clients do not create approvals directly. The surrounding authorization path depends on the action being reviewed.
The Checkfu CLI and TypeScript SDK are private-alpha artifacts with no self-service public installation channel. Part of this flow is behind a rollout fence; a deployment that has not enabled it refuses the request rather than running it. Confirm capability status before making availability part of your application’s contract.

Two different governance paths

Connected and platform ToolInvocations

A governed Connection or platform ToolInvocation keeps the complete ladder:
PermissionAssignment answers whether the acting subject may use the governed resource. ActionPolicy then returns allow, require_approval, or deny. An ActionApproval records the human decision only for the exact action that reached the approval branch.
Approval is not permission. It cannot fill in a missing PermissionAssignment, override a denial, restore a revoked Connection, or authorize a different ToolInvocation from the one reviewed.

Custom tools declared by the admitted Agent

A Custom-tool call has no Connection, ToolSource, provider credential, or PermissionAssignment in its invocation path. The admitted Agent already declares the Tool. Checkfu evaluates ActionPolicy directly:
With no restricting ActionPolicy, the Custom-tool call proceeds to its application-result wait. When approval is required, run.action_authorized is the durable fact that the exact call may be executed. An ActionApproval does not execute customer code, mint a provider-effect proof, or post the result. Reviewer authorization is separate from the Custom-tool invocation path. A trusted backend that asserts an end-user reviewer with acted_as must prove that the Principal holds an active approve PermissionAssignment on the resource mapped from the approval subject and survives the current ActionPolicy ladder. That does not add PermissionAssignment to the Tool call itself.

What is frozen

The ActionApproval binds the decision to the exact action that requested it. A ToolInvocation review can name the Session, Run, acting Principal, Agent version, Connection and Connection revision, ToolSource and Tool versions, schema hash, and the permitted outbound destination. A Custom-tool review instead names the admitted Agent, logical Tool name, tool_use_id, Session, Run, and optional surface coordinate. It does not invent a Connection, ToolSource, outbound request, or thread coordinate. The public ActionApproval resource exposes argument identity in its top-level arguments_hash, not argument values. Its context_summary carries the kind-specific frozen coordinates. Query strings, headers, request bodies, and custodied Credential values are not returned in the review resource. For a Custom tool, the exact arguments already appear in the preceding agent.tool_use event. Prove that the payload shown to a reviewer is the frozen payload before presenting it as such:
If the hash does not match, do not execute or present that payload as the approved action.
The resource and event use different field names in the current wire contract: the ActionApproval resource carries top-level arguments_hash, while action_approval.pending carries overlapping authority under frozen_context with arg_hash. Match the surface you are reading.

Lifecycle

A connected ToolInvocation approval is time-bounded and single-use. The approve response can carry a deterministic proof token; redemption moves the ActionApproval to consumed. GET /v1/action-approvals/{id} never reveals that proof. A Managed-tool purchase approval carries no redeemable provider-effect proof or provider credential. The decision stays bound to its original invocation, selection, quoted terms and expiry; execution and result release still require their separate current authority. A Custom-tool approval carries no provider-effect proof. Its approved decision advances the exact wait and produces run.action_authorized; the application then supplies user.custom_tool_result.

Responding

The public resource exposes:
  • GET /v1/action-approvals for backfill and reconciliation;
  • GET /v1/action-approvals/{id} for one frozen decision; and
  • POST /v1/action-approvals/{id}/responses for approve or deny with optimistic concurrency.
Send the current version as expected_version. Optional instructions are recorded with the resolution. Use one stable Idempotency-Key for the logical response and reuse it only for an exact retry. A keyed retry can replay the captured HTTP response. It does not extend approval expiry, make a consumed proof reusable, or turn a proof-free Custom-tool response into a bearer credential. For CMA-shaped clients, the reviewer may instead send user.tool_confirmation to the ordinary Session event endpoint with the same tool_use_id. allow moves the same wait into its Custom-result phase; it does not authorize the platform to execute customer code or replace the later user.custom_tool_result. Authenticated server context supplies event attribution; clients do not send authored_by.

Handle an approval

Build the event-driven queue, distinguish the two action paths, and continue safely.

Who may respond

A root or admin API key can respond under the role path. The audit record names the authenticated key. A trusted control-plane backend can instead include acted_as to assert an end-user Principal. The current responder contract then verifies that the backend may assert people and that the Principal has live approval authority on the resource mapped from the approval subject. A matching ActionPolicy denial still wins. Runner and model credentials cannot approve their own work. This responder check answers who may make the human decision. It does not change whether the underlying Custom-tool invocation uses PermissionAssignment; it does not.

Standing approvals

An eligible connected ToolInvocation approval can mint a bounded StandingApproval for future matching calls. It is a separate, listable, revocable resource. A StandingApproval can only downgrade require_approval to allow for its exact subject, Connection, Tool, scope, revision, and expiry. It never:
  • overrides deny;
  • fills in a missing PermissionAssignment;
  • applies to a Custom-tool call;
  • survives Connection rotation; or
  • widens beyond its recorded argument scope.
Revoking it affects the next matching ToolInvocation immediately. Prior decisions and audit records remain intact.

Revocation after approval

Connected ToolInvocations recheck live authority immediately before provider I/O. Any of these changes can refuse an already-approved action: Approval records what a person decided. It does not preserve stale authority. After a connected ToolInvocation completes, the gateway checks current Run authority before returning its result or offering a spill, and again after a spill wait. When the Run is canceled, expired, or its authority service is unavailable, the gateway withholds its response and spill sidecar. The Run-authority error explains that the provider call completed; retries of that ToolInvocation cannot repeat the effect. A spill implementation owns authority enforcement during its own asynchronous write. This Run check does not establish permission to share the result with another audience. A Custom-tool approval has no provider-side recheck because Checkfu does not perform that effect. Your application must still enforce its own tenant, account, and row-level authorization immediately before executing the handler.

Audit and uncertain effects

The Session event log records when an ActionApproval becomes pending and when it resolves. Audit records the authenticated responder, optional asserted Principal, frozen coordinates, policy path, and trusted effect outcome where Checkfu owns the effect. Neither surface returns custodied Credential values. A ToolInvocation can settle as completed, denied, or indeterminate. indeterminate means Checkfu cannot prove whether the provider accepted the effect. Recovery must reconcile that uncertainty instead of treating it as a safe automatic retry. For a Custom tool, the customer application owns effect safety. Use provider-supported idempotency, a durable effect ledger, or explicit reconciliation before retrying an ambiguous remote result.

Questions are not approvals

A Question asks a person for ordinary input. It grants no authority, has no approval proof, and is answered with user.question_answer. Both Questions and ActionApprovals can park a Run, so branch on the action kind rather than treating every wait as permission to act.