---
title: "Message received event"
method: POST
path: "message.received"
tags: ["Webhook Events"]
---

# Message received event

`POST message.received` (webhook)

Fired when a new inbox message is received.

## Payload

- WebhookPayloadMessage — Webhook payload for message received events
  - `id` string, required — Stable webhook event ID
  - `event` 'message.received', required
  - `message` object, required
    - `id` string, required — Internal message ID
    - `conversationId` string, required — Internal conversation ID
    - `platform` 'instagram' | 'facebook' | 'telegram' | 'whatsapp', required
    - `platformMessageId` string, required — Platform's message ID
    - `direction` 'incoming' | 'outgoing', required
    - `text` string, nullable, required — Message text content
    - `attachments` object[], required
      - `type` string, required — Attachment type (image, video, file, sticker, audio)
      - `url` string, required — Where to fetch the attachment. **The contract differs by platform.** - **WhatsApp**: points at `GET /v1/whatsapp/media/{mediaId}`, an authenticated Zernio endpoint. You MUST send `Authorization: Bearer <your API key>`; fetching it without that header returns `401`. Download and store the bytes when this webhook arrives: Meta drops inbound media after a limited retention window, after which the endpoint answers `400` permanently and the media is unrecoverable. - **Instagram / Facebook / Telegram**: a direct platform CDN link that needs no authentication and expires on the platform's own schedule.
      - `payload` object — Additional attachment metadata
    - `sender` object, required
      - `id` string, required — Sender's platform identifier. For WhatsApp this is the phone number (without leading `+`) when available, otherwise the `businessScopedUserId`.
      - `contactId` string — Zernio CRM Contact id for this sender, when one exists (omitted for outgoing/business sender).
      - `name` string
      - `username` string
      - `picture` string
      - `phoneNumber` string, nullable — WhatsApp only. Sender's phone number in E.164 format (with leading `+`). **Nullable during the BSUID rollout (April 2026+).** WhatsApp users who adopt a username can message businesses without exposing a phone number — this field is omitted for them. Match by `businessScopedUserId` instead. See `docs/whatsapp-bsuid-migration.md`.
      - `businessScopedUserId` string — WhatsApp only. Business-scoped user ID (BSUID) — Meta's canonical identifier for a WhatsApp user within your business. Present when Meta includes it in the inbound payload (rollout in progress since early April 2026). **Recommended primary identity anchor** going forward; fall back to `phoneNumber` only when this field is absent.
      - `parentBusinessScopedUserId` string — WhatsApp only. Parent BSUID for businesses with linked business portfolios. Omitted for standalone portfolios.
      - `whatsappUsername` string — WhatsApp only. User's WhatsApp username (e.g. `@jane`). Not a stable identifier — users can change it. Useful for display, not recommended as an identity anchor.
      - `instagramProfile` object — Instagram profile data for the sender. Only present for Instagram conversations.
        - `isFollower` boolean, nullable — Whether the sender follows your Instagram business account
        - `isFollowing` boolean, nullable — Whether your Instagram business account follows the sender
        - `followerCount` integer, nullable — The sender's follower count on Instagram
        - `isVerified` boolean, nullable — Whether the sender is a verified Instagram user
    - `sentAt` string, date-time, required
    - `isRead` boolean, required
  - `conversation` InboxWebhookConversation, required — The conversation context included in inbox webhook payloads.
    - `id` string, required
    - `platformConversationId` string, required
    - `participantId` string
    - `participantName` string
    - `participantUsername` string
    - `participantPicture` string
    - `status` 'active' | 'archived', required
    - `contactId` string — Zernio CRM Contact ID for the participant, when one exists. Resolved by joining `participantId` to the ContactChannel collection. Best-effort: omitted when no channel matches or `participantId` is absent. Lets integrators join any inbox webhook back to the CRM Contact without needing to look at the sender — which matters for outgoing and delivery-status events whose sender is the business.
  - `account` InboxWebhookAccount, required — The account context included in inbox webhook payloads.
    - `id` string, required — Social account ID
    - `accountId` string — Social account ID (same value as id). Canonical field so consumers can filter every webhook event on one field (e.g. route staging vs production by account). id is kept for backward compatibility.
    - `profileId` string — Zernio profile (workspace) ID this account belongs to. Use it to route or filter inbox webhooks by workspace. This is the profile ID only, not its name (resolve the name via the API with this ID). Optional; omitted on the shared WhatsApp sandbox account and when the account has no resolvable profile.
    - `platform` string, required
    - `username` string, required
    - `displayName` string
  - `metadata` object, nullable — Platform-specific message context (present when the message is a quick reply tap, postback button tap, inline keyboard callback, or a quote-reply to an earlier message)
    - `quotedMessageId` string — platformMessageId of the message this one is a quote-reply to. WhatsApp (`context.id`), Instagram and Facebook Messenger (`reply_to.mid`). On `message.sent` echoes (operator replied from the native app) this is the only metadata field populated.
    - `quickReplyPayload` string — Payload from a quick reply tap (Facebook/Instagram Messenger).
    - `postbackPayload` string — Payload from a postback button tap (Facebook/Instagram Messenger).
    - `postbackTitle` string — Title of the tapped postback button (Facebook/Instagram Messenger).
    - `callbackData` string — Callback data from an inline keyboard button tap (Telegram).
    - `interactiveType` 'button_reply' | 'list_reply' | 'nfm_reply' — WhatsApp only. Which kind of interactive reply the user sent: `button_reply` (tap on an interactive button), `list_reply` (tap on a list row), or `nfm_reply` (a WhatsApp Flow submission).
    - `interactiveId` string — WhatsApp only. The `id` of the tapped button or list row, matching the `id` you supplied when the message was sent. Not set for Flow responses.
    - `buttonPayload` string — WhatsApp only. Payload attached to a tapped template button. Template buttons emit a plain `button` webhook (not an interactive reply), so `interactiveType` is empty while this field is populated.
    - `flowResponseJson` string — WhatsApp only. Raw `nfm_reply.response_json` string returned by a Flow submission. Useful if you need the exact wire payload; for typed access use `flowResponseData` instead.
    - `flowResponseData` object — WhatsApp only. Parsed Flow response JSON. Populated when `flowResponseJson` is valid JSON; otherwise omitted. Keys and value types depend on the specific Flow that was submitted.
    - `order` object — WhatsApp only. Cart submitted by the user from a commerce message (catalog, product, or product-list message). Meta's `order` object forwarded verbatim.
      - `catalog_id` string — Meta catalog the ordered products belong to.
      - `text` string — Optional free-text note the user attached to the cart.
      - `product_items` object[]
        - `product_retailer_id` string — Retailer ID (SKU) of the ordered product.
        - `quantity` integer — Quantity ordered for this line item.
        - `item_price` number — Unit price of the item.
        - `currency` string — ISO 4217 currency code (e.g. USD).
    - `referredProduct` object — WhatsApp only. The product the user is asking about. Set when an inbound text carries Meta's `context.referred_product` (the user tapped "Message business" on a product). Forwarded verbatim.
      - `catalog_id` string — Meta catalog the product belongs to.
      - `product_retailer_id` string — Retailer ID (SKU) of the product being asked about.
    - `contacts` object[] — WhatsApp only. Contact cards the user shared, forwarded verbatim from Meta. Read `contactsOrigin` before treating any number here as the sender's own.
    - `contactsOrigin` 'contact_request' | 'other' — WhatsApp only. How the contact card was shared. `contact_request` means the user tapped a `request_contact_info` button, so the number is their own and consented. `other` means they picked a card from their address book: it may be anyone's, and must NOT be stored as the sender's identity. Omitted when Meta sends no origin.
    - `storyReply` object — Instagram only. Populated when an IG user replies to one of the account's stories (Meta `messaging_story_replies`). Mutually exclusive in practice with `isStoryMention`.
      - `storyId` string, required — The Instagram story ID the user replied to.
      - `storyUrl` string — Meta CDN URL for the story media. Expires approximately 24 hours after the story posted; consumers must fetch promptly or treat 404s as expected.
    - `isStoryMention` boolean — Instagram only. True when the message was generated by an IG user mentioning the account in their own story (`story_mention` attachment type). Mutually exclusive in practice with `storyReply`.
    - `referral` object, nullable — Ad-click attribution forwarded verbatim from Meta. Populated only on the FIRST inbound message after the click; absent on subsequent messages of the same conversation. The populated subset identifies the source platform: - `ctwa_clid` and `source_*` fields: WhatsApp CTWA (Click-to-WhatsApp). Attribution window is 7 days from click. Forward to Meta Conversions API for Business Messaging replay. - `ad_id` and `ads_context_data`: Facebook Messenger CTM (Click-to-Message) or Instagram CTD (Click-to-Direct). Use `ad_id` to attribute the conversation to a specific ad.
      - `ctwa_clid` string — Meta's GCLID-equivalent click identifier.
      - `source_id` string
      - `source_type` string
      - `source_url` string
      - `headline` string
      - `body` string
      - `media_type` string
      - `image_url` string
      - `video_url` string
      - `thumbnail_url` string
      - `ad_id` string — Facebook Messenger CTM / Instagram CTD only. The Meta ad ID the user clicked to start the conversation.
      - `ref` string — Optional `ref` parameter passed through from the Meta ad creative. Facebook Messenger CTM / Instagram CTD only.
      - `source` string — Meta-supplied source identifier (e.g. `ADS`). Facebook Messenger CTM / Instagram CTD only.
      - `type` string — Meta-supplied referral type (e.g. `OPEN_THREAD`). Facebook Messenger CTM / Instagram CTD only.
      - `ads_context_data` object — Snapshot of the ad's public context at click time. Facebook Messenger CTM / Instagram CTD only.
        - `ad_title` string
        - `photo_url` string
        - `video_url` string
        - `post_id` string
        - `product_id` string
        - `flow_id` string
  - `timestamp` string, date-time, required

## Acknowledgement `200`

Webhook received successfully

---

[API](https://skmtc.net/zernio/apis/zernio-api.md) · [All operations](https://skmtc.net/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zernio/zernio-api/versions/51932b099b2f/schema)
