Skip to content

Webhooks

The relay POSTs chat events to your backend under /api/v1/chat-callbacks/*, each signed with HMAC#1 — the same recipe as your outbound calls, in the other direction. The Python SDK adapter mounts these endpoints and verifies the signature for you.

The relay builds the callback URL from the tenant’s callback_url_base (configured at provisioning or via update/tenant): it appends /chat-callbacks/<event> and signs each request.

POST {callback_url_base}/api/v1/chat-callbacks/<event>

The adapter mounts nine endpoints. Six have existed since the first phase; the three agent_* events arrive with agent takeover.

PathEvent
POST /api/v1/chat-callbacks/conversation-startedNew conversation opened
POST /api/v1/chat-callbacks/message-receivedThe visitor sent a message
POST /api/v1/chat-callbacks/ticket-createdTicket created automatically by escalation
POST /api/v1/chat-callbacks/ticket-assignedTicket taken by a human agent
POST /api/v1/chat-callbacks/ticket-resolvedTicket marked resolved
POST /api/v1/chat-callbacks/escalation-triggeredThe bot escalated to a human
POST /api/v1/chat-callbacks/agent-assignedAn operator took the conversation
POST /api/v1/chat-callbacks/agent-releasedThe operator returned the conversation to the queue
POST /api/v1/chat-callbacks/agent-resolvedThe operator marked the conversation resolved

BloonioChatAdapter.from_env(app) mounts the nine endpoints and wires the client from the BLOONIO_CHAT_* variables.

from fastapi import FastAPI
from bloonio_chat_relay_client.adapters.fastapi import BloonioChatAdapter
app = FastAPI()
BloonioChatAdapter.from_env(app) # mounts /api/v1/chat-callbacks/* + wires the client

Register handlers to react to events on your side. Handlers are optional: an event with no registered handler is still accepted (HMAC#1 signature verified), logged at debug level, and returns {"received": true}.

async def on_ticket_created(event):
... # update your CRM, notify, etc.
BloonioChatAdapter.from_env(app, handlers={
"ticket-created": on_ticket_created,
})

The three agent-takeover events share the same body shape:

{
"conversation_id": "<chat_api ObjectId>",
"tenant_id": "<UUID v7>",
"status": "ASSIGNED" | "WAITING" | "RESOLVED",
"operator_id": "<UUID v7>",
"operator_display_name": "Sarah Chen", // only on agent-assigned
"visitor_session_id": "vs_<base64url>",
"escalation_reason": "user_requested" | "restricted_topic" | "low_confidence" | "direct" | null,
"claim_count": 1,
"claimed_at": "2026-05-22T12:34:56Z",
"released_at": null,
"resolved_at": null
}

Use it to update your CRM, send push notifications or trigger analytics.

  1. Read the three X-Bloonio-* headers from the incoming request.
  2. Recompute hex(hmac_sha256(tenant_secret, f"{ts_ms}.{sha256_hex(body)}")).
  3. Compare in constant time; check timestamp freshness and the absence of replay.
  4. If everything passes, process the event; otherwise, respond 401.

The SDK adapter does exactly this before calling your handlers — prefer it to a hand-rolled verification.