Introduction

Confirm is the approval layer for AI agents. Your code calls one API before an agent does anything sensitive. The agent pauses, a human decides, and a signed webhook resumes the run. Every decision lands in an immutable audit log.

Why this exists

Agents are good enough to act and not good enough to act unsupervised. The failure modes are concrete: a support agent refunds $10,000 instead of $10, a sales agent invents a fact in a cold email, an ops agent drops a table that billing still reads. Teams either ship agents with no brakes, or bolt approvals onto Slack threads that satisfy no auditor.

Confirm makes the brakes infrastructure: one API to pause, one review page for the human, one webhook to resume, one log for compliance.

The request lifecycle

  1. Create. Your code calls POST /v1/requests with a summary, the exact action payload, and who decides. That can be a single email or an approver group like "group:finance" for escalation.
  2. Notify. Every approver gets their own single-purpose magic link by email. No account, no app. Optionally, a Slack channel notification announces the request to the team.
  3. Decide. The review page shows the payload plus the context you passed: reasoning (why the agent wants this) and recentActions (what it did first), so the decision takes seconds. The human approves, rejects, or edits the payload before approving. Identity, timestamp, IP, and note are recorded.
  4. Resume. We fire an HMAC-signed webhook carrying the verdict, the effectivePayload (human edits win), and any agentState you parked with the request, so a fresh worker can rehydrate and continue even hours later.
  5. Expire. If nobody answers within the TTL, the request becomes EXPIRED and a webhook tells your system. Silence never becomes consent.

How you integrate

Three ways in, same approval underneath. Pick by stack.

PathBest forLooks like
TypeScript SDK
@confirm/sdk
Node/TS agents. The recommended path.guard() gates your whole tool layer with policies (fail-closed), or withApproval() wraps one function.
MCP server
confirm-mcp
MCP agents: Claude Desktop, Cursor.Adds a request_approval tool with one config block, no code.
Raw HTTPAny language, any framework.One POST with a bearer key. Resume on the webhook, or poll.

What ships in the box

CapabilityWhat it does
Escalation routingnotify: "group:finance" fans out to a named approver group. First decision wins, and the audit names exactly who decided.
Approver contextreasoning and recentActions render the agent's story above the payload on the review page.
Payload editingApprovers fix the JSON before approving. Your code executes effectivePayload, never the first draft.
State parkingagentState stores up to 256KB of serialized context and returns it in the webhook for rehydration.
Fail-safe expiryWorkspace default or per-request TTL. Unanswered requests expire and notify your agent.
Signed webhooksHMAC-SHA256 over a timestamped payload, retried, with delivery receipts.
TeamsInvite collaborators with roles. Approvers never need accounts.
Audit logAppend-only record of every request, decision, and delivery attempt.

Request statuses

StatusMeaning
PENDINGWaiting on a human. The agent should stay paused.
APPROVEDA human said yes. Execute effectivePayload.
REJECTEDA human said no. Do not execute; the rejection note explains why.
EXPIREDThe TTL elapsed with no decision. Treat it as a rejection.

PENDING is the only non-terminal state. Terminal states never change: a request can't be re-approved, un-rejected, or resurrected after expiry.

The quickstart takes you from zero to a resolved approval in about five minutes with the SDK. Building in a framework? Jump to the integration guides.