Execution
Human-in-the-Loop
Pause agent execution for human approval with crash-safe state and webhook callbacks
Pause an agent mid-execution, send a review to a human, and resume automatically when they respond.
Some decisions should not be fully automated. app.pause() blocks the agent until a human approves, rejects, or requests changes -- with crash-safe state persisted in PostgreSQL. The control plane resumes execution automatically when the webhook callback arrives.
@app.reasoner()
async def content_pipeline(brief: str) -> dict:
# Step 1: AI generates a draft
draft = await app.ai(system="You are a senior copywriter.", user=brief)
# Pause #1 — human reviews the draft (state is crash-safe in PostgreSQL)
review = await app.pause(
approval_request_id="draft-review",
approval_request_url="https://cms.example.com/review/draft",
expires_in_hours=24,
)
if review.decision == "rejected":
return {"status": "rejected", "feedback": review.feedback}
# Step 2: AI refines using human feedback
final = await app.ai(
system="Revise based on editorial feedback.",
user=f"Draft: {draft}\nFeedback: {review.feedback}",
)
# Pause #2 — human approves final version before publish
sign_off = await app.pause(
approval_request_id="final-approval",
approval_request_url="https://cms.example.com/review/final",
expires_in_hours=4, # auto-reject if no response
)
if sign_off.decision == "approved":
await app.call("publisher.publish", content=final)
return {"status": "published"}
return {"status": sign_off.decision, "feedback": sign_off.feedback}
# If the process crashes between pauses, it resumes exactly where it left offWhat just happened
- The agent ran normally until it reached a human gate
- Execution state was persisted before waiting, so restarts do not lose progress
- The human response became structured workflow input, not an external side channel
- The control plane resumed the same execution instead of starting a new one
Example paused/resumed lifecycle:
{ "execution_id": "exec_a1b2c3", "status": "waiting", "approval_request_id": "draft-review" }
{ "execution_id": "exec_a1b2c3", "status": "running", "decision": "approved" }
{ "execution_id": "exec_a1b2c3", "status": "succeeded", "result": { "status": "published" } }