THE HUMAN-IN-THE-LOOP API FOR AI AGENTS
One API call pauses any agent before it refunds money, sends the email, or touches production. A human clicks approve, reject, or edit — and a signed webhook resumes the run.
free tier · 100 approvals/mo · no card required
REQUEST
Before the sensitive action, your code calls POST /v1/requests with a summary and the exact payload. The agent stops there.
DECIDE
The approver gets a magic link — no account, no app. They see the payload, then approve it, reject it, or edit it first. Every decision records who, when, and from where.
RESUME
We POST the verdict to your endpoint, HMAC-signed. Execute effectivePayload — the human-corrected version wins over the original.
The same four-line integration covers every team's nightmare scenario. Pick one:
BRAND SAFETY
A sales agent drafts 50 personalized emails from scraped data. One invents a funding round that never happened. The SDR edits the payload in the approval view — the corrected version is what your webhook receives.
INTEGRATION
No SDK required — it's plain HTTPS with a bearer key. Works from LangChain tools, Vercel AI SDK, CrewAI, cron jobs, or a bash script. Resume on the signed webhook, or poll if you'd rather keep it simple.
Full API reference →// 1. Pause the agent before the sensitive action
const res = await fetch("https://confirm.dev/api/v1/requests", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CONFIRM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
summary: "Refund $10,000 to customer #4821",
payload: { action: "refund", amount: 10000 },
notify: "finance@company.com",
agentName: "SupportBot",
}),
});
const { id, status } = await res.json(); // status: "PENDING"
// 2. The agent yields here. Resume on the webhook, or poll:
// GET /api/v1/requests/{id} until status !== "PENDING"SECURITY
HASHED CREDENTIALS
API keys and magic-link tokens are stored as SHA-256 hashes. A database leak can't mint requests or approve them.
SIGNED WEBHOOKS
Every delivery carries an HMAC-SHA256 signature over a timestamped payload — verifiable, replay-resistant, per-endpoint secrets.
256-BIT MAGIC LINKS
Approval tokens are 256 bits of entropy, single-purpose, and expire on your TTL. Possession is the credential; nothing to phish a password from.
APPEND-ONLY AUDIT
Requests, decisions, and every webhook delivery attempt are immutable rows. SOC2 evidence is a query, not an archaeology project.
IDENTITY ON EVERY DECISION
Approvals record the approver, timestamp, IP address, user agent, and their note — the full who / what / when / from where.
FAIL SAFE, NOT OPEN
Unanswered requests expire to a terminal EXPIRED state and notify your system. Silence never becomes consent.
The full model is documented, not marketed: read the security page.
Per approval, not per seat. Approvers are always free.
Exact limits per plan are in the docs.
Every request has a TTL (default 24 hours, up to 7 days). When it elapses, the request moves to a terminal EXPIRED state and a request.expired webhook fires — so your agent fails safe instead of hanging forever or acting anyway.
No. Approvers get a single-purpose magic link by email. The 256-bit token in the link is the credential — nothing to sign up for, install, or remember. Only developers who build with the API have accounts.
Yes — that's the point of Edit Payload. The approver fixes the JSON in the review screen and approves; the webhook's effectivePayload carries their version. The original stays stored alongside it for the audit trail.
Every delivery is signed: HMAC-SHA256 over timestamp.body with your endpoint's secret, sent in X-Confirm-Signature. Verification is ~6 lines of code, shown in the integration samples above and in the docs.
Yes — Confirm is framework-agnostic by design. Anywhere you can make an HTTPS request, you can gate an action: wrap it in a LangChain tool, a Vercel AI SDK tool call, a CrewAI task, or a plain function.
Every request (summary, original payload, metadata), every decision (approver identity, timestamp, IP, user agent, note, and any payload edits), and every webhook delivery attempt with its HTTP status. Rows are append-only.