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

# Message delivery failed event

`POST message.failed` (webhook)

Fired when an outgoing message fails to deliver. Currently only emitted
for WhatsApp (other platforms don't expose per-message failure via
webhook). The payload error object contains code, title, and
message from the platform.

## Payload

- WebhookPayloadMessageDeliveryStatus — Shared payload for message.delivered, message.read, and message.failed events. Fires when the platform reports a new delivery state for an outgoing message. Platform support: * message.delivered — WhatsApp, Facebook Messenger. * message.read — WhatsApp, Facebook Messenger, Instagram. * message.failed — WhatsApp only (other platforms don't expose per-message failure via webhook).
  - `id` string, required
  - `event` 'message.delivered' | 'message.read' | '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', 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)
      - `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
    - `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 — 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. Only present for Instagram conversations.
        - `isFollower` boolean, nullable
        - `isFollowing` boolean, nullable
        - `followerCount` integer, nullable
        - `isVerified` boolean, nullable
    - `sentAt` string, date-time, required
    - `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
    - `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). Null for unmapped codes; fall back to title/message.
  - `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
  - `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)
