Skip to main content
When governance requires review, the Run parks instead of failing. Your product has to notice, show someone the decision, and answer. This guide implements that loop.

The loop

CHECKFU_API_KEY and CHECKFU_WORKSPACE_ID come from Get access, and APPROVAL_ID is the approval_… the wait event carried. The TypeScript snippets below use the TypeScript SDK with this client:
1

Detect the wait

Watch the Session stream for run.requires_action. Key off the action payload, not the Session status. session.status_waiting tells you the Session is parked, but only the action tells you what it is parked on.
2

Show the reviewer what was frozen

action_approval.pending carries the frozen context, and GET /v1/action-approvals/{id} returns the full record including context_summary.Branch on that context before rendering it:
  • A ToolInvocation ActionApproval names the Connection, tool and source versions, outbound scheme, host, port, method and path, Session, Run, and acting Principal. It carries no query string, headers, body, or argument values; arguments appear only as arguments_hash.
  • A Custom-tool ActionApproval has context_summary.kind === "custom_tool". It names the admitted agent definition and version, nullable installation and surface, tool_use_id, and logical tool name. It has no Connection, ToolSource, or outbound request. Read the exact arguments from the preceding agent.tool_use event when building the review; execute them only after authority is granted.
Do not force both variants into one review-card shape. The ActionApprovals concept lists the complete frozen contract for each.
3

Respond

A CapabilityGateway approve response carries one deterministic, single-use proof token. A Custom-tool approval and every denial return no proof. Idempotency-Key is optional, but using a stable value preserves an encrypted copy of the exact HTTP response for retries.
GET /action-approvals/{id} never reveals a CapabilityGateway proof. Preserve a CapabilityGateway approve response. If delivery is lost, retry the exact same decision and reuse the Idempotency-Key when supplied. The keyed receipt can replay the captured response for up to 24 hours, but it does not extend proof expiry or make a consumed proof usable. This warning does not turn a proof-free Custom-tool response into a bearer credential.
4

Continue the correct branch

For a CapabilityGateway decision or a Custom-tool denial, action_approval.resolved lands in the log, followed by run.resumed; the normal continuation tells the harness whether the tool action was approved or refused. Do not treat a denial as a Session failure.An approved Custom-tool action is different. Wait for run.action_authorized, correlate its tool_use_id to the exact preceding agent.tool_use, execute that recorded tool action in your application, and post its result:
ActionApproval alone does not start a Run, dispatch, lease, or Sandbox for this branch. The same compute-closed Run stays parked after run.action_authorized; the matching user.custom_tool_result is what closes that wait and admits the ordinary continuation Run.

Handling the awkward cases

The last two are the freeze doing its job. An approval is a decision about one exact tool action, and any change to that tool action invalidates it rather than approximating consent.

Building the queue

For a live queue, drive off the event log rather than polling GET /v1/action-approvals. action_approval.pending tells you an item appeared; action_approval.resolved tells you it was answered, including when someone else answered it first. That is what keeps two open review tabs from fighting. Use the list endpoint for backfill and reconciliation, not as the primary signal.

Next steps

ActionApprovals

The freeze, the proof token, and the governance checks that produce a review.

Resume a stream

Make sure you never miss the wait event in the first place.