API reference
The API is deliberately small: one endpoint to pause an agent, one to check the verdict. Base URL https://confirm.dev/api, JSON in and out.
Authentication
Pass your API key as a bearer token. Keys are created in Dashboard → API Keys and scoped to one workspace.
Authorization: Bearer cfm_live_...Errors share one envelope everywhere: { "error": { "code": "...", "message": "..." } }.
Create a request
/v1/requestsCreates a PENDING approval request, emails the approver a magic link, and fires request.created to your webhooks.
Body
| Field | Type | Description |
|---|---|---|
| summaryrequired | string | Human-readable description of the action, ≤500 chars. This is what the approver reads first — write it like a headline. |
| payload | json | The exact action the agent wants to take. Shown — and editable — in the review UI. |
| notifyrequired | string | The approver's email address, or "group:<key>" to escalate to an approver group (Pro plan). See Teams & approval groups. |
| agentName | string | Shown to the approver ("SupportBot requires approval"). ≤100 chars. |
| metadata | json | Opaque to us; echoed back in reads and webhooks. Use it for run IDs and trace correlation. |
| agentState | json (≤256KB) | Serialized agent context (conversation, plan, scratchpad). Stored with the request and returned in GET and webhooks so a stateless worker can rehydrate after a long approval wait. Never shown to the approver. Oversized payloads return 400 state_too_large. |
| reasoning | string (≤2000) | The agent's explanation of why it wants this action. Shown to the approver above the payload so they can decide fast. |
| recentActions | string[] (≤10 × 300) | What the agent did right before asking (steps, lookups, checks). Rendered as a timeline on the review page. |
| ttlMinutes | integer | Minutes until the request expires. 5–10080. Defaults to your workspace's configured expiry (Settings), initially 1440 (24h). Expiry is the auto-reject: the request becomes EXPIRED and your agent is notified. |
Example
curl -X POST https://confirm.dev/api/v1/requests \
-H "Authorization: Bearer $CONFIRM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"summary": "Send 50 outbound emails (batch q3-outbound)",
"payload": { "batchId": "q3-outbound", "count": 50 },
"notify": "sdr-lead@company.com",
"agentName": "OutreachBot",
"reasoning": "All 50 drafts passed brand checks; batch is ready to send.",
"recentActions": ["Drafted 50 emails from CRM data", "Ran brand-safety checks: 0 flags"],
"agentState": { "batchId": "q3-outbound", "drafts": "s3://bucket/drafts.json" },
"metadata": { "runId": "run_442" },
"ttlMinutes": 120
}'{
"id": "clx2h8…",
"object": "approval_request",
"status": "PENDING",
"summary": "Send 50 outbound emails (batch q3-outbound)",
"payload": { "batchId": "q3-outbound", "count": 50 },
"modifiedPayload": null,
"effectivePayload": { "batchId": "q3-outbound", "count": 50 },
"metadata": { "runId": "run_442" },
"agentState": { "batchId": "q3-outbound", "drafts": "s3://bucket/drafts.json" },
"reasoning": "All 50 drafts passed brand checks; batch is ready to send.",
"recentActions": ["Drafted 50 emails from CRM data", "Ran brand-safety checks: 0 flags"],
"agentName": "OutreachBot",
"approverEmail": "sdr-lead@company.com",
"approverGroup": null,
"resolvedAt": null,
"resolvedByEmail": null,
"resolutionNote": null,
"expiresAt": "2026-07-15T22:00:00.000Z",
"createdAt": "2026-07-15T20:00:00.000Z"
}Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_json / validation_error | Malformed body or failed field validation; the message names the field. |
| 401 | unauthorized | Missing, malformed, invalid, or revoked API key. |
| 402 | quota_exceeded | Included volume exhausted on Hobby, or the monthly safety cap reached on an overage plan. |
| 403 | plan_required | notify: "group:…" used on a plan without approver groups. |
| 404 | unknown_group | The group key doesn't exist in this workspace. |
| 400 | empty_group | The group exists but has no members to notify. |
Retrieve a request
/v1/requests/:idReturns the same shape as create, with current status. Expiry is applied lazily on read — you will never see a stale PENDING past its expiresAt. Requests belonging to other workspaces return 404.
curl https://confirm.dev/api/v1/requests/clx2h8… \
-H "Authorization: Bearer $CONFIRM_API_KEY"RULE
APPROVED, execute effectivePayload — it already resolves the human-edit-wins precedence for you. payload is the historical record of what the agent originally wanted.Webhook events
State changes push to your registered endpoints as signed POSTs — usually the better pattern than polling. Payload shape, headers, signature verification, and retry behavior are documented on the Webhooks page.