How to Add Human-in-the-Loop Approval to Any AI Agent (With Durable Execution)

·10 min read

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:

  1. 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.
  2. 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.
  3. 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 closed

The 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

SDKDurable HITL approach
LangGraphNative checkpointer + interrupt — the cheapest, cleanest path
Vanilla loopYou own the loop, so serialize the run context to Postgres and rehydrate on resume
CrewAIWrap 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.

Stop rebuilding the plumbing.

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