Events
The nine events across the leads, conversations, messages and knowledge groups, their payloads, the resolved-versus-closed split, and the volume cap.
Nine events are delivered, across the four feature groups. Each arrives in the standard envelope described in Webhooks overview; only the data object differs. Your endpoint receives every event of the group it is wired to — branch on event.
Group leads
lead.created
Fires for every captured lead, whatever the capture path — the widget form, the agent's capture_lead action, or the public lead endpoint.
{
"id": "9f1c1c2a-1b21-4f7c-9d0f-6a3a4a1c9d55",
"event": "lead.created",
"data": {
"id": "5b3e6c30-9c2c-4a2c-8a53-1f6f0c9a77b1",
"chatbotId": "0c21f0e4-6d54-4e33-9a2b-7a0f4a2d1c88",
"email": "sam@example.com",
"name": "Sam Rivera",
"phone": null
},
"ts": 1786290764000
}name and phone are null when the visitor did not provide them.
Group conversations
conversation.created
Fires when a new conversation thread starts — on any channel, including the omnichannel ones. It fires when the thread is created, not on every message.
{
"id": "3d0d1a77-5f2b-4a1e-b6a1-2c8c0f5d9e10",
"event": "conversation.created",
"data": {
"conversationId": "b1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"chatbotId": "0c21f0e4-6d54-4e33-9a2b-7a0f4a2d1c88",
"source": "public_widget"
},
"ts": 1786290801000
}conversation.resolved and conversation.closed
Two mutually exclusive end-of-life events, split by which terminal state the conversation entered:
conversation.resolvedfires on the transition to Closed — the "handled and done" outcome (the Resolve button, orPOST /api/v1/conversations/{id}/resolve). Itsstatusisclosed.conversation.closedfires on the transition to Archived or Spam; itsstatusisarchivedorspam, telling you which.
Both are user/agent-driven — no background job auto-closes conversations, so hearing one always means a person (or your own API call) did it. The two share one payload shape:
| Field | Notes |
|---|---|
conversationId | The conversation that reached a terminal state. |
chatbotId | The chatbot it belongs to; null when it has none. |
status | closed (resolved), or archived / spam (closed). |
{
"id": "6a2f8c41-9d3e-4b7a-8c15-3f0e9d2c4b68",
"event": "conversation.closed",
"data": {
"conversationId": "b1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"chatbotId": "0c21f0e4-6d54-4e33-9a2b-7a0f4a2d1c88",
"status": "archived"
},
"ts": 1786291205000
}handoff.requested
Fires when a visitor asks for a human (or the AI escalates), whatever the entry point. Use it to page your team in real time instead of watching the inbox.
| Field | Notes |
|---|---|
conversationId | The conversation now waiting for a human. |
chatbotId | The chatbot it belongs to; null when it has none. |
source | Always escalation today — the handoff entry point. |
{
"id": "8d1a2b63-3e49-4c7b-9d26-4a1fae3d5c79",
"event": "handoff.requested",
"data": {
"conversationId": "b1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"chatbotId": "0c21f0e4-6d54-4e33-9a2b-7a0f4a2d1c88",
"source": "escalation"
},
"ts": 1786291180000
}csat.received
Fires when a visitor submits a satisfaction rating, on their first rating for that conversation.
{
"id": "7c9b0a52-2f38-4b6d-a0e1-9f3b7d4c2a61",
"event": "csat.received",
"data": {
"conversationId": "b1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"rating": 5,
"comment": "Answered instantly, great."
},
"ts": 1786291042000
}rating is 1–5. comment is null when none was written.
Group messages
message.created
Fires for every message — visitor, AI, and agent — from the platform's single message choke point. It carries metadata only, never the message content (volume and privacy); fetch the body from GET /api/v1/conversations/{id}/messages when you need it.
| Field | Notes |
|---|---|
conversationId | The conversation the message belongs to. |
chatbotId | The chatbot it belongs to; null when it has none. |
messageId | The new message. |
role | user (visitor), assistant (AI), or agent (a human). |
createdAt | ISO 8601 UTC timestamp. |
{
"id": "2b7d9e14-5c31-4a08-8f62-7d0c3b1e9a44",
"event": "message.created",
"data": {
"conversationId": "b1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"chatbotId": "0c21f0e4-6d54-4e33-9a2b-7a0f4a2d1c88",
"messageId": "c4e5f6a7-8b9c-4d0e-a1f2-3b4c5d6e7f80",
"role": "assistant",
"createdAt": "2026-08-08T09:12:44.000Z"
},
"ts": 1786291042000
}This event is throttled to 300 per minute per workspace. Events over the cap are dropped, not queued (the drop is counted for observability), so treat the stream as a real-time signal, not a lossless log — reconcile against GET /api/v1/conversations/{id}/messages when completeness matters.
Group knowledge
source.ingest.completed and source.ingest.failed
Fire when a knowledge source finishes processing — successfully or not — covering uploads, URL adds, crawls, edits, and reprocessing. failed fires only on the final attempt, after retries are exhausted, so one failure event means the source genuinely needs attention.
Both carry the same fields; source.ingest.failed adds error:
| Field | Notes |
|---|---|
sourceId | The knowledge source. |
chatbotId | The chatbot the source belongs to. |
type | file, url, manual, or faq. |
title | The source's title. |
error | Short failure reason. source.ingest.failed only. |
{
"id": "5f2c8a19-6d40-4b3e-9c17-2e8b1d4a6f03",
"event": "source.ingest.failed",
"data": {
"sourceId": "7a1b2c3d-4e5f-4a6b-8c9d-0e1f2a3b4c5d",
"chatbotId": "0c21f0e4-6d54-4e33-9a2b-7a0f4a2d1c88",
"type": "url",
"title": "Pricing page",
"error": "Fetch failed: 404 Not Found"
},
"ts": 1786291500000
}Where to go next
Last updated