🔒 Read-only demo — browse freely, no changes saved. Deploy your own →
Why this exists

Your agent is about to wire $50,000. Who approves it?

You built the agent. You wired up LangGraph's interrupt() to pause before the irreversible action. Now what? Who gets notified? Where do they decide? What if they're on holiday? You're three days from launch and you're about to build a notification system, a dashboard, and an escalation engine from scratch.

What LangGraph gives you
interrupt() — pause execution
resume() — continue after decision
Notification to the approver
Dashboard to approve / reject
Escalation if no response
Audit trail for compliance
Team routing
What Approval Hub adds
interrupt() — pause execution
resume() — continue after decision
Email + Slack notification instantly
Clean dashboard to approve / reject
Auto-escalate after your timeout
Full append-only audit log
Route to a person or a named team
💸 Finance Agent

Wire $50,000 to a new vendor account

The agent has verified the details. It needs one human to confirm before it moves the money. With LangGraph alone — nobody knows it's waiting.

🗂 HR Agent

Send rejection emails to 40 candidates

The pipeline ran overnight. Before it fires 40 emails, someone on the team should review the shortlist. interrupt() paused it. But who got pinged?

💬 Developer, production deploy

"When a thread is interrupted, nobody gets notified. No email, no Slack, no ping of any kind."

💬 Developer, LangGraph community

"There's no built-in mechanism to say 'if nobody responds in 30 minutes, escalate to the backup approver.'"

Approval Hub fills every gap — deployed in 5 minutes, free forever.

Dashboard · Email · Slack · Audit trail · Escalation · Team routing. Open source, self-hosted. And you're not alone — 85% of teams building AI agents say human oversight is non-negotiable (Gartner, 2025).

Get Running

Up and running in 5 minutes

Four steps. One Vercel deploy, one env var table, one pip install, three lines of Python.

1

Deploy your hub to Vercel

One click. Vercel clones the repo and prompts you for env vars during setup.

▲ Deploy to Vercel
2

Set environment variables

In Vercel → Project Settings → Environment Variables, add:

VariableWhere to get it
SUPABASE_URLSupabase → Project Settings → API
SUPABASE_SERVICE_ROLE_KEYSupabase → Project Settings → API → service_role
SUPABASE_ANON_KEYSupabase → Project Settings → API → anon/public
API_SECRET_TOKENAny random string — becomes your auth key
NEXT_PUBLIC_APP_URLYour Vercel deployment URL (e.g. https://my-hub.vercel.app)
RESEND_API_KEYresend.com → API Keys (optional — enables email)
SLACK_WEBHOOK_URLSlack → Incoming Webhooks (optional — enables Slack)
3

Install the SDK

pip install langgraph-approval-hub
Requires Python 3.9+. The only dependency is requests — no LangChain version pinning, no conflicts.
4

Add to your agent

Three lines. Paste anywhere inside a LangGraph node function.

Python
from langgraph_approval_hub import request_approval

decision = request_approval(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    agent_name="Finance Agent",
    action_description="Process $4,200 refund for 12 customers",
    assignee="alice@acme.com",
    assignee_type="email",
)

if decision == "approved":
    process_refunds()
request_approval() blocks until a human decides. Alice gets an email with a direct dashboard link. Your agent resumes the moment she clicks Approve or Reject.
Code Patterns

Real-world examples

Every team has slightly different requirements. These patterns cover the most common ones.

Basic approval — person assignee

The simplest case: one person must approve before the agent continues.

Python
from langgraph_approval_hub import request_approval

decision = request_approval(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    agent_name="Finance Agent",
    action_description="Process $4,200 refund for 12 customers",
    assignee="alice@acme.com",
    assignee_type="email",
)

if decision == "approved":
    process_refunds()

Route to a team

Use assignee_type="team" when multiple people share responsibility. All team members are notified — first to respond wins.

Python
decision = request_approval(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    agent_name="Data Pipeline Agent",
    action_description="Drop and recreate the events_staging table",
    assignee="data-team",        # team name from Settings → Teams
    assignee_type="team",
)

Escalate if no response

If the assignee doesn't respond within timeout_minutes, the hub re-notifies the escalation target and marks the request as Escalated on the dashboard.

Python
decision = request_approval(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    agent_name="Billing Agent",
    action_description="Issue $12,000 credit to enterprise account ACME-001",
    assignee="billing@acme.com",
    assignee_type="email",
    escalate_to="cfo@acme.com",  # notified if no response
    timeout_minutes=30,           # escalates after 30 min
)

Show agent reasoning + handle errors

Pass agent_reasoning to give the approver full context. Always wrap in try/except — the SDK raises TimeoutError and RuntimeError.

With reasoning
decision = request_approval(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    agent_name="Customer Service Agent",
    action_description="Issue full refund of $340 to customer #A-4821",
    assignee="support-team",
    assignee_type="team",
    agent_reasoning="""
1. Customer purchased on 2024-03-01, within the 30-day return window.
2. Item returned in original packaging — no restocking fee applies.
3. Customer account has no prior refund requests.
4. Refund amount matches the original charge exactly.
""",
)
With error handling
try:
    decision = request_approval(
        hub_url="https://your-hub.vercel.app",
        api_token="your-api-secret-token",
        agent_name="Finance Agent",
        action_description="Wire $50,000 to vendor account",
        assignee="finance-team",
        assignee_type="team",
        timeout_minutes=60,
    )
except TimeoutError:
    # Nobody responded — fail safe, do nothing
    notify_team("Approval timed out — no action taken")
    return
except RuntimeError as e:
    # Hub unreachable or rejected the request
    log_error(e)
    raise

if decision == "approved":
    execute_wire_transfer()
Default safe: if decision == "rejected" or an exception is raised, your agent should do nothing. Never treat a missing decision as implicit approval.

Non-blocking (async) pattern

Use submit_approval() when you want to continue working while waiting for a decision. Check back later with get_decision().

Python
from langgraph_approval_hub import submit_approval, get_decision

# Submit — returns immediately, agent continues
approval_id = submit_approval(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    agent_name="OutreachBot",
    action_description="Send cold email campaign to 200 leads",
    assignee="marketing@company.com",
    assignee_type="email",
)

# ... do other work while waiting ...

# Check the decision later (non-blocking)
result = get_decision(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
    approval_id=approval_id,
)
print(result["status"])  # "pending" | "approved" | "rejected" | "expired"

List all pending approvals:

Python
from langgraph_approval_hub import get_pending

pending = get_pending(
    hub_url="https://your-hub.vercel.app",
    api_token="your-api-secret-token",
)
for item in pending:
    print(item["id"], item["agent_name"], item["status"])
New in v0.2.0. Install with pip install langgraph-approval-hub==0.2.0
Enterprise

Deploy for your whole org

One hub instance handles all your agents. Route approvals to the right team, escalate to managers, export decisions for compliance.

How it fits together

Your LangGraph Agent → POST /api/interrupt → Approval Hub (Vercel)
↓ saves to Supabase
↓ fires notifications → Email (Resend) + Slack webhook
↓ Approver opens dashboard link
↓ Clicks Approve / Reject
Agent resumes with "approved" or "rejected"

Enabling notifications

Set these two env vars in Vercel — both are free:

VariableServiceWhat it enables
RESEND_API_KEYresend.com — free up to 100 emails/dayEmail to assignee (and team members) on every new request
SLACK_WEBHOOK_URLSlack Incoming Webhooks — freeSlack message to your channel on every new request + escalation

Audit trail

Every decision is recorded — agent name, action, who decided, when, and any note they left. Go to the Audit Log to view or export as JSON.

The audit log is append-only. Approved and rejected decisions are both recorded. Use ↗ Export JSON for compliance exports.
Reference

SDK & API reference

Full parameter list and API endpoint table.

SDK parameters

ParameterTypeRequiredDescription
hub_urlstrRequiredYour Approval Hub URL
api_tokenstrRequiredYour API_SECRET_TOKEN env var value
agent_namestrRequiredName shown in the dashboard
action_descriptionstrRequiredPlain-English description of what the agent wants to do
assigneestrRequiredEmail address or team name
assignee_type"email" | "team"RequiredWhether assignee is an individual or a team
agent_reasoningstrOptionalStep-by-step reasoning shown to the approver on the detail page
escalate_tostrOptionalEmail to escalate to if timeout exceeded
timeout_minutesintOptionalMinutes before escalation triggers (default: 60)
poll_intervalintOptionalSeconds between status polls — request_approval() only (default: 5)

API endpoints

All endpoints are on your Vercel deployment URL. POST /api/interrupt requires a Bearer token in the Authorization header.

MethodRouteAuthDescription
POST/api/interruptBearer tokenCreate approval request, fire notifications
GET/api/approvalsBearer tokenList approvals (?status=pending|escalated)
GET/api/approvals/[id]Bearer tokenGet single approval with notification log
POST/api/approvals/[id]/decideNoneSubmit approve or reject decision
GET/api/auditNoneFull audit log — also used for JSON export
GET/api/teamsNoneList all configured teams
POST/api/teamsNoneCreate or update a team