Every HITL framework gives you the pause button. GetABrain gives you the humans. LangGraph's interrupt() pauses your graph and waits for input — but it doesn't hand you anyone to provide that input. This is one pattern for wiring GetABrain in as that supply, with the actual LangGraph primitives involved.
The gap in every HITL framework
LangGraph's interrupt(), AWS Bedrock Agents' confirmation step, Cloudflare Agents, and the Microsoft Agent Framework's human-in-the-loop pattern all solve the same half of the problem: they pause execution, persist state, and wait for input to resume. That's genuinely useful — it's the mechanism that keeps a long-running agent from acting on a risky step unsupervised.
None of them solve the other half. They don't hand you a person. Every tutorial for every one of these frameworks ends the same way: "...and then a human reviews it," with no detail on where that human comes from. In practice that means a Slack channel someone has to babysit, or a reviewer queue your own team staffs by hand.
How LangGraph's interrupt() actually works
Quick primer, since the wiring below depends on it: LangGraph's `interrupt()` (from `langgraph.types`) is called from inside a node. It raises internally and the runtime catches it, saves the graph's current state via a configured checkpointer, and returns control to whatever is driving the graph — your code, a server, a CLI.
Nothing resumes automatically. Something external has to call the graph again with `Command(resume=<value>)`; that value becomes `interrupt()`'s return value inside the node, and execution continues from there. This requires a checkpointer to be configured — without one, there's no state to resume from.
That resume call is the exact spot GetABrain fits. The graph is already correctly paused and checkpointed by LangGraph; the only missing piece is who supplies the value passed to `Command(resume=...)`.
Wiring GetABrain in as the human supply
The pattern has three parts: (1) the node that would otherwise ask your own team posts a query to GetABrain instead of blocking, (2) it calls `interrupt()` with enough context (the query id) for something outside the graph to know what it's waiting on, and (3) a small resume loop — a webhook handler or a poll job — watches for GetABrain to deliver a completed, quality-scored human answer and then calls back into the graph with `Command(resume=answer)`.
There's no dedicated GetABrain LangGraph package today — this is a couple of REST calls around LangGraph's own primitives, not a drop-in adapter. If that changes, this post will get updated to point at it instead of the raw pattern below.
Cost
Pay-per-response, from $0.05 per response with a 15% platform fee included — no subscription, no minimum batch. New accounts get $5.00 of free trial credit that activates on email verification, so an agent (or the developer wiring it up) can make a real HITL call before spending anything.
from langgraph.types import interrupt, Command
import requests
GETABRAIN_API = "https://www.getabrain.ai/api/v1/requestor/queries"
def human_review_node(state):
# Post the decision to GetABrain instead of blocking on your own team.
query = requests.post(
GETABRAIN_API,
headers={"X-API-Key": API_KEY, "X-API-Secret": API_SECRET},
json={
"type": "yes_no",
"title": "Approve this agent action?",
"content_data": {"question": state["proposed_action"]},
"required_responses": 3,
"bid_amount_cents": 25,
},
).json()
# Surface the wait through LangGraph's own interrupt so state stays
# correctly checkpointed while GetABrain's humans answer.
answer = interrupt({"waiting_on": "getabrain", "query_id": query["id"]})
return {"approved": answer == "yes"}
# Elsewhere — a webhook handler or poll job watches the GetABrain query and,
# once it has a completed answer, resumes the exact paused graph:
# graph.invoke(Command(resume=answer), config)