Human-in-the-loop for LangGraph and LangChain

Human-in-the-loop for LangGraph agents, answered on a phone.

You have two paths. Give the agent a Pushary tool that calls decisions.ask() from inside a node, or pause the graph with LangGraph's native interrupt() and resume it once a human responds. Both send the question to a person's phone, block until they approve, deny, or answer, fail closed if nobody responds, and write the outcome to a durable decisions ledger. Install with pip install pushary, or use @pushary/server in TypeScript. There is no @pushary/langgraph package and you do not need one, because the same decisions.ask call is the whole integration.

pip install pusharytwo calls
import os
from pushary import PusharyServer

px = PusharyServer(api_key=os.environ["PUSHARY_API_KEY"])

# Connect the user's phone once, keyed to your own id.
px.enroll("user_123")

# Ask a human from inside a LangGraph node.
def approve_transfer(state):
    decision = px.decisions.ask(
        question="Approve this transfer?",
        external_id=state["user_id"],
        type="confirm",
    )
    return {"approved": decision["approved"]}

Why you need it

LangGraph pauses the agent. Pushary reaches the human.

LangGraph's interrupt() pauses a node and persists state, and it does not notify anyone or deliver the decision to a phone. Wrap the pause with decisions.ask(): it sends the approval to a person's phone, blocks on a fail-closed answer, survives restarts, and records an audit trail. The externalId is your own end-user, so each user approves their own action.

How it works

From ask to resume

Connect the phone once, then ask a human from the tool or step that guards the action. The waiting, the delivery, and the record are handled for you.

  1. 01

    Install the SDK

    Run pip install pushary for Python, or use @pushary/server in TypeScript. Set your API key.

  2. 02

    Connect the phone

    Call px.enroll(external_id) once per end-user to connect their phone with a keyless one-tap link.

  3. 03

    Ask a human in a node

    Call px.decisions.ask() from inside the node that guards the action, or pause with interrupt() and resume with Command(resume=answer) driven by a Pushary webhook.

  4. 04

    Resume on the decision

    Read the returned decision, which is fail-closed, and continue the graph on the human's answer. Read the durable audit trail whenever you need the record.

A decision is a yes or no confirm, a multiple-choice select, or a free-text input. Every one is stored with its outcome and kept in the audit log.

For your users

Your agent asks your users, not just you

Ship the agent to real users and let each of them approve their own decisions from their phone, under your brand.

Your users answer on their own phones

The externalId is your own id for the end-user. Connect their phone once with a keyless one-tap link, and every approval lands with the right person. No account, no app, no key on their side.

Durable, fail-closed waits

The agent stays open on a durable wait backed by a decisions ledger. If nobody answers in the window you set, the decision comes back denied, so a timeout or a restart never turns into an unapproved action.

Phone-first delivery

Pushary reaches the person on the device they carry: native app push first, then PWA push notifications, then Slack if you route it there. You do not build a delivery pipeline.

Signed webhooks and an audit trail

Every decision is written to a durable ledger you can query and export, and resolved decisions post a signed webhook to your callback URL. You have the record of who approved what and when.

FAQ

Questions, answered

Human-in-the-loop for other frameworks

Let your LangGraph agent ask a human.

Connect a user with one tap, ask a human with one call, and act on a fail-closed decision. The durable store and the audit log come with it.

Partner plan, $99 a month. Your users never see a Pushary login or bill.