Skip to content

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:

  1. Reject the request if X-Ved-Request-Timestamp is more than 5 minutes from your clock (replay protection).
  2. Recompute HMAC-SHA256(your_shared_secret, f"v0:{timestamp}:{raw_body}") over the exact raw request body bytes (not a re-serialized copy).
  3. 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 your callback_url is 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.