# redphonr vs LangGraph interrupt(): build or buy human-in-the-loop?

> LangGraph's interrupt() pauses a graph. It does not notify anyone, escalate or log who answered. How redphonr fills that gap, with code for both together.

Source: https://blog.blueprintd.co/posts/redphonr-vs-langgraph-interrupt/
Type: Comparison
Published: 3 October 2026

> **Disclosure:** We make redphonr. LangGraph details come from the LangGraph documentation as of October 2026.

LangGraph has a clean human-in-the-loop primitive: `interrupt()`. Many teams assume that solves the problem. It solves half of it. This post explains which half, and when to add redphonr.

## What interrupt() does

```python
from langgraph.types import interrupt, Command

def approve_refund(state):
    decision = interrupt({"question": f"Refund ${state['amount']} to {state['order_id']}?"})
    return {"approved": decision == "approve"}

graph = builder.compile(checkpointer=checkpointer)
graph.invoke(inputs, config={"configurable": {"thread_id": "order-88213"}})
# ...later, once you have the answer:
graph.invoke(Command(resume="approve"), config={"configurable": {"thread_id": "order-88213"}})
```

The graph stops, its state is saved by the checkpointer, and it resumes with whatever you pass to `Command(resume=...)`. It is durable, free and well designed.

## What it does not do

`interrupt()` hands a payload back to the code that ran the graph. Everything after that is yours:

- **Delivering the question** to a person: Slack, SMS, email, a call.
- **Waiting** for them across minutes or hours, outside your request cycle.
- **Escalating** when they do not answer: another channel, then the backup person.
- **Collecting the answer** and calling `Command(resume=...)` on the right thread.
- **Timeouts** with a safe default.
- **An audit trail** of who decided, when and how.

If the user is sitting in your app's chat UI, this is easy: show a button. If the agent runs in the background, it is a small notification system you now have to build and run.

## Comparison

| | LangGraph interrupt() | [redphonr](https://www.redphonr.com/?utm_source=blog&utm_medium=post&utm_campaign=redphonr-vs-langgraph-interrupt) |
|---|---|---|
| Pauses and saves agent state | Yes | The agent waits on a tool call |
| Delivers the question to a person | No | Slack, SMS, WhatsApp, Telegram, Discord, email, voice |
| Escalation and on-call | No | Yes |
| Timeout with deny default | You build it | Built in |
| Audit log | You build it | Built in |
| Works outside LangGraph | No | Any MCP agent or HTTP client |
| Cost | Free, open source | Free to start |

## Option 1: give the agent redphonr's tools

Load the redphonr MCP server into a LangChain agent (which runs on LangGraph) with the LangChain MCP adapters, and the model can call `request_approval` itself:

```python
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

client = MultiServerMCPClient({
    "redphonr": {
        "transport": "streamable_http",
        "url": "https://mcp.redphonr.com/mcp",
        "headers": {"Authorization": f"Bearer {REDPHONR_API_KEY}"},
    }
})
tools = await client.get_tools()
agent = create_agent(model, tools=tools + your_tools, system_prompt=SYSTEM_PROMPT)
```

This is the fastest route. The tool call blocks until a person answers or the request times out.

## Option 2: keep interrupt(), use redphonr for delivery

For very long waits, you may not want a process blocked on a tool call. Keep `interrupt()` for state, create a redphonr request when the graph pauses, and resume the thread when the answer arrives:

```python
state = graph.invoke(inputs, config)
if "__interrupt__" in state:
    question = state["__interrupt__"][0].value["question"]
    req = create_redphonr_request(question, thread_id=config["configurable"]["thread_id"])

# In your webhook or poller, when the request is resolved:
graph.invoke(Command(resume=decision), config={"configurable": {"thread_id": thread_id}})
```


## Which should you use?

- **interrupt() alone** when the person is in your UI when the graph pauses.
- **redphonr** when the agent runs unattended and the person could be anywhere, or when you need escalation and an audit trail.
- **Both** for long-running graphs: LangGraph keeps the state, redphonr gets the answer.

See also [the human-in-the-loop guide](https://blog.blueprintd.co/posts/human-in-the-loop-ai-agents-guide/).

**[Add redphonr to your LangGraph agent](https://www.redphonr.com/?utm_source=blog&utm_medium=post&utm_campaign=redphonr-vs-langgraph-interrupt)**

## Frequently asked questions

### How does human-in-the-loop work in LangGraph?

You call interrupt() inside a node with a payload. LangGraph saves the graph state with a checkpointer and stops. Later you resume the same thread with Command(resume=value), and that value becomes the return value of interrupt().

### Does LangGraph interrupt() send a notification?

No. interrupt() pauses the graph and surfaces a payload to whatever code invoked it. Delivering the question to a person, waiting for them, escalating and resuming the graph is up to you or a service like redphonr.

### Can I use redphonr with LangGraph?

Yes, in two ways. Load redphonr's MCP tools into a LangGraph agent so it can call request_approval directly, or keep interrupt() for state and use redphonr to deliver the question and resume the thread when the person answers.
