require_approval, Checkfu parks the Run and creates an ActionApproval. Your application notices the wait, shows an authorized reviewer the exact frozen action, and answers it.
Connected ToolInvocations and Custom tools share the human-decision resource but not the same surrounding authorization or execution path.
The examples use the TypeScript SDK:
Detect the wait
Drive a live review queue from the Session event stream. A direct
session.status_waiting says the Session is parked; run.requires_action says why.custom_tool wait needs an application result but no human decision. A Question grants no authority and is answered with user.question_answer.Read the frozen decision
Fetch
GET /v1/action-approvals/{id} or use the overlapping authority carried by action_approval.pending.Render the context by kind:- A connected ToolInvocation can name the acting Principal, Agent version, Session, Run, Connection revision, ToolSource and Tool versions, schema hash, and outbound destination. It follows PermissionAssignment → ActionPolicy → ActionApproval.
- A Custom-tool ActionApproval names the admitted Agent, logical Tool,
tool_use_id, Session, Run, and optional surface coordinate. It has no Connection, ToolSource, provider request, PermissionAssignment in the Tool invocation path, or child-thread coordinate incontext_summary.
arguments_hash, not argument values. For a Custom tool, read the exact arguments from the preceding agent.tool_use event and prove that the displayed payload matches:Respond once
For a primary-thread ActionApproval, send the current Two reviewers racing on one item are serialized by
version as expected_version. Retain one idempotency key for this logical response and reuse it only for an exact retry.expected_version; the loser re-reads and shows the durable outcome. A stale response returns 409 validation.conflict. Re-read the approval instead of silently approving its new version or changed arguments. Responder authorization is checked separately and can return 403 policy.denied.A ToolInvocation ActionApproval is the CapabilityGateway branch. Its CapabilityGateway approve response carries one deterministic, single-use proof token. Managed-tool, Custom-tool, browser, harness-tool, and Automation-step approvals carry no provider-effect proof, and every denial carries none. Idempotent replay does not extend expiry or make a consumed proof usable again.Continue the correct path
Branch on the approval context.Connected ToolInvocation through platform MCP: Resolving ActionApproval records the decision; it does not dispatch the operation or automatically resume or create a Run. Retry the identical original
tools/call, preserving its original ToolInvocationId and the returned requestState where applicable. Checkfu rechecks current Connection, grant, policy, approval, and other required authority before execution. A completed identical retry is refused as already_executed rather than dispatched again. A denial is not permission to execute elsewhere.Connected ToolInvocation parked in a native Session: On a route that supports native approval waits, Checkfu can wake the original parked call after recording the decision. It rechecks the original caller, acting Principal, Connection, Tool, policy and retained Skill inputs. The resumed call still needs fresh execution authorization before using its single-use approval proof. The original account, arguments, frozen context and expiry remain bound. Do not create a replacement call to work around a refusal. A missing, expired or erased original call prevents the wake; a recorded continuation does not prove provider completion or result delivery. Do not assume restart or duplicate recovery is supported on the selected route.Custom tool: PermissionAssignment is not part of the Tool-call path. After approval, wait for run.action_authorized, correlate it to the preceding agent.tool_use, execute the handler in your application, and post the exact result. ActionApproval itself does not execute the handler or start a new Run.Custom-tool approval continuation and proof custody
Whenapproval.context_summary.kind === "custom_tool", the preceding agent.tool_use is the frozen customer-handled call. After approval, run.action_authorized records the exact authorization. The same compute-closed Run stays parked until the application executes the handler and posts user.custom_tool_result; approval itself does not execute the Tool or start another Run.
Accepting the matching user.custom_tool_result continues the original waiting
Run under current authority with its retained context pins. It does not create
a new Run or resolve newer latest context. The result records the handler’s
output for the original tool_use_id; retry with the same result payload and
idempotency key to recover that same accepted event. Follow the original Run’s
recorded outcome to determine when its work settles. Input acceptance, an Agent
message, or session.status_idle with stop_reason: "requires_action" does not
establish completion. That idle event is an intermediate wait. A provider-effect
receipt remains separate from result acceptance and Run settlement.
GET /action-approvals/{id} never reveals a CapabilityGateway proof. Preserve a CapabilityGateway approve response only long enough to present its proof to the immediate trusted effect; do not log it, put it in Session content, reuse it after expiry, or treat it as authority for another action. This proof rule applies only to the ToolInvocation branch; the Custom-tool result carries causal attribution, not a bearer credential.
Tool confirmations
For CMA-shaped clients, a Tool wait can instead be answered through the Session’s event endpoint, not the ActionApproval resource response route. Echo the exacttool_use_id from agent.tool_use.
result: "deny" and an optional nonempty denyMessage to refuse it. Checkfu still applies the frozen approval and responder-authority checks. An allowed Custom-tool wait then requires the matching user.custom_tool_result with the same Tool-use ID.
Authenticated server context supplies durable actor attribution for this event too; the client does not supply authored_by.
Submit a Custom-tool result
Use the SDK interaction helper so the event contains only the currently permitted Tool result, request correlation, and optional scope. Authenticated server context supplies durable actor attribution; client event bytes do not supplyauthored_by.
Make the application effect safe
toolUseId is replay-stable for the exact recorded call, but a local receipt written after a remote provider call is not atomic with that provider. A crash after provider acceptance and before the local write can repeat the effect.
Use one of these patterns:
- pass a provider-supported idempotency key derived from
toolUseId; - durably reserve an effect intent before provider contact and reconcile its state; or
- use an outbox or effect ledger with pre-effect intent and post-effect settlement.
Reconciliation cases
Use
GET /v1/action-approvals for queue backfill and periodic reconciliation. Use Session events as the primary live signal so multiple review surfaces converge on the same durable decision.