Webhook Events
Every event ChatGenius can deliver to your endpoint: the shared envelope, the attachment shape, and all 14 event types with full-profile JSON examples.
Webhook & API Add-on · $29/month · Requires Creator+ planThe envelope
Every event is posted as JSON with Content-Type: application/json, in one of two styles. Standard, which every endpoint created since July 2026 receives, wraps the event body: {"type", "timestamp", "data"}, and you read the fields from data.*. Legacy, for integrations created before July 2026, posts the flat body with no wrapper. Everything inside is identical between the two; the style is fixed per endpoint and shown on the endpoint in the portal.
The event type also rides in the X-SumGenius-Event request header on every delivery, and every delivery is signed: see verifying signatures. How much customer data is included is controlled by your payload profile (full or minimal).
Event body fields
-
event_idstring
32-character hex id, unique per event and stable across retries; it is also the delivery id header. Use it to deduplicate.
-
event_typestring
One of the 14 types in the index.
-
occurred_atUTC ISO-8601
When the event happened on the platform.
-
source.platformenum
facebook · instagram · sms · whatsapp · telegram
-
source.channelenum
meta_dm · twilio_sms · whatsapp_cloud · telegram
-
conversation.idinteger | null
ChatGenius conversation id; pass it to the Send API to reply. Can be null on the very first inbound message from a new person.
-
conversation.assigned_to / assigned_to_namefull profile only
Team member id and name the conversation is assigned to, or null.
-
customer.platform_user_idstring
Platform-assigned user id. Always present, on both profiles: your stable join key.
-
customer enrichmentfull profile only
platform_user_name, platform_user_username, contact_id, tags, stage, custom_fields. custom_fields is a reserved placeholder that is always {}.
-
messageobject
{id, text_raw, attachments[]}. Non-message events replace this block with their own (comment, appointment, lead, escalation, send_failed).
-
metaobject
version (currently v1), payload_profile, generated_at.
{
"type": "message.received",
"timestamp": "2026-02-20T08:15:03Z",
"data": {
"event_id": "a3f8e1c2d4b567890abcdef012345678",
"event_type": "message.received",
"occurred_at": "2026-02-20T08:15:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": 10482,
"assigned_to": 90011,
"assigned_to_name": "Anna"
},
"customer": {
"platform_user_id": "17841400123456789",
"platform_user_name": "Jane Doe",
"platform_user_username": "janedoe",
"contact_id": 555,
"tags": ["VIP", "Returning"],
"stage": "qualified",
"custom_fields": {}
},
"message": {
"id": "mid.1234567890abcdef",
"text_raw": "Hi, do you have availability next Tuesday?",
"attachments": []
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-02-20T08:15:00Z"
}
}
}
{
"event_id": "a3f8e1c2d4b567890abcdef012345678",
"event_type": "message.received",
"occurred_at": "2026-02-20T08:15:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
...
}
Attachments
Media the customer sent arrives in message.attachments. Four fields are always present; the rest ride on the full profile only.
-
typestring
Attachment category, e.g. image · audio · video.
-
media_typestring
MIME type when available.
-
media_idstring
Platform media identifier.
-
received_atUTC ISO-8601
When the attachment was received.
-
urlfull profile only
Media URL. Time-limited, provided by the platform; download promptly if you need to keep it.
-
title / captionfull profile only
Title or caption text when present.
{
"type": "image",
"media_type": "image/jpeg",
"media_id": "18025531234567890",
"received_at": "2026-02-20T08:15:00Z",
// full profile only:
"url": "https://lookaside.fbsbx.com/ig_messaging_cdn/...",
"title": null,
"caption": null
}
Message events
Inbound DMs and comments on Facebook and Instagram.
message.received
A customer sent a direct message on Facebook or Instagram (source.platform is facebook or instagram). Fires on text and on attachments.
Share variants
message.reel_shared and message.post_shared fire in addition to message.received for the same message when an Instagram reel or post is shared into the thread, carrying the same message.id. Subscribe to them when you want to react to shares specifically; deduplicate against message.id if you consume both.
{
"event_id": "a3f8e1c2d4b567890abcdef012345678",
"event_type": "message.received",
"occurred_at": "2026-02-20T08:15:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": 10482,
"assigned_to": 90011,
"assigned_to_name": "Anna"
},
"customer": {
"platform_user_id": "17841400123456789",
"platform_user_name": "Jane Doe",
"platform_user_username": "janedoe",
"contact_id": 555,
"tags": ["VIP", "Returning"],
"stage": "qualified",
"custom_fields": {}
},
"message": {
"id": "mid.1234567890abcdef",
"text_raw": "Hi, do you have availability next Tuesday?",
"attachments": []
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-02-20T08:15:00Z"
}
}
comment.received
A customer left a comment on one of your Instagram or Facebook posts, reels, or live videos. Fires for every inbound comment, whether or not a ChatGenius comment trigger is configured.
There is no message block; the payload carries source_post and comment instead. conversation.id is null for comments: the follow-up DM, if a trigger sends one, arrives as its own message.received event.
-
source_postobject
id is always present: the attribution join key. url is the permalink, populated when the comment matched a trigger targeting a specific post, otherwise null.
-
commentobject
{id, text_raw, matched_keyword, trigger_id, trigger_name, source_type}. source_type is comment · live. The trigger fields (matched_keyword, trigger_id, trigger_name) are null unless a comment trigger matched.
{
"event_id": "b7c2e1a4f9d063518adef0129876dcba",
"event_type": "comment.received",
"occurred_at": "2026-02-20T08:15:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": null
},
"customer": {
"platform_user_id": "17841400123456789",
"platform_user_name": null,
"platform_user_username": "janedoe"
},
"source_post": {
"id": "17912345678901234",
"url": "https://www.instagram.com/p/CtExAmple/"
},
"comment": {
"id": "17987654321098765",
"text_raw": "PRICE",
"matched_keyword": "price",
"trigger_id": 42,
"trigger_name": "Pricing DM",
"source_type": "comment"
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-02-20T08:15:00Z"
}
}
sms · whatsapp · telegram .message_received
A customer messaged you on SMS, WhatsApp, or Telegram. Same envelope as every other event, plus one channel sub-block under message. The channel pages cover routing identifiers, supported actions, and limits.
-
message.smsobject
{to_phone, from_phone, num_segments, num_media, encoding}. encoding is GSM-7 · UCS-2.
-
message.whatsappobject
{wa_id, phone_number, business_phone_number_id, profile_name, message_type, context_message_id}.
-
message.telegramobject
{chat_id, chat_type, username, message_type}. chat_id is the recipient identifier for the Send API.
{
"event_id": "7f7a80c84bf17c4b5b72974c97365a77",
"event_type": "sms.message_received",
"occurred_at": "2026-04-27T23:22:10Z",
"source": {
"platform": "sms",
"channel": "twilio_sms"
},
"conversation": {
"id": 119301
},
"customer": {
"platform_user_id": "+15551234567",
"phone": "+15551234567"
},
"message": {
"id": "SMdb4342728f73bca6a83770a705e27b31",
"text_raw": "Hi, what time do you open Saturday?",
"attachments": [],
"sms": {
"to_phone": "+15559876543",
"from_phone": "+15551234567",
"num_segments": 1,
"num_media": 0,
"encoding": "GSM-7"
}
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-04-27T23:22:10Z"
}
}
// sms.message_received
"sms": {
"to_phone": "+15559876543",
"from_phone": "+15551234567",
"num_segments": 1,
"num_media": 0,
"encoding": "GSM-7"
}
// whatsapp.message_received
"whatsapp": {
"wa_id": "15551234567",
"phone_number": "+15551234567",
"business_phone_number_id": "108765432109876",
"profile_name": "Jane Doe",
"message_type": "text",
"context_message_id": null
}
// telegram.message_received
"telegram": {
"chat_id": "820032950394075",
"chat_type": "private",
"username": "janedoe",
"message_type": "text"
}
Lead events
Contact info captured from conversation content, ready for your CRM.
lead.email_captured · lead.phone_captured
Fires once per conversation, the first time an email (or phone) is captured from the conversation content, on any channel. A conversation that captures an email and later a phone produces one of each; the two events are identical except for event_type.
The lead block {email, phone, captured_at} is always included regardless of payload profile: contact info is the point of the event. Fields not yet captured are null.
lead.phone is a phone collected from message content, not the SMS or WhatsApp sender number; on those channels the sender number is already delivered as customer.platform_user_id on every event.
{
"event_id": "c7a3b5d1e9f2468013579bdf2468ace0",
"event_type": "lead.email_captured",
"occurred_at": "2026-07-23T07:20:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": 119789
},
"customer": {
"platform_user_id": "992584566622479"
},
"lead": {
"email": "jane@example.com",
"phone": "+15551234567",
"captured_at": "2026-07-23T07:20:00Z"
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-07-23T07:20:00Z"
}
}
Appointment events
In-chat bookings made through the AI booking agent. Calendar syncs do not fire these.
appointment.booked · .cancelled · .rescheduled
All three carry the same appointment block; the event_type and the appointment's status tell you what happened. appointment.rescheduled fires again on each subsequent reschedule, with the new datetime. The block is always full regardless of payload profile.
-
appointmentobject
{id, confirmation_code, datetime, service_type, duration_minutes, cost, status, timezone, location {city, state}, customer {name, email, phone}}.
-
datetimeUTC ISO-8601
Always UTC. timezone is the appointment's own booking timezone; convert back to display the time the customer chose.
-
appointment.customerobject
The contact details captured during booking. The top-level customer remains the messaging identity.
{
"event_id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
"event_type": "appointment.booked",
"occurred_at": "2026-07-24T06:30:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": 187765
},
"customer": {
"platform_user_id": "820032950394075",
"platform_user_name": "Jared"
},
"appointment": {
"id": 1234,
"confirmation_code": "AB12CD",
"datetime": "2026-07-30T15:00:00Z",
"service_type": "consultation",
"duration_minutes": 30,
"cost": null,
"status": "confirmed",
"timezone": "America/New_York",
"location": {
"city": "Austin",
"state": "TX"
},
"customer": {
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+15551234567"
}
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-07-24T06:30:00Z"
}
}
Conversation events
Human handoff.
conversation.escalated
Fires every time a conversation is handed to a human, triggered by negative sentiment or an explicit hand-off, including repeat escalations on the same conversation. Wire it to Slack or a ticketing system, and match it to a thread via conversation.id.
There is no de-escalation event: the AI stays paused on an escalated conversation by design.
-
escalationobject
{reason, assigned_to, escalated_at}. assigned_to is the agent the conversation was routed to, or null if no agent was available.
{
"event_id": "9d4f7a3b1c2e5d6f8091a2b3c4d5e6f7",
"event_type": "conversation.escalated",
"occurred_at": "2026-07-24T05:40:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": 187765
},
"customer": {
"platform_user_id": "820032950394075",
"platform_user_name": "Jared",
"platform_user_username": "jaredr.media"
},
"escalation": {
"reason": "Negative sentiment detected",
"assigned_to": "Alex",
"escalated_at": "2026-07-24T05:40:00Z"
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-07-24T05:40:00Z"
}
}
Delivery events
Outbound sends that permanently failed.
message.send_failed
Fires when an outbound Send API request permanently fails: the platform returned a non-retryable error (most commonly an expired Meta messaging window) or retries were exhausted. Your system believed the send succeeded; this event lets it reconcile. Match it back to your original request via send_failed.idempotency_key.
-
send_failedobject
{action, idempotency_key, request_id, error_code, error_message, http_code, attempts, text_attempted}.
-
text_attemptedfull profile only
The message text you tried to send. null on the minimal profile.
{
"event_id": "b2f1e0d9c8b7a695847362514f0e1d2c",
"event_type": "message.send_failed",
"occurred_at": "2026-07-23T06:40:00Z",
"source": {
"platform": "instagram",
"channel": "meta_dm"
},
"conversation": {
"id": 42
},
"customer": {
"platform_user_id": "178414"
},
"send_failed": {
"action": "send_message",
"idempotency_key": "your-key-abc-123",
"request_id": "req-xyz-789",
"error_code": "10",
"error_message": "Meta 7-day window expired.",
"http_code": 400,
"attempts": 1,
"text_attempted": "Hi, following up on your order"
},
"meta": {
"version": "v1",
"payload_profile": "full",
"generated_at": "2026-07-23T06:40:00Z"
}
}