---
title: "Event Reference"
source: https://sumgenius.ai/docs/webhook-events
generated: 2026-07-30
---

# 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+ plan

Messages

[message.received](#message-received)

[message.reel_shared](#message-received)

[message.post_shared](#message-received)

[comment.received](#comment-received)

Channel inbound

[sms.message_received](#channel-messages)

[whatsapp.message_received](#channel-messages)

[telegram.message_received](#channel-messages)

Leads

[lead.email_captured](#lead-captured)

[lead.phone_captured](#lead-captured)

Appointments

[appointment.booked](#appointments)

[appointment.cancelled](#appointments)

[appointment.rescheduled](#appointments)

Conversation

[conversation.escalated](#conversation-escalated)

Delivery

[message.send_failed](#send-failed)

### The 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](https://sumgenius.ai/docs/receiving-webhooks#verify). How much customer data is included is controlled by your [payload profile](https://sumgenius.ai/docs/receiving-webhooks#payload-profiles) (`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](#events).
- 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`.

**Standard envelope · message.received**

```
{
  "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"
    }
  }
}
```

**Legacy (flat) equivalent**

```
{
  "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.

**Attachment object**

```
{
  "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.

**message.received · full profile**

```
{
  "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.

**comment.received · 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](https://sumgenius.ai/docs/webhook-sms), [WhatsApp](https://sumgenius.ai/docs/webhook-whatsapp), or [Telegram](https://sumgenius.ai/docs/webhook-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.

**sms.message_received · full profile**

```
{
  "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"
  }
}
```

**Channel sub-blocks**

```
// 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.

**lead.email_captured**

```
{
  "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](https://sumgenius.ai/docs/appointment-booking). 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.

**appointment.booked**

```
{
  "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.

**conversation.escalated**

```
{
  "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](https://sumgenius.ai/docs/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.

**message.send_failed**

```
{
  "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"
  }
}
```
