How to Add Human-in-the-Loop Approval to Any AI Agent (With Durable Execution)
Human-in-the-loop (HITL) for an AI agent means the agent pauses before a risky action — a refund, an account change, a PR merge, an outbound email — and waits for a person to approve or reject it. Done properly, that pause is durable: it's checkpointed to a database, not held in memory, so it survives a process crash, a deploy, or a server restart and resumes from the exact step. Most "agent boilerplates" skip the durable part, and that's exactly where they fall over in production.
Why in-memory approval flows are a trap
The naive HITL implementation keeps the paused run in memory: the agent hits a risky tool call, sets a flag, and waits for an API call to approve it. This demos beautifully. Then reality happens — your serverless function times out, you ship a deploy, the container gets recycled, the process OOMs. The in-memory state is gone. The customer who was mid-refund is now in an undefined state: did it happen? Will it happen twice? Nobody knows, and that's the worst possible answer when money is involved.
What durable execution actually requires
Durable human-in-the-loop needs three things, and all three are non-negotiable:
- Checkpointing. When the agent reaches a risky step, its full state — the message history, the pending tool call, the memory references — is serialized to durable storage (Postgres), not a variable.
- An approval queue. The pending request lives in a table with a status (pending / approved / rejected) and a reference back to the checkpoint. A human resolves it whenever — seconds or hours later, from any device.
- Exact-step resume. On approval, the run rehydrates from the checkpoint and continues from precisely where it stopped. No replaying prior steps (which would double-charge), no lost context.
How the pause/resume looks in practice
Here's the flow, using a support agent asked for a refund:
▸ ticket #4472 "refund my last order"
⏸ refund_request() HIGH-RISK — checkpoint written, run paused
… (human reviews in the approval queue)
✗ process killed # deploy, crash, timeout — doesn't matter
$ restart
✓ paused run recovered from checkpoint
✔ approved → resume at exact step → refund issued → ticket closedThe test that proves it's real: kill the process while the run is paused, bring it back, approve — and it completes correctly, exactly once. If your HITL can't pass that test, it isn't production-grade; it's a demo with a confirmation dialog.
How it differs across SDKs
| SDK | Durable HITL approach |
|---|---|
| LangGraph | Native checkpointer + interrupt — the cheapest, cleanest path |
| Vanilla loop | You own the loop, so serialize the run context to Postgres and rehydrate on resume |
| CrewAI | Wrap the task lifecycle; checkpoint between tasks (coarser granularity — be honest about it) |
Where this fits
Durable human-in-the-loop is one of the six pieces of the production layer every agent needs, and it's the hardest to get right — which is why it's the moment we lead with in the agentFast demo. agentFast ships durable HITL across LangGraph, CrewAI, and Vanilla out of the box, so "survives a crash mid-transaction and resumes" is a checkbox you tick, not a month you spend.
agentFast is the production layer — memory, 50 tools, observability, evals, guardrails, human-in-the-loop — for LangGraph, CrewAI, the Claude & OpenAI Agent SDKs & Vanilla. Own it for life.
Get agentFast — from $89