Architecture & Lifecycle¶
Every interrupt you register is owned by Ved, the hgateway agent that runs its lifecycle end-to-end — from delivery through resolution and back into your graph.
%%{init: {"themeVariables": {"signalColor": "#0c8f44", "signalTextColor": "#0c8f44", "actorLineColor": "#0c8f44", "sequenceNumberColor": "#0c8f44"}}}%%
sequenceDiagram
participant Agent as Agent (via SDK)
participant Ved as Ved (hgateway)
participant Human as Human (Slack)
participant BFA as Your BFA
rect rgba(12, 143, 68, 0.12)
Note over Agent,BFA: Ved owns the HITL lifecycle end-to-end
Agent->>Ved: register interrupt (HitlSpec) — Raised
Ved-->>Agent: accepted — graph suspends
Ved->>Human: notify (routing, channel) — Delivered
Note over Ved,Human: TTL / escalation / reminders<br/>tracked while waiting — Interaction
Human-->>Ved: response — Resolved
Ved->>BFA: signed resume callback — Resumed
Note over BFA: resumes the agent with<br/>the typed response
end
Note over Ved: responder's reasoning captured<br/>asynchronously — Post-Resolution
Each labeled step above maps directly onto the six-stage vocabulary used throughout this page — the highlighted band is the span Ved owns end-to-end, from the moment your agent raises the interrupt to the moment it resumes.
What's a BFA?¶
BFA = backend for agent — the agent-side analog of a BFF (backend for frontend). Just as a BFF sits behind a frontend and exposes exactly the APIs that frontend needs, a BFA sits in front of an agent and exposes exactly the APIs needed to invoke and resume it, streaming the agent's response back to the client consuming it upstream.
A BFA isn't a hgateway concept with its own SDK class, and it isn't tied to LangGraph or any particular agent framework — it's an independent design pattern that sits alongside whatever ADK your agent is built on. In this flow, your BFA is a service that:
- exposes the invoke/resume surface for your agent,
- owns a callback URL used to resume the agent with the HITL resolution — Ved calls that same API to resume the agent with the resolution, and
- is responsible for turning that resume payload back into whatever your
agent framework needs to continue the paused run (e.g. LangGraph's
Command(resume=...)), then streaming the continuation to its upstream consumer.
The full lifecycle¶
Raised¶
What it means: your agent has hit a point where a human needs to decide, approve, edit, or supply context before it can keep going, and flags that moment as a HITL.
Ved's role: your agent calls
raise_interrupt. The
SDK POSTs the typed HitlSpec
to Ved's register endpoint, authenticated with your HGateway API key (see
Auth: register below). Ved then either
accepts (owns the HITL from here) or declines/is unreachable
(SDK falls back to a local interrupt(fallback) — see the
Integration Guide). If accepted, the SDK suspends
the graph with the sentinel
{"__hgateway_owned__": True, "hitl_instance_id": "..."} — your BFA checks
this to know not to render its own UI (see
Integration Guide, BFA-level integration).
Delivered¶
What it means: the HITL is handed off to whoever needs to act on it.
Ved's role: delivers an interactive message (Slack today) to whoever your
RunFeatures.routing
pointed at, and starts tracking
Ttl expiry in the background
while the HITL waits for interaction.
Interaction¶
What it means: the responder actively engages with the raised HITL before resolving it.
Ved's role: Enables the responder requesting an impact analysis to
estimate the blast radius of an option, a system recommendation, asking a
question on-thread (which Ved replies to), or forwarding the HITL to a peer.
If Ttl expires with no interaction, Ved either applies a default response or
forwards to Routing.secondary per OnExpiry — the proactive staleness
handling described in
Why hgateway. Nothing
waiting on a human silently vanishes.
Resolved¶
What it means: with the help of that interaction, the responder resolves the HITL. Their resolution flows back into your agent.
Ved's role: maps the responder's resolution into the typed response class
matching your content's HITL type/subtype, e.g.
BinaryApprovalResponse
or SingleDecisionResponse
— see Response Schemas for the full set.
Resumed¶
What it means: your agent picks back up with the resolution in hand.
Ved's role: POSTs the resume to your BFA's callback_url:
{
"hitl_instance_id": "…",
"thread_id": "…",
"run_id": "…",
"resume": { "decision": "approve" }
}
authenticated with an HMAC signature your BFA must verify (see
Auth: resume below). Your BFA takes the
resume value and calls graph.invoke(Command(resume=...), config) (or
equivalent) to continue the graph with thread_id.
If your BFA's endpoint doesn't return 2xx, Ved retries with exponential
backoff (up to 5 attempts, capped at 30 minutes between tries). After the
final attempt fails, the HITL's status is set to resume_failed rather than retried forever — check your
dashboard if a resume never arrives.
Post-Resolution¶
What it means: independently of the resume above, the reasoning behind the resolution gets captured for future HITLs to draw on.
Ved's role: asynchronously records the responder's reasoning alongside their resolution, completing the decision trace for the raised HITL (see Why hgateway).
Auth: register (SDK → gateway)¶
The SDK sends your HGATEWAY_AGENT_API_KEY as a standard bearer token:
Authorization: Bearer <HGATEWAY_AGENT_API_KEY>
Auth: resume (gateway → BFA)¶
The gateway signs every resume callback the same way Slack signs its webhooks, so a BFA that already verifies Slack signatures can reuse that logic. Two headers are sent alongside the JSON body:
X-Ved-Signature: v0=<hex hmac-sha256>
X-Ved-Request-Timestamp: <unix timestamp>
The signature is computed as HMAC-SHA256(secret, "v0:{timestamp}:{raw_body}"),
hex-encoded, prefixed v0=. To verify on your BFA:
- Reject the request if
X-Ved-Request-Timestampis more than 5 minutes from your clock (replay protection). - Recompute
HMAC-SHA256(your_shared_secret, f"v0:{timestamp}:{raw_body}")over the exact raw request body bytes (not a re-serialized copy). - Constant-time-compare your result to
X-Ved-Signature.
The shared secret is the same one configured for your agent's Slack signature verification — one secret, reused for both.
Common pitfalls¶
- Resolving your own copy of a gateway-owned interrupt — always check the
__hgateway_owned__sentinel before your BFA does anything with a suspended graph (see Raised above). - Verifying the signature against a re-serialized body — HMAC over
json.dumps(parsed_body)instead of the raw bytes will mismatch if key order or whitespace differs. Verify against the raw request body. - No response within 5 attempts — if your BFA endpoint is down across all
retries, the HITL's status moves to
resume_failed, not retried indefinitely. Make sure yourcallback_urlis reliably reachable.
Where the dashboard fits¶
Every stage of the lifecycle above — Raised through Post-Resolution — is traced per HITL instance on dashboard, so you can see exactly where a given HITL is sitting without instrumenting anything yourself. See Why hgateway for the full feature list — it's not part of the integration path described on this page, nothing here requires it.
Next: Integration Guide to set up the dashboard, install the SDK, and integrate step-by-step.