> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WhatsApp Coexistence Webhooks

> Handle Business app message echoes and lifecycle events for a WhatsApp Coexistence number.

# WhatsApp Coexistence Webhooks

WhatsApp Coexistence adds customer-facing events for messages sent from the WhatsApp Business app and for changes to the coexistence connection.

Return an HTTP `2xx` response promptly. Verify every webhook using the Telnyx webhook signature before processing it. See [Receiving webhooks](/docs/development/api-fundamentals/webhooks/receiving-webhooks).

## Event summary

| Telnyx event                                | Source                    | Purpose                                                         |
| ------------------------------------------- | ------------------------- | --------------------------------------------------------------- |
| `message.echo`                              | Meta `smb_message_echoes` | Mirrors a message sent manually from the WhatsApp Business app. |
| `whatsapp.account.update`                   | Meta `account_update`     | Reports offboarding, reconnection, or partner removal.          |
| `message.received` with `body.type: edit`   | Meta `messages`           | Reports an edit to a user-originated message.                   |
| `message.received` with `body.type: revoke` | Meta `messages`           | Reports a user-originated message deletion.                     |

Meta also sends `history` and `smb_app_state_sync` synchronization webhooks. Telnyx validates and processes these internally. They are not forwarded to the customer webhook URL.

## `message.echo`

`message.echo` represents a message sent from the WhatsApp Business app. It is outbound and includes `origin: whatsapp_business_app`.

```json theme={null}
{
  "data": {
    "record_type": "event",
    "id": "4f923db9-186f-4c6a-9a14-0b6d46e31d77",
    "event_type": "message.echo",
    "occurred_at": "2026-09-02T10:15:30.000Z",
    "payload": {
      "record_type": "message",
      "direction": "outbound",
      "origin": "whatsapp_business_app",
      "id": "c7b87bc9-d220-4b2c-916a-83c11ef9821f",
      "type": "WHATSAPP",
      "organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
      "messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
      "from": {
        "phone_number": "+13125551234",
        "carrier": "",
        "line_type": ""
      },
      "to": "+18655550001",
      "body": {
        "from": "+18655550001",
        "id": "c7b87bc9-d220-4b2c-916a-83c11ef9821f",
        "foreign_id": "wamid.HBgLMTg2NTU1NTAwMDEFQIAERgSODQ2",
        "timestamp": "1788344130",
        "type": "text",
        "text": {
          "body": "Sent from the WhatsApp Business app"
        }
      },
      "cost": {
        "amount": null,
        "currency": null
      },
      "errors": []
    }
  }
}
```

The payload has the following coexistence-specific semantics:

| Field                            | Value                   | Meaning                                                       |
| -------------------------------- | ----------------------- | ------------------------------------------------------------- |
| `data.event_type`                | `message.echo`          | Distinguishes the echo from `message.received`.               |
| `data.payload.direction`         | `outbound`              | The business sent the message to the recipient.               |
| `data.payload.origin`            | `whatsapp_business_app` | The Business app, not the Telnyx API, originated the message. |
| `data.payload.from.phone_number` | Business number         | The coexistence-enabled phone number.                         |
| `data.payload.to`                | Recipient               | The WhatsApp user who received the message.                   |
| `data.payload.body.foreign_id`   | Meta `wamid`            | Correlates the message with Meta events.                      |

`message.echo` is non-billable and does not open or extend the Cloud API customer service window. Avoid feeding it into inbound autoresponse, opt-out, or bot workflows.

### Handle message echoes

```python Python theme={null}
def handle_telnyx_webhook(event):
    data = event["data"]
    if data["event_type"] != "message.echo":
        return

    payload = data["payload"]
    assert payload["direction"] == "outbound"
    assert payload["origin"] == "whatsapp_business_app"
    store_outbound_business_app_message(payload)
```

```javascript Node.js theme={null}
function handleTelnyxWebhook(event) {
  const data = event.data;
  if (data.event_type !== "message.echo") return;

  const payload = data.payload;
  if (
    payload.direction !== "outbound" ||
    payload.origin !== "whatsapp_business_app"
  ) {
    throw new Error("Invalid WhatsApp Business app echo");
  }

  storeOutboundBusinessAppMessage(payload);
}
```

## `whatsapp.account.update`

Coexistence lifecycle changes arrive as `whatsapp.account.update`. Inspect `data.payload.event` to determine the transition.

```json theme={null}
{
  "data": {
    "record_type": "event",
    "id": "72f74d14-7506-4de7-bd13-62aacf140fa3",
    "event_type": "whatsapp.account.update",
    "occurred_at": "2026-09-02T10:20:00.000Z",
    "payload": {
      "record_type": "whatsapp_account",
      "waba_id": "106540352242922",
      "event": "ACCOUNT_OFFBOARDED"
    }
  }
}
```

| `data.payload.event`  | Meaning                                                                         | Expected state |
| --------------------- | ------------------------------------------------------------------------------- | -------------- |
| `ACCOUNT_OFFBOARDED`  | Meta offboarded the coexistence connection.                                     | `offboarded`   |
| `ACCOUNT_RECONNECTED` | The business reconnected after offboarding. A new synchronization cycle begins. | `sync_pending` |
| `PARTNER_REMOVED`     | The business disconnected Telnyx from the Business app.                         | `disconnected` |

Treat lifecycle events as idempotent. Duplicate or delayed Meta events can arrive, and Telnyx fences stale transitions against the current connection lifecycle.

Meta may include `disconnection_info` with a `PARTNER_REMOVED` event. The current Telnyx customer webhook does not expose that object, so integrations must use `data.payload.event` as the lifecycle signal.

## Message edits and revocations

WhatsApp user edits and revocations are delivered through the standard `message.received` event. Inspect `data.payload.body.type` to distinguish them. Telnyx does not emit separate `message.edited` or `message.revoked` event names.

### Edited message

An edit can apply to text or to a media caption. Meta accepts edits for up to 15 minutes after the original message was sent.

```json theme={null}
{
  "data": {
    "record_type": "event",
    "id": "2ff83f52-fdf7-4235-b64e-326e49786ff3",
    "event_type": "message.received",
    "occurred_at": "2026-09-02T10:25:00.000Z",
    "payload": {
      "record_type": "message",
      "direction": "inbound",
      "id": "6c4cbb69-9c05-4371-9d9b-84a521a2cd20",
      "type": "WHATSAPP",
      "organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
      "messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
      "from": {
        "phone_number": "+18655550001",
        "carrier": "",
        "line_type": ""
      },
      "to": "+13125551234",
      "body": {
        "from": "+18655550001",
        "id": "6c4cbb69-9c05-4371-9d9b-84a521a2cd20",
        "foreign_id": "wamid.EDIT_EVENT_ID",
        "timestamp": "1788344700",
        "type": "edit",
        "edit": {
          "original_message_id": "fefc7543-0816-4652-a70a-22af8fd935ec",
          "message": {
            "type": "text",
            "text": {
              "body": "Updated message text"
            }
          }
        }
      },
      "cost": {
        "amount": null,
        "currency": null
      },
      "received_at": "2026-09-02T10:24:59.000Z",
      "errors": []
    }
  }
}
```

### Revoked message

A user can revoke a message for up to two days after it was sent.

```json theme={null}
{
  "data": {
    "record_type": "event",
    "id": "cf2a2cf3-277a-472c-a281-57e2d33c2547",
    "event_type": "message.received",
    "occurred_at": "2026-09-02T10:30:00.000Z",
    "payload": {
      "record_type": "message",
      "direction": "inbound",
      "id": "6c7681d6-e590-4a9f-9e4e-2f41435194cf",
      "type": "WHATSAPP",
      "organization_id": "7a7f45eb-6670-45fd-a3e3-5b9e4a4f2103",
      "messaging_profile_id": "4001799e-1457-4c7f-a96a-735ed44c7de7",
      "from": {
        "phone_number": "+18655550001",
        "carrier": "",
        "line_type": ""
      },
      "to": "+13125551234",
      "body": {
        "from": "+18655550001",
        "id": "6c7681d6-e590-4a9f-9e4e-2f41435194cf",
        "foreign_id": "wamid.REVOKE_EVENT_ID",
        "timestamp": "1788345000",
        "type": "revoke",
        "revoke": {
          "original_message_id": "fefc7543-0816-4652-a70a-22af8fd935ec"
        }
      },
      "cost": {
        "amount": null,
        "currency": null
      },
      "received_at": "2026-09-02T10:29:59.000Z",
      "errors": []
    }
  }
}
```

When Telnyx has already mapped the original Meta message ID, `original_message_id` is the Telnyx message UUID. If the mapping is unavailable, the field can contain the original Meta `wamid`, so treat it as an opaque string.

## Delivery and retry handling

* Return an HTTP `2xx` response before starting long-running work.
* Deduplicate message echoes using `data.payload.id` or `data.payload.body.foreign_id`.
* Persist lifecycle processing by WABA ID and event type.
* Do not assume that Meta events arrive in chronological order.
* Keep unknown fields and enum values when storing the payload so integrations remain compatible with future additions.

## Unsupported or internal events

`history` and `smb_app_state_sync` are synchronization protocol events. Telnyx consumes them internally and does not deliver them as customer webhook events.

Messages sent from unsupported companion clients might not generate `message.echo`. Treat the customer webhook stream as best-effort visibility, not a complete archive of Business app activity.
