Skip to content
AgentField

Multi-step human approval that survives restarts

Pause execution mid-run for a human decision. State persists in PostgreSQL, so a crashed server resumes exactly where it left off.

Block an agent for a human decision — for hours, days, or across a server restart. The control plane writes the execution state to PostgreSQL before pausing, then a webhook callback resumes the same execution after the human responds.

from agentfield import Agent

app = Agent(node_id="content-pipeline", version="1.0.0")

@app.reasoner()
async def publish_with_review(brief: str) -> dict:
    # Step 1: AI drafts
    draft = await app.ai(system="You are a senior copywriter.", user=brief)

    # Pause #1 — editor reviews the draft (state is now in Postgres)
    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 != "approved":
        return {"status": review.decision, "feedback": review.feedback}

    # Step 2: AI revises using the editor's feedback
    final = await app.ai(
        system="Revise based on editorial feedback.",
        user=f"Draft: {draft}\nFeedback: {review.feedback}",
    )

    # Pause #2 — publisher signs off before going live
    sign_off = await app.pause(
        approval_request_id="final-approval",
        approval_request_url="https://cms.example.com/review/final",
        expires_in_hours=4,
    )
    if sign_off.decision == "approved":
        await app.call("publisher.publish", content=final)
    return {"status": sign_off.decision}

app.run()

If the agent process restarts mid-pause, reconnect to the pending approval:

result = await app.wait_for_resume(
    approval_request_id="draft-review",
    timeout=24 * 3600,
)

What this gives you

  • Multiple human gates inside a single execution.
  • Crash-safe: the execution state lives in PostgreSQL, not in agent memory.
  • Resolutions arrive via HMAC-signed webhook — your reviewer UI just POSTs the decision.

Next