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

# Message played event

`POST message.played` (webhook)

Fires the first time the recipient plays a voice message you sent on WhatsApp.

## Payload

- WebhookPayloadMessageDeliveryStatus — Shared payload for message.delivered, message.read, message.played and message.failed events. Fires when the platform reports a new delivery state for an outgoing message. Platform support: * message.delivered: WhatsApp, Facebook Messenger, SMS, RCS. * message.read: WhatsApp, Facebook Messenger, Instagram, RCS. Not SMS (carriers report delivery, never read). * message.played: WhatsApp only, voice messages. * message.failed: WhatsApp, SMS and RCS (other platforms don't expose per-message failure via webhook). On SMS, `error.code` is the carrier's numeric code and `error.message` its reason.
  - `id` string, required — Stable webhook event ID: the dedupe key, also sent as the X-Zernio-Event-Id header and identical on every retry and redelivery. It identifies the event only, never an account or other resource.
  - `event` 'message.delivered' | 'message.read' | 'message.played' | 'message.failed', required
  - `message` InboxWebhookMessage, required — The message object included in inbox webhook payloads.
    - `id` string, required — Internal message ID
    - `conversationId` string, required — Internal conversation ID
    - `platform` 'instagram' | 'facebook' | 'telegram' | 'whatsapp' | 'sms', required
    - `platformMessageId` string, required — Platform's message ID
    - `direction` 'incoming' | 'outgoing', required
    - `text` string, nullable, required — Message text content (retained on deleted messages for API consumers; Zernio dashboard UI hides this)
    - `attachments` object[], required
      - `type` string, required — Attachment type (image, video, file, sticker, audio, share)
      - `url` string, required — Where to fetch the attachment. The contract depends on direction and platform: inbound WhatsApp media points at the authenticated `GET /v1/whatsapp/media/{mediaId}` and requires `Authorization: Bearer <your API key>`, while outgoing media carries the URL originally supplied and Instagram / Facebook / Telegram carry direct platform CDN links that need no authentication.
      - `payload` object — Additional attachment metadata
        - `voice` boolean — WhatsApp audio only. True for a voice note recorded in the WhatsApp client, false for an audio file.
    - `sender` object, required
      - `id` string, required — Sender's platform identifier. For WhatsApp this is the phone number (without leading `+`) when available, otherwise the `businessScopedUserId`. For other platforms, the platform's own user ID.
      - `contactId` string — Zernio CRM Contact id for this sender, when one exists (joined via the ContactChannel mapping). Lets integrators link a message straight to a Contact without a follow-up Contacts API call. Omitted when the sender isn't a tracked contact (e.g. outgoing messages where the sender is the business, or first-touch messages before the contact is created).
      - `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, so 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.shop`). Not a stable identifier, because users can change it. Useful for display, not recommended as an identity anchor.
      - `instagramProfile` object — Instagram profile data. Only present for Instagram conversations.
        - `isFollower` boolean, nullable
        - `isFollowing` boolean, nullable
        - `followerCount` integer, nullable
        - `isVerified` boolean, nullable
    - `sentAt` string, date-time, required — When the message was sent, as reported by the platform and passed through unmodified. Full ISO 8601 date-time: Instagram and Facebook carry millisecond precision, while some platforms (for example WhatsApp and Telegram) report whole seconds. Use this field as the chronological ordering key. If two messages share the same value, fetch the conversation messages with sortOrder=desc for the deterministic order.
    - `isRead` boolean, required
  - `statusAt` string, date-time, required — When the platform reported this status.
  - `error` object, nullable — Populated only on message.failed.
    - `code` integer
    - `title` string
    - `message` string
    - `details` string — Platform's extended detail for `code` (WhatsApp: Meta's `error_data.details`), when the platform sent one. Absent on SMS.
    - `href` string, uri — Link to the platform's documentation for `code`, when the platform sent one.
    - `explanation` string, nullable — Plain-language translation of `code` (e.g. for 131026, that the recipient has likely opted out of marketing messages while utility templates are unaffected, or for 131031, that Meta restricted the WhatsApp Business Account). Null for unmapped codes; fall back to title/message.
  - `pricing` WhatsAppMessagePricing, nullable — WhatsApp only. Meta's `pricing` object from the status webhook, camelCased. Present (possibly null) on every WhatsApp `message.sent`, `message.delivered`, `message.read` and `message.failed`; absent on other platforms. Meta includes it on `sent` and on one of `delivered` or `read`, so it is null on the others and usually on `failed`.
    - `billable` boolean, nullable, required — Whether Meta bills this message. Meta has announced it will deprecate this field.
    - `pricingModel` string, nullable, required — `PMP` (per-message pricing) or `CBP` (conversation-based, messages before 2025-07-01).
    - `category` string, nullable, required — Pricing category as Meta sends it, for example `marketing`, `marketing_lite`, `utility`, `authentication`, `authentication-international`, `service`, `referral_conversion`.
    - `type` string, nullable, required — `regular` (billable), `free_customer_service` or `free_entry_point`.
  - `billingConversation` WhatsAppBillingConversation, nullable — WhatsApp only. Meta's `conversation` object from the status webhook (the billing window, not the Zernio inbox `conversation`). Same presence rules as `pricing`; from Graph API v24 Meta sends it only inside an open free entry point window.
    - `id` string, nullable, required — Meta's conversation id.
    - `expiresAt` string, date-time, nullable, required — When the window expires. Meta sends it only on the `sent` status.
    - `originType` string, nullable, required — Meta `origin.type`, for example `marketing`, `utility`, `service`, `referral_conversion`.
  - `conversation` InboxWebhookConversation, required — The conversation context included in inbox webhook payloads.
    - `id` string, required — Zernio's internal conversation id (also the message's conversationId). Accepted by every /v1/inbox/conversations/{conversationId} endpoint.
    - `platformConversationId` string, required — The platform's conversation id. This is the `id` GET /v1/inbox/conversations returns for the same conversation (on Instagram and Messenger it is the participant's IGSID / PSID), so key your records on it to match webhooks with list rows. Also accepted by the conversation endpoints.
    - `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 — Account ID
    - `accountId` string — 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 ID this account belongs to. Use it to route or filter inbox webhooks by profile. 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
  - `timestamp` string, date-time, required — UTC time at which Zernio generated this event (set once when the event payload is built, before delivery is queued). Retries and redeliveries keep the original value, so it reflects the event, not the delivery attempt.

## Acknowledgement `200`

Webhook received successfully

---

[API](https://skmtc.dev/zernio/apis/zernio-api.md) · [All operations](https://skmtc.dev/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zernio/apis/zernio-api/revisions/f92af0e169c1?raw)
