Skip to main content

Webhook events

bubbl3 sends 14 event types, plus one conversion.<event key> type per event in the workspace's catalog in Settings, Events (for example conversion.lead.replied), which an endpoint subscribes to by name. A webhook URL added on an event's page is an endpoint subscribed to that event's type. Every delivery is one JSON object with the same envelope, signed as described in the webhooks quick-start. The examples are the bodies a test event sends.

Envelope​

FieldTypeDescription
idstringEvent id (evt_...).
type"message.received"
created_atstring (date-time)ISO 8601 timestamp.
workspace_idstringWorkspace id (ws_...).
dataobject

message.received​

A contact sent a message to one of your lines.

data fields

FieldTypeRequiredDescription
messageobjectYes
message.idstringYesMessage id (msg_...).
message.conversation_idstringYesConversation id (cnv_...).
message.contact_idstringYesContact id (ctc_...).
message.line_idstring or nullYesLine id (ln_...).
message.directionenumYesOne of: outbound, inbound.
message.channelenumYesOne of: imessage, sms.
message.tostring or nullYesPhone number in E.164 format.
message.fromstring or nullYesPhone number in E.164 format.
message.bodystringYes
message.mediaobject[]Yes
message.media[].urlstringYes
message.media[].mimestring or nullYes
message.media[].filenamestring or nullYes
message.media[].sizeinteger or nullYesFrom 0 to 9007199254740991.
message.statusenumYesOne of: queued, scheduled, sending, sent, delivered, read, failed, received.
message.error_codestring or nullYes
message.error_detailsobject or nullYes
message.scheduled_forstring (date-time) or nullYesISO 8601 timestamp.
message.sent_atstring (date-time) or nullYesISO 8601 timestamp.
message.delivered_atstring (date-time) or nullYesISO 8601 timestamp.
message.read_atstring (date-time) or nullYesISO 8601 timestamp.
message.external_idstring or nullYes
message.sourceenum or nullYesOne of: api, dashboard, workflow, agent, outreach, mcp.
message.sent_by_staffbooleanYes
message.metadataobjectYes
message.is_testbooleanYes
message.created_atstring (date-time)YesISO 8601 timestamp.
message.updated_atstring (date-time)YesISO 8601 timestamp.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "message.received",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"message": {
"id": "msg_TestMessage000001",
"conversation_id": "cnv_TestConvo00000001",
"contact_id": "ctc_TestContact000001",
"line_id": "ln_TestLine0000000001",
"direction": "inbound",
"channel": "imessage",
"to": "+12125550101",
"from": "+12125550100",
"body": "This is a test event from bubbl3.",
"media": [],
"status": "received",
"error_code": null,
"error_details": null,
"scheduled_for": null,
"sent_at": null,
"delivered_at": null,
"read_at": null,
"external_id": null,
"source": "api",
"sent_by_staff": false,
"metadata": {},
"is_test": true,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
}
}
}

message.status​

A message you sent changed status, for example from sent to delivered or read, or failed.

data fields

FieldTypeRequiredDescription
messageobjectYes
message.idstringYesMessage id (msg_...).
message.conversation_idstringYesConversation id (cnv_...).
message.contact_idstringYesContact id (ctc_...).
message.line_idstring or nullYesLine id (ln_...).
message.directionenumYesOne of: outbound, inbound.
message.channelenumYesOne of: imessage, sms.
message.tostring or nullYesPhone number in E.164 format.
message.fromstring or nullYesPhone number in E.164 format.
message.bodystringYes
message.mediaobject[]Yes
message.media[].urlstringYes
message.media[].mimestring or nullYes
message.media[].filenamestring or nullYes
message.media[].sizeinteger or nullYesFrom 0 to 9007199254740991.
message.statusenumYesOne of: queued, scheduled, sending, sent, delivered, read, failed, received.
message.error_codestring or nullYes
message.error_detailsobject or nullYes
message.scheduled_forstring (date-time) or nullYesISO 8601 timestamp.
message.sent_atstring (date-time) or nullYesISO 8601 timestamp.
message.delivered_atstring (date-time) or nullYesISO 8601 timestamp.
message.read_atstring (date-time) or nullYesISO 8601 timestamp.
message.external_idstring or nullYes
message.sourceenum or nullYesOne of: api, dashboard, workflow, agent, outreach, mcp.
message.sent_by_staffbooleanYes
message.metadataobjectYes
message.is_testbooleanYes
message.created_atstring (date-time)YesISO 8601 timestamp.
message.updated_atstring (date-time)YesISO 8601 timestamp.
previous_statusenum or nullYesOne of: queued, scheduled, sending, sent, delivered, read, failed, received.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "message.status",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"message": {
"id": "msg_TestMessage000001",
"conversation_id": "cnv_TestConvo00000001",
"contact_id": "ctc_TestContact000001",
"line_id": "ln_TestLine0000000001",
"direction": "outbound",
"channel": "imessage",
"to": "+12125550100",
"from": "+12125550101",
"body": "This is a test event from bubbl3.",
"media": [],
"status": "delivered",
"error_code": null,
"error_details": null,
"scheduled_for": null,
"sent_at": "2026-01-01T12:00:00.000Z",
"delivered_at": "2026-01-01T12:00:00.000Z",
"read_at": null,
"external_id": null,
"source": "api",
"sent_by_staff": false,
"metadata": {},
"is_test": true,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
},
"previous_status": "sent"
}
}

contact.created​

A contact was created, by the API, an import or an inbound message.

data fields

FieldTypeRequiredDescription
contactobjectYes
contact.idstringYesContact id (ctc_...).
contact.phonestringYesPhone number in E.164 format.
contact.emailstring or nullYes
contact.first_namestring or nullYes
contact.last_namestring or nullYes
contact.companystring or nullYes
contact.fieldsobjectYes
contact.labelsstring[]Yes
contact.consent_statusenumYesOne of: unknown, opted_in, opted_out.
contact.consent_sourcestring or nullYes
contact.consent_atstring (date-time) or nullYesISO 8601 timestamp.
contact.imessage_capableboolean or nullYes
contact.capability_checked_atstring (date-time) or nullYesISO 8601 timestamp.
contact.assigned_line_idstring or nullYesLine id (ln_...).
contact.owner_user_idstring (uuid) or nullYes
contact.last_inbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.last_outbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.first_reply_atstring (date-time) or nullYesISO 8601 timestamp.
contact.outreach_classenumYescold for a contact from an outbound tool; standard otherwise. One of: standard, cold.
contact.test_contactbooleanYesMarked for live workflow tests: a live test only texts test contacts.
contact.created_atstring (date-time)YesISO 8601 timestamp.
contact.updated_atstring (date-time)YesISO 8601 timestamp.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "contact.created",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"contact": {
"id": "ctc_TestContact000001",
"phone": "+12125550100",
"email": null,
"first_name": "Test",
"last_name": "Contact",
"company": null,
"fields": {},
"labels": [],
"consent_status": "opted_in",
"consent_source": "test_event",
"consent_at": "2026-01-01T12:00:00.000Z",
"imessage_capable": true,
"capability_checked_at": "2026-01-01T12:00:00.000Z",
"assigned_line_id": "ln_TestLine0000000001",
"owner_user_id": null,
"last_inbound_at": "2026-01-01T12:00:00.000Z",
"last_outbound_at": null,
"first_reply_at": "2026-01-01T12:00:00.000Z",
"outreach_class": "standard",
"test_contact": false,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
}
}
}

contact.opted_out​

A contact opted out, by texting a STOP keyword or through the API or dashboard.

data fields

FieldTypeRequiredDescription
contactobjectYes
contact.idstringYesContact id (ctc_...).
contact.phonestringYesPhone number in E.164 format.
contact.emailstring or nullYes
contact.first_namestring or nullYes
contact.last_namestring or nullYes
contact.companystring or nullYes
contact.fieldsobjectYes
contact.labelsstring[]Yes
contact.consent_statusenumYesOne of: unknown, opted_in, opted_out.
contact.consent_sourcestring or nullYes
contact.consent_atstring (date-time) or nullYesISO 8601 timestamp.
contact.imessage_capableboolean or nullYes
contact.capability_checked_atstring (date-time) or nullYesISO 8601 timestamp.
contact.assigned_line_idstring or nullYesLine id (ln_...).
contact.owner_user_idstring (uuid) or nullYes
contact.last_inbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.last_outbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.first_reply_atstring (date-time) or nullYesISO 8601 timestamp.
contact.outreach_classenumYescold for a contact from an outbound tool; standard otherwise. One of: standard, cold.
contact.test_contactbooleanYesMarked for live workflow tests: a live test only texts test contacts.
contact.created_atstring (date-time)YesISO 8601 timestamp.
contact.updated_atstring (date-time)YesISO 8601 timestamp.
keywordstring or nullYesThe STOP keyword, when the contact texted one.
sourcestringYes

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "contact.opted_out",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"contact": {
"id": "ctc_TestContact000001",
"phone": "+12125550100",
"email": null,
"first_name": "Test",
"last_name": "Contact",
"company": null,
"fields": {},
"labels": [],
"consent_status": "opted_out",
"consent_source": "test_event",
"consent_at": "2026-01-01T12:00:00.000Z",
"imessage_capable": true,
"capability_checked_at": "2026-01-01T12:00:00.000Z",
"assigned_line_id": "ln_TestLine0000000001",
"owner_user_id": null,
"last_inbound_at": "2026-01-01T12:00:00.000Z",
"last_outbound_at": null,
"first_reply_at": "2026-01-01T12:00:00.000Z",
"outreach_class": "standard",
"test_contact": false,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
},
"keyword": "STOP",
"source": "inbound_keyword"
}
}

contact.opted_in​

A contact opted back in.

data fields

FieldTypeRequiredDescription
contactobjectYes
contact.idstringYesContact id (ctc_...).
contact.phonestringYesPhone number in E.164 format.
contact.emailstring or nullYes
contact.first_namestring or nullYes
contact.last_namestring or nullYes
contact.companystring or nullYes
contact.fieldsobjectYes
contact.labelsstring[]Yes
contact.consent_statusenumYesOne of: unknown, opted_in, opted_out.
contact.consent_sourcestring or nullYes
contact.consent_atstring (date-time) or nullYesISO 8601 timestamp.
contact.imessage_capableboolean or nullYes
contact.capability_checked_atstring (date-time) or nullYesISO 8601 timestamp.
contact.assigned_line_idstring or nullYesLine id (ln_...).
contact.owner_user_idstring (uuid) or nullYes
contact.last_inbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.last_outbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.first_reply_atstring (date-time) or nullYesISO 8601 timestamp.
contact.outreach_classenumYescold for a contact from an outbound tool; standard otherwise. One of: standard, cold.
contact.test_contactbooleanYesMarked for live workflow tests: a live test only texts test contacts.
contact.created_atstring (date-time)YesISO 8601 timestamp.
contact.updated_atstring (date-time)YesISO 8601 timestamp.
keywordstring or nullYes
sourcestringYes

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "contact.opted_in",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"contact": {
"id": "ctc_TestContact000001",
"phone": "+12125550100",
"email": null,
"first_name": "Test",
"last_name": "Contact",
"company": null,
"fields": {},
"labels": [],
"consent_status": "opted_in",
"consent_source": "test_event",
"consent_at": "2026-01-01T12:00:00.000Z",
"imessage_capable": true,
"capability_checked_at": "2026-01-01T12:00:00.000Z",
"assigned_line_id": "ln_TestLine0000000001",
"owner_user_id": null,
"last_inbound_at": "2026-01-01T12:00:00.000Z",
"last_outbound_at": null,
"first_reply_at": "2026-01-01T12:00:00.000Z",
"outreach_class": "standard",
"test_contact": false,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
},
"keyword": "START",
"source": "inbound_keyword"
}
}

conversation.assigned​

A conversation was assigned to a person or team, or unassigned.

data fields

FieldTypeRequiredDescription
conversationobjectYes
conversation.idstringYesConversation id (cnv_...).
conversation.contactobjectYes
conversation.contact.idstringYesContact id (ctc_...).
conversation.contact.phonestringYesPhone number in E.164 format.
conversation.contact.first_namestring or nullYes
conversation.contact.last_namestring or nullYes
conversation.contact.companystring or nullYes
conversation.line_idstring or nullYesLine id (ln_...).
conversation.statusenumYesOne of: open, snoozed, closed.
conversation.assignee_user_idstring (uuid) or nullYes
conversation.team_idstring (uuid) or nullYes
conversation.labelsstring[]Yes
conversation.last_message_atstring (date-time) or nullYesISO 8601 timestamp.
conversation.last_messageobject or nullYes
conversation.last_message.idstringYesMessage id (msg_...).
conversation.last_message.directionenumYesOne of: outbound, inbound.
conversation.last_message.previewstringYesUp to 200 characters.
conversation.unread_countintegerYesFrom 0 to 9007199254740991.
conversation.handled_bystringYes
conversation.snoozed_untilstring (date-time) or nullYesISO 8601 timestamp.
conversation.created_atstring (date-time)YesISO 8601 timestamp.
conversation.updated_atstring (date-time)YesISO 8601 timestamp.
assignee_user_idstring (uuid) or nullYes
previous_assignee_user_idstring (uuid) or nullYes
team_idstring (uuid) or nullYes

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "conversation.assigned",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"conversation": {
"id": "cnv_TestConvo00000001",
"contact": {
"id": "ctc_TestContact000001",
"phone": "+12125550100",
"first_name": "Test",
"last_name": "Contact",
"company": null
},
"line_id": "ln_TestLine0000000001",
"status": "open",
"assignee_user_id": "00000000-0000-4000-8000-000000000001",
"team_id": null,
"labels": [],
"last_message_at": "2026-01-01T12:00:00.000Z",
"last_message": {
"id": "msg_TestMessage000001",
"direction": "inbound",
"preview": "This is a test event from bubbl3."
},
"unread_count": 1,
"handled_by": "human",
"snoozed_until": null,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
},
"assignee_user_id": "00000000-0000-4000-8000-000000000001",
"previous_assignee_user_id": null,
"team_id": null
}
}

line.state_changed​

A line moved between states, such as warming, active, paused or retired.

data fields

FieldTypeRequiredDescription
lineobjectYes
line.idstringYesLine id (ln_...).
line.phonestringYesPhone number in E.164 format.
line.display_namestring or nullYes
line.contact_cardobjectYes
line.contact_card.namestringNoUp to 100 characters.
line.contact_card.image_urlstringNoUp to 2048 characters.
line.pool_idstring or nullYesPool id (pool_...).
line.stateenumYesOne of: provisioning, ready, warming, active, throttled, paused, quarantined, retired.
line.state_reasonstring or nullYes
line.state_changed_atstring (date-time) or nullYesISO 8601 timestamp.
line.health_scoreintegerYesFrom 0 to 100.
line.warmup_dayintegerYesFrom 0 to 9007199254740991.
line.daily_new_capintegerYesFrom 0 to 9007199254740991.
line.is_sandboxbooleanYes
line.usage_todayobjectYes
line.usage_today.daystring (date)YesUsage day in America/New_York, rolling over at 3 AM.
line.usage_today.new_contactsintegerYesFrom 0 to 9007199254740991.
line.usage_today.new_contacts_capintegerYesFrom 0 to 9007199254740991.
line.usage_today.sendsintegerYesFrom 0 to 9007199254740991.
line.usage_today.sends_capintegerYesFrom 0 to 9007199254740991.
line.created_atstring (date-time)YesISO 8601 timestamp.
fromenumYesOne of: provisioning, ready, warming, active, throttled, paused, quarantined, retired.
toenumYesOne of: provisioning, ready, warming, active, throttled, paused, quarantined, retired.
reasonstringYes

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "line.state_changed",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"line": {
"id": "ln_TestLine0000000001",
"phone": "+12125550101",
"display_name": "Test line",
"contact_card": {},
"pool_id": "pool_TestPool00000001",
"state": "active",
"state_reason": null,
"state_changed_at": "2026-01-01T12:00:00.000Z",
"health_score": 92,
"warmup_day": 21,
"daily_new_cap": 50,
"is_sandbox": false,
"usage_today": {
"day": "2026-01-01",
"new_contacts": 0,
"new_contacts_cap": 50,
"sends": 0,
"sends_cap": 300
},
"created_at": "2026-01-01T12:00:00.000Z"
},
"from": "active",
"to": "throttled",
"reason": "low_reply_ratio"
}
}

line.degraded​

A line’s health fell below the healthy range; bubbl3 sends less through it.

data fields

FieldTypeRequiredDescription
lineobjectYes
line.idstringYesLine id (ln_...).
line.phonestringYesPhone number in E.164 format.
line.display_namestring or nullYes
line.contact_cardobjectYes
line.contact_card.namestringNoUp to 100 characters.
line.contact_card.image_urlstringNoUp to 2048 characters.
line.pool_idstring or nullYesPool id (pool_...).
line.stateenumYesOne of: provisioning, ready, warming, active, throttled, paused, quarantined, retired.
line.state_reasonstring or nullYes
line.state_changed_atstring (date-time) or nullYesISO 8601 timestamp.
line.health_scoreintegerYesFrom 0 to 100.
line.warmup_dayintegerYesFrom 0 to 9007199254740991.
line.daily_new_capintegerYesFrom 0 to 9007199254740991.
line.is_sandboxbooleanYes
line.usage_todayobjectYes
line.usage_today.daystring (date)YesUsage day in America/New_York, rolling over at 3 AM.
line.usage_today.new_contactsintegerYesFrom 0 to 9007199254740991.
line.usage_today.new_contacts_capintegerYesFrom 0 to 9007199254740991.
line.usage_today.sendsintegerYesFrom 0 to 9007199254740991.
line.usage_today.sends_capintegerYesFrom 0 to 9007199254740991.
line.created_atstring (date-time)YesISO 8601 timestamp.
reasonstringYes
health_scoreintegerYesFrom 0 to 100.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "line.degraded",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"line": {
"id": "ln_TestLine0000000001",
"phone": "+12125550101",
"display_name": "Test line",
"contact_card": {},
"pool_id": "pool_TestPool00000001",
"state": "active",
"state_reason": null,
"state_changed_at": "2026-01-01T12:00:00.000Z",
"health_score": 55,
"warmup_day": 21,
"daily_new_cap": 50,
"is_sandbox": false,
"usage_today": {
"day": "2026-01-01",
"new_contacts": 0,
"new_contacts_cap": 50,
"sends": 0,
"sends_cap": 300
},
"created_at": "2026-01-01T12:00:00.000Z"
},
"reason": "health_score",
"health_score": 55
}
}

line.recovered​

A degraded line is healthy again.

data fields

FieldTypeRequiredDescription
lineobjectYes
line.idstringYesLine id (ln_...).
line.phonestringYesPhone number in E.164 format.
line.display_namestring or nullYes
line.contact_cardobjectYes
line.contact_card.namestringNoUp to 100 characters.
line.contact_card.image_urlstringNoUp to 2048 characters.
line.pool_idstring or nullYesPool id (pool_...).
line.stateenumYesOne of: provisioning, ready, warming, active, throttled, paused, quarantined, retired.
line.state_reasonstring or nullYes
line.state_changed_atstring (date-time) or nullYesISO 8601 timestamp.
line.health_scoreintegerYesFrom 0 to 100.
line.warmup_dayintegerYesFrom 0 to 9007199254740991.
line.daily_new_capintegerYesFrom 0 to 9007199254740991.
line.is_sandboxbooleanYes
line.usage_todayobjectYes
line.usage_today.daystring (date)YesUsage day in America/New_York, rolling over at 3 AM.
line.usage_today.new_contactsintegerYesFrom 0 to 9007199254740991.
line.usage_today.new_contacts_capintegerYesFrom 0 to 9007199254740991.
line.usage_today.sendsintegerYesFrom 0 to 9007199254740991.
line.usage_today.sends_capintegerYesFrom 0 to 9007199254740991.
line.created_atstring (date-time)YesISO 8601 timestamp.
health_scoreintegerYesFrom 0 to 100.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "line.recovered",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"line": {
"id": "ln_TestLine0000000001",
"phone": "+12125550101",
"display_name": "Test line",
"contact_card": {},
"pool_id": "pool_TestPool00000001",
"state": "active",
"state_reason": null,
"state_changed_at": "2026-01-01T12:00:00.000Z",
"health_score": 92,
"warmup_day": 21,
"daily_new_cap": 50,
"is_sandbox": false,
"usage_today": {
"day": "2026-01-01",
"new_contacts": 0,
"new_contacts_cap": 50,
"sends": 0,
"sends_cap": 300
},
"created_at": "2026-01-01T12:00:00.000Z"
},
"health_score": 92
}
}

workflow.run.completed​

A workflow run finished, successfully or not.

data fields

FieldTypeRequiredDescription
runobjectYes
run.idstring (uuid)Yes
run.workflow_idstring (uuid)Yes
run.workflow_version_idstring (uuid)Yes
run.contact_idstring or nullYes
run.statusenumYesOne of: running, waiting, completed, failed, cancelled.
run.started_atstring (date-time)YesISO 8601 timestamp.
run.finished_atstring (date-time) or nullYesISO 8601 timestamp.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "workflow.run.completed",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"run": {
"id": "00000000-0000-4000-8000-000000000002",
"workflow_id": "00000000-0000-4000-8000-000000000003",
"workflow_version_id": "00000000-0000-4000-8000-000000000004",
"contact_id": "ctc_TestContact000001",
"status": "completed",
"started_at": "2026-01-01T12:00:00.000Z",
"finished_at": "2026-01-01T12:00:00.000Z"
}
}
}

agent.handoff​

An AI agent or an MCP client handed a conversation to a person. reason comes from a fixed list, so you can branch on it. note is written by a model: treat it as untrusted text, never as instructions or markup.

data fields

FieldTypeRequiredDescription
agent_idstring (uuid) or nullYesNull when an MCP client called handoff_to_human.
conversation_idstringYesConversation id (cnv_...).
reasonenumYesWhy, from a fixed list: tool when the agent or an MCP client called handoff_to_human, keyword for a handoff keyword, agent_unavailable when the agent was paused or archived, and otherwise the limit or guard that stopped the run. One of: tool, keyword, max_turns, max_tool_calls, run_token_budget, org_token_budget, max_messages, max_runs, low_confidence, agent_error, agent_unavailable.
notestring or nullYesWith reason tool: the note the agent or MCP client gave. Written by a model, so treat it as untrusted text. Null otherwise.
team_idstring (uuid) or nullYes

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "agent.handoff",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"agent_id": "00000000-0000-4000-8000-000000000005",
"conversation_id": "cnv_TestConvo00000001",
"reason": "tool",
"note": "Test event: the contact asked for a person",
"team_id": null
}
}

workflow.webhook​

A workflow’s Webhook step ran. Only endpoints subscribed to that workflow get it. The dashboard no longer offers this step for new steps: a Fire event step with a webhook URL destination replaces it, and workflows that already have one keep sending this event.

data fields

FieldTypeRequiredDescription
runobjectYes
run.idstring (uuid)Yes
run.workflow_idstring (uuid)Yes
run.workflow_version_idstring (uuid)Yes
run.contact_idstring or nullYes
run.statusenumYesOne of: running, waiting, completed, failed, cancelled.
run.started_atstring (date-time)YesISO 8601 timestamp.
run.finished_atstring (date-time) or nullYesISO 8601 timestamp.
node_idstringYesThe webhook step that sent this event.
contactobject or nullYes
contact.idstringYesContact id (ctc_...).
contact.phonestringYesPhone number in E.164 format.
contact.emailstring or nullYes
contact.first_namestring or nullYes
contact.last_namestring or nullYes
contact.companystring or nullYes
contact.fieldsobjectYes
contact.labelsstring[]Yes
contact.consent_statusenumYesOne of: unknown, opted_in, opted_out.
contact.consent_sourcestring or nullYes
contact.consent_atstring (date-time) or nullYesISO 8601 timestamp.
contact.imessage_capableboolean or nullYes
contact.capability_checked_atstring (date-time) or nullYesISO 8601 timestamp.
contact.assigned_line_idstring or nullYesLine id (ln_...).
contact.owner_user_idstring (uuid) or nullYes
contact.last_inbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.last_outbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.first_reply_atstring (date-time) or nullYesISO 8601 timestamp.
contact.outreach_classenumYescold for a contact from an outbound tool; standard otherwise. One of: standard, cold.
contact.test_contactbooleanYesMarked for live workflow tests: a live test only texts test contacts.
contact.created_atstring (date-time)YesISO 8601 timestamp.
contact.updated_atstring (date-time)YesISO 8601 timestamp.
dataobjectYesValues set on the step.
testtrueNoPresent when the run is a workflow's live test. Absent on real runs.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "workflow.webhook",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"run": {
"id": "00000000-0000-4000-8000-000000000002",
"workflow_id": "00000000-0000-4000-8000-000000000003",
"workflow_version_id": "00000000-0000-4000-8000-000000000004",
"contact_id": "ctc_TestContact000001",
"status": "running",
"started_at": "2026-01-01T12:00:00.000Z",
"finished_at": null
},
"node_id": "notify",
"contact": {
"id": "ctc_TestContact000001",
"phone": "+12125550100",
"email": null,
"first_name": "Test",
"last_name": "Contact",
"company": null,
"fields": {},
"labels": [],
"consent_status": "opted_in",
"consent_source": "test_event",
"consent_at": "2026-01-01T12:00:00.000Z",
"imessage_capable": true,
"capability_checked_at": "2026-01-01T12:00:00.000Z",
"assigned_line_id": "ln_TestLine0000000001",
"owner_user_id": null,
"last_inbound_at": "2026-01-01T12:00:00.000Z",
"last_outbound_at": null,
"first_reply_at": "2026-01-01T12:00:00.000Z",
"outreach_class": "standard",
"test_contact": false,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
},
"data": {
"stage": "test"
}
}
}

lead.received​

An inbound source or a connected CRM created or merged a contact. Sent once per lead: a repeated post of the same lead sends nothing.

data fields

FieldTypeRequiredDescription
contactobjectYes
contact.idstringYesContact id (ctc_...).
contact.phonestringYesPhone number in E.164 format.
contact.emailstring or nullYes
contact.first_namestring or nullYes
contact.last_namestring or nullYes
contact.companystring or nullYes
contact.fieldsobjectYes
contact.labelsstring[]Yes
contact.consent_statusenumYesOne of: unknown, opted_in, opted_out.
contact.consent_sourcestring or nullYes
contact.consent_atstring (date-time) or nullYesISO 8601 timestamp.
contact.imessage_capableboolean or nullYes
contact.capability_checked_atstring (date-time) or nullYesISO 8601 timestamp.
contact.assigned_line_idstring or nullYesLine id (ln_...).
contact.owner_user_idstring (uuid) or nullYes
contact.last_inbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.last_outbound_atstring (date-time) or nullYesISO 8601 timestamp.
contact.first_reply_atstring (date-time) or nullYesISO 8601 timestamp.
contact.outreach_classenumYescold for a contact from an outbound tool; standard otherwise. One of: standard, cold.
contact.test_contactbooleanYesMarked for live workflow tests: a live test only texts test contacts.
contact.created_atstring (date-time)YesISO 8601 timestamp.
contact.updated_atstring (date-time)YesISO 8601 timestamp.
outcomeenumYesOne of: created, merged.
sourceobjectYes
source.kindenumYesOne of: inbound_source, integration.
source.idstringYesThe inbound source (src_...) or connection (int_...).
source.namestring or nullYes
source.providerstring or nullYesThe provider, for connections and Meta sources.
lead_event_idstring (uuid) or nullYesThe intake lead event, for inbound sources.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "lead.received",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"contact": {
"id": "ctc_TestContact000001",
"phone": "+12125550100",
"email": null,
"first_name": "Test",
"last_name": "Contact",
"company": null,
"fields": {},
"labels": [],
"consent_status": "opted_in",
"consent_source": "test_event",
"consent_at": "2026-01-01T12:00:00.000Z",
"imessage_capable": true,
"capability_checked_at": "2026-01-01T12:00:00.000Z",
"assigned_line_id": "ln_TestLine0000000001",
"owner_user_id": null,
"last_inbound_at": "2026-01-01T12:00:00.000Z",
"last_outbound_at": null,
"first_reply_at": "2026-01-01T12:00:00.000Z",
"outreach_class": "standard",
"test_contact": false,
"created_at": "2026-01-01T12:00:00.000Z",
"updated_at": "2026-01-01T12:00:00.000Z"
},
"outcome": "created",
"source": {
"kind": "inbound_source",
"id": "src_TestSource00000001",
"name": "Test form",
"provider": null
},
"lead_event_id": "00000000-0000-4000-8000-000000000006"
}
}

integration.connection.status​

An integration connection changed status: connected, needs_attention (with the reason) or disconnected.

data fields

FieldTypeRequiredDescription
connectionobjectYes
connection.idstringYesIntegration connection id (int_...).
connection.providerstringYes
connection.external_account_namestring or nullYes
statusenumYesOne of: connected, needs_attention, disconnected.
previous_statusenum or nullYesOne of: connected, needs_attention, disconnected.
reasonstring or nullYesWhat needs attention, when it does.

Example

{
"id": "evt_7Hc2LmQ9xT4bWz1R",
"type": "integration.connection.status",
"created_at": "2026-10-09T15:04:05.000Z",
"workspace_id": "ws_7Hc2LmQ9xT4bWz1R",
"data": {
"connection": {
"id": "int_TestConnection0001",
"provider": "hubspot",
"external_account_name": "Test CRM"
},
"status": "needs_attention",
"previous_status": "connected",
"reason": "Test event: the CRM refused bubbl3’s token"
}
}