← All notes
WorkflowsMay 24, 20264 min read

LangGraph Checkpoints for Human Approval Gates

A practical recipe for adding approval gates to LangGraph agents with checkpoints, interrupts, and resumable tool execution.

LangGraphapproval workflowsagent architecturecheckpoints

LangGraph Checkpoints for Human Approval Gates

Some agent actions should not run on autopilot. Creating a pull request is fine. Force-pushing a branch, spending cloud budget, emailing customers, or deleting records needs a stop sign. The useful pattern is not “human in the loop” as a slogan. It is a small state machine that can pause before a risky tool call, persist enough context, ask for approval, then resume without losing the thread.

LangGraph is a good fit for this pattern because it treats an agent as a graph of nodes and edges, with state passed between steps. Its persistence model lets a graph checkpoint state for a thread, then continue later. That gives builders a clean way to design approval gates instead of hiding them inside prompt text.

Approval-gated LangGraph workflow
Checkpoint before risky action, interrupt for approval, then resume execution

When to add an approval gate

Do not put approval in front of every tool. That makes the agent slow and trains reviewers to rubber-stamp. Gate actions with high blast radius or weak reversibility.

Good approval candidates:

  • writes to production data
  • external messages to users, customers, or vendors
  • cloud actions that create spend or change infrastructure
  • repository actions such as merge, force push, release, or tag
  • security-sensitive actions such as rotating keys or changing permissions

Usually safe without approval:

  • reading documentation
  • searching code
  • running local tests
  • creating a draft artifact
  • proposing a patch without applying it

The design goal: let the agent do cheap thinking and reversible work alone, then ask before irreversible work.

Minimal graph shape

A practical approval graph has five parts:

PartPurposeOutput
Intake nodeNormalize user request and inputsStructured task state
Plan nodeDecide next action and tool argumentsProposed action
Approval gatePause if action is riskyApproval request
Tool nodeExecute approved actionTool result
Verify nodeCheck result and summarizeFinal response

The important boundary is between “propose” and “execute.” The agent should produce a concrete action for review: tool name, arguments, target resource, expected effect, rollback option, and why the action is needed. Reviewers should not approve vague text like “update infrastructure.” They should approve a specific operation.

State to persist

Checkpointing works best when state is explicit. Keep the graph state small, serializable, and reviewable.

Example state shape:

from typing import TypedDict, Literal

class AgentState(TypedDict):
    task: str
    thread_id: str
    risk_level: Literal["low", "medium", "high"]
    proposed_tool: str | None
    proposed_args: dict | None
    approval_status: Literal["not_required", "pending", "approved", "rejected"]
    tool_result: dict | None
    final_summary: str | None

This state gives the graph enough information to pause and resume. It also gives the reviewer a compact audit record. Avoid storing giant logs or raw secrets in state. Store references, paths, IDs, and redacted summaries instead.

Recipe: approval before tool execution

  1. Classify risk in code, not only in prompts. Use deterministic rules for known risky tools. For example, send_email, deploy_service, and merge_pr can require approval by default.
  2. Generate a proposed action. The plan node fills proposed_tool and proposed_args, but does not call the tool.
  3. Checkpoint before the gate. Persist state using the graph checkpointer for the current thread.
  4. Interrupt for approval. Return an approval request to the UI, chat, or ticket system. Include exact arguments and expected side effects.
  5. Resume with a decision. If approved, continue to the tool node. If rejected, route to a revise or stop node.
  6. Verify after execution. The tool node should return evidence: command output, API response ID, diff summary, or resource URL.
  7. Record the decision. Keep approver, timestamp, decision, and instruction outside the prompt transcript where possible.

Approval request format

A clear approval request is short and structured:

Action: merge_pr
Target: repo=acme/api, pr=142
Reason: tests passed and reviewer requested merge
Expected effect: main branch receives 3 commits
Rollback: revert merge commit
Approve: approve thread abc123
Reject: reject thread abc123: explain reason

This format is boring on purpose. It reduces ambiguity. It also makes approvals searchable later.

Common mistakes

  • Prompt-only gates. “Ask before doing dangerous things” in the system prompt is not enough. Route risky tools through graph logic.
  • Approval after execution. A summary after a destructive tool call is not approval. Put the gate before the tool node.
  • Hidden arguments. Reviewers need the exact resource and arguments, not a natural-language paraphrase.
  • No resume ID. Approval needs a stable thread_id or equivalent handle. Otherwise the system cannot safely continue the right run.
  • Over-gating. If every step requires approval, the agent becomes a manual checklist with extra latency.

Builder checklist

Before shipping an approval-gated LangGraph agent, verify:

  • [ ] risky tools are listed in code
  • [ ] state includes proposed tool and arguments
  • [ ] checkpointing is enabled per thread
  • [ ] approval request shows side effects and rollback
  • [ ] rejection has a route, not just an error
  • [ ] execution returns verifiable evidence
  • [ ] approvals are logged outside model-generated text

The payoff is simple: agents can keep autonomy for exploration, drafting, testing, and analysis, while humans stay responsible for high-impact actions. That is the practical middle ground for production agent systems: fast where mistakes are cheap, careful where mistakes are expensive.

END OF NOTE