Skip to main content
The HTTP Bot adapter integrates any backend system with a LangBot pipeline over plain HTTP. Ticketing systems, CRMs, internal tools, custom web apps — all can drive a pipeline through it:
  • Inbound: your backend POSTs a signed message to a fixed LangBot URL;
  • Outbound: LangBot POSTs replies to a callback URL you configure.
It needs no long-lived connection and fully preserves two native pipeline capabilities:
  • Message aggregation (N→1): a user fires several messages in a row, merged into one turn;
  • Multi-part replies (1→M): one turn may produce several replies (function calls, multi-message plugins, streamed chunks).
If you want an in-browser, real-time chat widget, use the Web Page Bot instead. HTTP Bot is designed for backend-to-backend integration.

How it works

Your backend  ──(1) POST signed message──►  LangBot   /bots/<bot_uuid>
                                            (pipeline: aggregate → think → reply)
Your callback ◄─(2) POST signed reply(s)──  LangBot   one POST per reply part
  • (1) Inbound is fire-and-collect: LangBot returns 202 Accepted immediately and does not carry the pipeline result on that response;
  • (2) Outbound replies arrive later as separate signed POSTs to your callback_url; a single turn may produce several callbacks;
  • Everything is keyed by a session_id you choose (e.g. a ticket number); each session_id maps to one isolated session.

Create the bot

In the LangBot WebUI, go to Bots > Create Bot, fill in a name, and pick HTTP Bot as the platform/adapter.

Configuration

FieldRequiredNotes
Inbound Signing SecretyesYour backend signs inbound requests with this; LangBot verifies every inbound request.
Outbound Callback URLyesWhere LangBot POSTs replies. Config-only — cannot be overridden per message (SSRF protection).
Outbound Signing SecretnoLangBot signs callbacks with this; defaults to the inbound secret if blank.
Default Session Typenoperson (default) or group.
Require Inbound SignaturenoKeep enabled in production.
Callback Timeout (seconds)noPer-callback HTTP timeout, default 15.
Callback Max RetriesnoRetries on timeout or 5xx with exponential backoff, default 3.
After binding a pipeline and enabling the bot, the config page shows the Inbound Webhook URL, like https://your-langbot/bots/<bot_uuid>. Copy it.

Signature scheme

Both directions use the same dependency-free HMAC-SHA256 scheme:
signing_string = "{timestamp}." + raw_body_bytes
signature      = "sha256=" + hex(HMAC_SHA256(secret, signing_string))
Sent as headers:
HeaderMeaning
X-LB-TimestampUnix seconds. Rejected if more than ±300s from server time.
X-LB-Signaturesha256=<hex> over "{timestamp}." + body.
X-LB-Idempotency-Key(optional, inbound) dedup key; a repeat returns 409.
Verify outbound callbacks the same way, using the outbound secret (or the inbound secret if left blank).

Send your first message (curl)

BOT="https://your-langbot/bots/<bot_uuid>"
SECRET="your-inbound-secret"
BODY='{"session_id":"ticket-10293","message":[{"type":"Plain","text":"Export keeps failing on the dashboard."}]}'
TS=$(date +%s)
SIG="sha256=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"

curl -sS -X POST "$BOT" \
  -H "Content-Type: application/json" \
  -H "X-LB-Timestamp: $TS" \
  -H "X-LB-Signature: $SIG" \
  -d "$BODY"
# -> 202 {"code":0,"msg":"accepted","data":{"session_id":"ticket-10293","accepted_message_id":"in_...","aggregating":true}}
Replies will be POSTed to your configured callback URL shortly after.

Inbound request format

POST /bots/{bot_uuid}
{
  "session_id": "ticket-10293",
  "session_type": "person",
  "sender": { "id": "user-5567", "name": "Alice" },
  "message": [
    { "type": "Plain", "text": "Export keeps failing on the dashboard." },
    { "type": "Image", "url": "https://example.com/screenshot.png" }
  ]
}
  • session_id (required): your stable id, mapped 1:1 to a LangBot session;
  • message (required): a LangBot message chain. Text uses {"type":"Plain","text":"..."}, images use {"type":"Image","url":"..."} (or base64); other types: Voice, File, At, Quote.
The callback URL is not accepted in the body — it is taken only from bot config. This is deliberate: even if the inbound secret leaks, an attacker cannot redirect replies to an arbitrary host.

Aggregation (N → 1)

If the pipeline has message aggregation enabled, send several messages with the same session_id inside the aggregation window and they merge into one turn. No special flag — just reuse the session_id.

Outbound callback format

LangBot POSTs each reply part to your callback URL:
{
  "session_id": "ticket-10293",
  "reply_to": "in_01H...",
  "sequence": 1,
  "is_final": false,
  "stream": false,
  "message": [ { "type": "Plain", "text": "Looking into it…" } ],
  "timestamp": "2026-06-22T09:00:01Z"
}
Your endpoint should return 2xx quickly. Non-2xx / timeout → LangBot retries with exponential backoff.

Multi-part replies (1 → M)

One turn may emit multiple callbacks, delivered in sequence order for a given session:
seq=1 is_final=false  "Checking your export logs…"
seq=2 is_final=false  "Found 2 failed exports."
seq=3 is_final=true   "Fixed — please try again."
Stitch by session_id + sequence; the turn is complete when is_final: true arrives.

Reset a session

Start a fresh conversation for a session_id (drops history):
POST /bots/{bot_uuid}/reset
{ "session_id": "ticket-10293", "session_type": "person" }
→ 200 { "code":0, "msg":"reset", "data": { "session_id":"ticket-10293", "removed": true } }
Signed exactly like an inbound message.

Synchronous convenience mode

If you don’t need streaming/multi-part and just want one reply back on the same HTTP call, POST to /sync. LangBot waits for the turn to finish and returns all reply parts collapsed into one array:
POST /bots/{bot_uuid}/sync
{ "session_id": "ticket-10293", "message": [ { "type":"Plain", "text":"hi" } ] }
→ 200 { "code":0, "msg":"ok",
        "data": { "session_id":"ticket-10293", "reply_to":"in_...", "message": [ ... ] } }
Sync mode is lossy (you lose sequence and streaming boundaries) and blocks up to callback_timeout × 4 seconds. Prefer the callback model for anything real-time or multi-part. Only one in-flight /sync per session_id.

Error codes

{ "code": 40101, "msg": "invalid signature: signature_mismatch", "data": null }
HTTPcodemeaning
2020accepted
40040001malformed body / missing session_id or message
40140101bad/expired signature
40940901duplicate idempotency key
41341301message too large (>1 MiB)
50050001internal error

Reference clients & 5-minute demo

The main repo’s examples/http-bot/ ships an interactive playground plus Python and TypeScript reference clients.

Interactive playground (run this first)

playground.py is a single-file web app: type a message in your browser → it is signed and POSTed to a running http_bot bot → replies stream back into the page, with a debug panel showing the signature, the 202 ack, and each callback’s sequence / verification.
# From the LangBot repo root, with the backend running:
PUBLIC_IP=<your-host-ip> ./.venv/bin/python examples/http-bot/playground.py
# then open  http://<your-host-ip>:8920/
On startup it reads the API key + the http_bot bot from data/langbot.db and points that bot’s callback_url + secrets back at itself via the LangBot API (live reload, no restart). Requires an enabled http_bot bot bound to a working pipeline.

Command-line reference clients

examples/http-bot/ also ships Python and TypeScript reference clients (with a callback receiver):
cd examples/http-bot
pip install flask requests

# Terminal 1: callback receiver (point the bot's callback_url here; use cloudflared / ngrok locally)
python client.py serve --port 8900 --secret SHARED_SECRET

# Terminal 2: push a message
python client.py push \
  --url https://your-langbot/bots/<bot_uuid> \
  --secret SHARED_SECRET \
  --session ticket-1 \
  --text "hello"
Terminal 1 prints each reply part ([part ] / [FINAL]) with its sequence number — that’s 1→M multi-reply with signature verification, live. A machine-readable contract is in the main repo at docs/http-bot-openapi.json.

Security checklist

  • Keep Require Inbound Signature on in production;
  • Use HTTPS callback URLs, set only in config (no per-message override);
  • Treat secrets like passwords; rotate via the dashboard;
  • The inbound route is unauthenticated at the framework level by design — security comes entirely from the HMAC signature, so never disable it on a public deployment.