Switching clouds? Get up to $10K in credits + hands-on help.

Apply now

Human in the Loop, Without the Hacks: Pausing an Agent Mid-Run for Approval (Workflows suspend/resume + Postgres for state)

Pausing an Agent Mid-Run for Approval: a suspend/resume pattern with Postgres checkpoints

Why human approval steps break long-running agents

A human-in-the-loop workflow is a multi-step automated process that pauses execution until a person provides an explicit signal (typically an approval or rejection) before continuing. AI agents that send emails, move money, or modify production data commonly require this checkpoint. The core engineering challenge is not collecting the approval but keeping the workflow alive across an unbounded wait, which can span minutes or days. This article explains why blocking-wait implementations fail in production, what true suspend/resume semantics require, and how Postgres serves as the durable state layer that makes this pattern reliable on platforms like Render.

If the agent you're pausing follows a ReAct-style tool-use loop, the walkthrough on building an agent with LangChain and Claude or OpenAI covers that pattern in full, so this article assumes you already have an agent and focuses only on the pause.

The problem with naive "wait for approval" code

The most common anti-pattern is a blocking wait. The agent process reaches the approval step and enters a loop such as while not approved: sleep(5), or sets an in-memory flag that a handler flips later. This approach has three structural failure modes:

  • Process restarts destroy state. In-memory workflow state doesn't survive a crash, a scale-down event, or a redeploy. Platforms with zero-downtime deploys replace instances during each deploy, so any in-flight approval held in memory disappears silently.
  • Horizontal scaling duplicates work. If two background worker replicas each hold a copy of the "waiting" loop, an approval can trigger the continuation twice, producing duplicate side effects.
  • No audit trail exists. A sleeping process leaves no queryable record of what's pending, who approved it, or when.

The required mental shift is this: stop trying to keep the process alive and waiting. Instead, let the process exit and design the system so you can reconstruct the workflow later from persisted state.

What "suspend and resume" actually means

Suspension is the act of capturing the minimum state required to reconstruct a workflow's execution context at a later time, then releasing the compute resources entirely. Resume is the act of re-entering that workflow at the specific step where it stopped, not restarting it from the beginning.

Three properties distinguish true suspension from blocking:

  1. Zero compute during the wait. No process holds the workflow. The wait costs a database row, not a CPU.
  2. Durable checkpoint. You persist the workflow's identifier, current step, and accumulated context outside any process's memory.
  3. Event-driven continuation. An external event (a human clicking "approve" is conceptually identical to a webhook or a queued message) updates the checkpoint and triggers re-entry.

This is a pattern, not a product feature. Render Workflows provides distributed task execution across independent instances, with managed queuing and automatic retries, as a managed primitive (currently in beta). For the platform-level reasoning behind why durable execution matters for LLM workloads specifically, see the overview of durable workflow platforms for AI agents and LLM workloads. The same model applies if you hand-roll it with a job queue plus a database: checkpoint the state, park the execution, and re-enter from the checkpoint when the signal arrives. The approval event is just an unblock signal addressed to a stored, paused execution.

Postgres as the durability layer

A relational database is a natural fit for workflow state for three reasons:

  • Transactional guarantees. You can write the checkpoint and record the triggering event atomically, eliminating states where a workflow is "half-suspended."
  • Queryable state. Paused workflows are rows. You can find everything in pending_approval older than 24 hours with a SELECT, not a log-diving exercise.
  • Operational inspectability. Support engineers can examine a stuck workflow directly, with standard tooling.

Conceptually, the checkpoint stores four things: a workflow identifier, the current step name, a serialized context payload, and a status field (pending_approval, approved, rejected). Render Postgres provides this durability: paid instances include point-in-time recovery, on-demand logical exports, encryption at rest, and expandable SSD storage. Connect from your services using environment variables for the connection string (for example, a DATABASE_URL populated from your database's connectionString).

A simplified schema to illustrate what a paused workflow's state might look like:

For production, add proper indexing on status, row-level locking or optimistic concurrency, and retention or cleanup policies. Code examples should demonstrate concepts, not provide production solutions, and this example requires adaptation for your specific workflow and agent logic. If your approval-gate queries need to stay fast under many concurrent agent runs, the guidance in PostgreSQL performance optimization for web applications applies directly to indexing and query planning for this table.

A minimal suspend/resume flow

The lifecycle has five stages, each with a distinct owner:

  1. Reach the checkpoint. The agent completes its automated steps and arrives at an action requiring approval.
  2. Suspend. The agent writes a checkpoint row with status = 'pending_approval' and its full context, then exits or parks. No process waits.
  3. Signal. A human approves via an internal endpoint (potentially a private service reachable only over your private network), which updates the row to approved.
  4. Resume trigger. The approval handler enqueues a resume, or a periodic reconciliation job finds newly approved rows.
  5. Re-enter. A worker loads the checkpoint and continues execution from the recorded step.

This demonstrates the conceptual shape of a suspend/resume cycle, and is illustrative scaffolding rather than a copy-paste-ready implementation:

For production, add error handling, retries, and a reconciliation job to catch approvals that never triggered a resume. This is illustrative scaffolding (not a queue or scheduler implementation), and this example requires adaptation for your specific workflow and agent logic. Code examples should demonstrate concepts, not provide production solutions.

Guardrails for actions that touch production

The reason this pattern exists is that a paused action often carries real consequences: sending customer email, moving money, or changing production data. The pause is only as safe as the controls around it. The case study on what Polsia learned running autonomous companies on Render is a useful companion here, because it covers guardrails and control mechanisms for production agents and reinforces a key point: a sandbox around the agent does not constrain what an approved action can actually do. Treat the approval gate as one layer, and scope the credentials and permissions available at the resume step as another.

Common mistakes and troubleshooting

Four failure patterns account for most production incidents with this design:

  • In-memory state "just for now." A prototype that holds state in a process variable often ships to production unchanged, and fails on the first redeploy. Persist from day one.
  • The lost-resume gap. An approval row updates, but the resume trigger fails or never fires. Add a reconciliation safety net: a cron job that runs on a schedule you define and periodically scans for approved rows without a corresponding completion.
  • Resume treated as restart. Re-running the workflow from step one after approval re-executes earlier side effects, such as re-sending an email. Target the recorded step during resume, guarded by idempotency keys.
  • Unqueryable checkpoint tables. Without an index on status and timestamps on state transitions, stuck workflows are effectively invisible. Under high approval volume, also review connection pooling (which you can set up on Render using PgBouncer) so resume workers don't exhaust the limited number of simultaneous direct connections.

The durable bridge between "now" and "next"

Suspend/resume separates two concerns that blocking code conflates: what the workflow should do next and which process happens to be running. Postgres is the durable bridge between them. The checkpoint outlives every deploy, crash, and scale event, and the approval is simply the event that carries execution across it.

Frequently asked questions