ActionBoxBlog

BackendPlatformML engineerAutomation

Durable Execution Explained: Pause and Resume Stateful AI Agents

What durable execution means for stateful AI agents, how checkpoints survive restarts, and where human approval belongs before side effects.

Digital clock displayed on a laptop at a desk

An AI agent that pauses for a person has a state-management problem, not just a notification problem. The process can restart, the worker can move to another machine, and the reviewer may answer an hour later. Durable execution means the run can recover its checkpoint and continue from the same logical state instead of starting over or repeating a side effect.

Durable execution in one diagram

Architecture Flow
1 Save plan and tool arguments 2 Create approval Action 3 Notify with bounded context Worker can stop or restart safely 4 Approve or reject 5 Resume decision 6 Restore checkpoint 7 Execute once if approved Stateful agent Durable checkpoint Actionbox Reviewer Side effect API- Stateful agent Durable checkpoint Actionbox Reviewer Side effect API-

The checkpoint and the human decision are related but separate records. LangGraph, an agent runtime, or a workflow engine owns the paused run state. Actionbox owns the durable question, reviewer response, expiry, and audit history. Keeping those responsibilities explicit prevents a notification from being mistaken for a checkpoint.

What must survive a restart?

Persist the smallest state that lets the agent make the same safe decision after a worker disappears:

StateWhy it matters
Run or thread IDReconnects the resumed work to the original execution
Tool name and exact argumentsDefines the side effect the reviewer actually approved
Checkpoint versionPrevents a stale answer from resuming a newer run
Action ID and decisionLinks the human answer to the agent state
Idempotency keyMakes retries safe when the process crashes after creating an Action
Expiry policyDefines what happens when nobody answers

Do not persist raw credentials in the approval context. Redact secrets before sending a payload to a reviewer, and include a link to a protected system when the full evidence is too sensitive for the Action.

LangGraph checkpoint and resume

The following simplified graph shows the runtime boundary. The interrupt pauses the graph; the driver resumes the same thread_id after it obtains a decision. In production, replace the in-memory saver with a durable checkpointer.

python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph
from langgraph.types import Command, interrupt


def approve_side_effect(state: dict) -> dict:
    decision = interrupt({
        "title": state["proposal"]["title"],
        "tool": state["proposal"]["tool"],
        "arguments": state["proposal"]["arguments"],
    })
    return {"approved": decision["approved"] is True}


def execute(state: dict) -> dict:
    if not state["approved"]:
        return {"outcome": "skipped"}
    return {"outcome": run_tool(state["proposal"])}


builder = StateGraph(dict)
builder.add_node("approve", approve_side_effect)
builder.add_node("execute", execute)
builder.add_edge(START, "approve")
builder.add_edge("approve", "execute")
builder.add_edge("execute", END)
graph = builder.compile(checkpointer=InMemorySaver())

config = {"configurable": {"thread_id": "deploy-2.18.0"}}
result = graph.invoke({"proposal": proposal}, config=config)

if result.get("__interrupt__"):
    # The worker can ask Actionbox here, then resume this same thread.
    result = graph.invoke(Command(resume={"approved": True}), config=config)

The important property is not the exact framework syntax. It is that the graph has a stable identity and the side effect is after the approval boundary. A restart should restore the checkpoint and inspect the existing Action rather than creating a second approval request.

For the complete driver—including stable interrupt IDs, multiple pending decisions, and fail-closed timeout handling—use the LangGraph human-in-the-loop tutorial.

Create the human request outside the interrupt

Create the Action in the graph driver after the interrupt surfaces. That keeps the request side effect outside a node that the framework may replay:

python
import os
from actionbox import Actionbox

box = Actionbox(os.environ["ACTIONBOX_API_KEY"])

action = box.create(
    title="Approve the production deploy?",
    description="The agent is paused before the deployment side effect.",
    options=[
        {"id": "approve", "label": "Approve", "style": "primary"},
        {"id": "reject", "label": "Reject", "style": "destructive"},
    ],
    idempotency_key="langgraph:deploy-2.18.0:approval-1",
)
decision = action.wait(timeout=3600)
approved = decision == "approve"

If the local wait expires, leave the Action open for a later worker or cancel it deliberately. Never treat a local timeout as approval, and never execute a side effect after the process has lost the checkpoint that explains what was approved.

Durable execution is a safety boundary

Use durable execution when the work can outlive a process: deployments, refunds, access grants, data migrations, and AI agent tool calls. The repeatable pattern is:

  1. Prepare the exact operation.
  2. Persist the run identity and checkpoint.
  3. Ask a human with bounded context and an expiry.
  4. Resume the same run with the typed response.
  5. Execute once with an idempotency key.
  6. Report the real success or failure outcome.

For a framework comparison, continue with human-in-the-loop AI agents. For runnable code, choose LangGraph human-in-the-loop or OpenAI Agents SDK human-in-the-loop. You can also start directly with the human approval API.

Create a free Source · Human approval API · Read the docs

Sources and further reading

Try this workflow in minutes

Create a free Source, then run the exact commands from this post against the live API — no approval infrastructure to build.

S
Suson Sapkota

Founder, Actionbox