Sign inGet Started

WhatsApp reaction events

An emoji reaction annotates an existing message rather than creating one, so it never appears as a message of its own or in the message's lifecycle events. Bird keeps two views of it instead: the reactions standing on the message now, and a log of every change that led there. Both record changes by the contact and by your business number.
To be told about a contact's reaction as it happens, subscribe to whatsapp.reacted. To add or remove your own, see Sending reactions.

Prerequisites

An API key with WhatsApp read permission, and the ID (wam_…) of the message that was reacted to.

1. Read the current reactions

If your application displays the reaction currently attached to a message, retrieve the message and read its reactions. The list contains one standing reaction per sender and is omitted when no reactions stand.
For GET /v1/whatsapp/messages/wam_01ky8b3xq4gd7pmzn2ka51f7te, the reaction-related fields have this shape (other message fields omitted):
Code example
{
  "id": "wam_01ky8b3xq4gd7pmzn2ka51f7te",
  "reactions": [
    {
      "emoji": "👍",
      "from": { "phone_number": "+14155550100" }
    }
  ]
}
The message retains its original content, direction, and delivery status. reactions[].from identifies the person who reacted.
This is the state to display. Reaction webhooks can share a timestamp and arrive out of order, so use one as the trigger to read the message back rather than as the current state.

2. Read the reaction log

List reaction events with GET /v1/whatsapp/messages/{message_id}/reaction-events to see every change to the referenced message, newest first: additions, replacements, and removals. Each entry has a reaction ID (war_…), the emoji, who made the change in from, an occurred_at timestamp, and a status:
  • received: a contact's change.
  • sent: WhatsApp accepted a change by your business number.
  • failed: WhatsApp refused your change.
  • rejected: Bird refused your change before sending it.
A failed or rejected entry carries the reason on error. A removal carries emoji: null. A change of yours that is still pending has no entry until its outcome is known. A reaction is never charged, so no failure here is a billing one.
The reaction-events API returns a paginated response. This example shows a rejected business reaction and an earlier received contact reaction:
Code example
{
  "data": [
    {
      "id": "war_01krdgeqcxet5s7t44vh8rt9mh",
      "emoji": "🎉",
      "status": "rejected",
      "from": {
        "phone_number": "+13124495569"
      },
      "error": {
        "code": "internal_error",
        "description": "the receiving number is no longer connected",
        "occurred_at": "2026-08-28T19:04:22Z"
      },
      "occurred_at": "2026-08-28T19:04:22Z"
    },
    {
      "id": "war_01krdgeqcxet5s7t44vh8rt9mg",
      "emoji": "👍",
      "status": "received",
      "from": {
        "phone_number": "+14155550100",
        "bsuid": "US.13491208655302741918"
      },
      "occurred_at": "2026-08-28T19:01:10Z"
    }
  ],
  "next_cursor": null,
  "prev_cursor": null,
  "refresh_cursor": "eyJ2IjoxLCJzIjoiMjAyNi0wOC0yOFQxOTowNDoyMloiLCJpIjoiMDE5ZTFiMDctNWQ5ZC03NjhiLTkzZTgtODRkYzUxOGQyNjkxIn0"
}
Reaction history is separate from the message's delivery events. The lifecycle events endpoint does not contain whatsapp.reacted changes. For retention and pagination, follow the reaction log reference.

Troubleshooting

  • Reaction missing from the message list: Look up the original message. A reaction is attached to it and has no separate message row.
  • Reaction state changes unexpectedly: Read the message's reactions instead of ordering webhook events by arrival time or timestamp.

Next steps