Sign inGet started

WhatsApp-Events

Bird zeichnet Events für ein- und ausgehende WhatsApp-Nachrichten auf. Eine ausgehende Timeline zeigt, was nach der Rückgabe von 202 durch einen Send passiert ist: Annahme, Übergabe an WhatsApp, Zustellung, Lesen oder Fehler. Eine eingehende Timeline erfasst, wann Bird die Nachricht empfangen hat.

Der Event-Envelope

Öffentliche ausgehende Zustellungs-Events verwenden den Standard-Webhook-Envelope: eine type, eine timestamp und ein typspezifisches data-Objekt.
Codebeispiel
{
  "data": {
    "direction": "outbound",
    "from": { "phone_number": "+13124495569" },
    "metadata": { "session_id": "sess_4821" },
    "tags": [{ "name": "flow", "value": "login-otp" }],
    "to": { "phone_number": "+14155550100" },
    "whatsapp_id": "wam_01ky7qbvswf3fvyaw3az90391c",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b"
  },
  "timestamp": "2026-07-23T14:51:39.913Z",
  "type": "whatsapp.delivered"
}
Jeder öffentliche WhatsApp-Webhook-Payload für eine Nachricht enthält whatsapp_id, workspace_id, direction, from, to, tags und metadata. whatsapp.reacted ist die Ausnahme, da eine Reaktion eine Anmerkung zu einer Nachricht ist und keine eigene Nachricht; Reaktionen weiter unten beschreibt die Struktur. Eine Adresse kann eine E.164-phone_number, eine Meta-Business-Scoped-User-ID in bsuid oder beides enthalten. Eine Nachricht von einem WhatsApp-Nutzer enthält auch das vom Nutzer veröffentlichte Profil in username und display_name. tags und metadata sind null, wenn der Send keine enthielt. Eine als Antwort gesendete Nachricht enthält außerdem in_reply_to_message_id auf jedem ausgehenden Event in ihrer Timeline, von whatsapp.accepted über whatsapp.read, whatsapp.failed oder whatsapp.rejected, und benennt die Nachricht, auf die sie antwortet.
Die Events-API gibt schlankere Timeline-Einträge mit einer id, einem type und einem occurred_at-Zeitstempel zurück. Die Nachrichten-ID ist bereits in der Request-URL enthalten.

Lebenszyklus-Events

Events erscheinen in chronologischer Reihenfolge. Eine ausgehende Nachricht kann bei whatsapp.failed oder whatsapp.rejected enden, und whatsapp.read erscheint nur, wenn der Empfänger die Nachricht öffnet. Eine eingehende Nachricht hat ein einzelnes whatsapp.received-Timeline-Event.
EventBedeutung
whatsapp.acceptedBird hat die Sendeanforderung angenommen. Das ist, was die 202 zurückgemeldet hat.
whatsapp.sentBird hat die Nachricht an das WhatsApp-Netzwerk übergeben.
whatsapp.deliveredWhatsApp hat die Zustellung an das Gerät des Empfängers bestätigt.
whatsapp.readDer Empfänger hat die Nachricht geöffnet.
whatsapp.failedDie Nachricht wurde nicht zugestellt. error.code gibt an, was sie aufgehalten hat.
whatsapp.rejectedBird hat die Nachricht vor dem Senden abgelehnt. Sie wurde nicht berechnet.
whatsapp.receivedBird hat eine eingehende Nachricht von einem Kontakt empfangen.
Anwendbare delivered- oder read-Callbacks können Metas Preisanteil auslösen. WhatsApp-Event-Payloads enthalten keine Kosten. Lesen Sie die Nachricht mit GET /v1/whatsapp/messages/{message_id} zurück, um die Kosten zu sehen. Siehe Kosten und Abrechnung.
whatsapp.read ändert den Nachrichten-status nicht. Eine zugestellte Nachricht bleibt delivered; die Nachricht zeichnet das Lesen zusätzlich in read_at auf.
whatsapp.delivered kann vollständig übersprungen werden. Wenn der Empfänger den Chat bereits auf seinem Gerät geöffnet hat, meldet Meta das Lesen, ohne jemals eine Zustellung zu melden, sodass die Timeline whatsapp.acceptedwhatsapp.sentwhatsapp.read ohne whatsapp.delivered dazwischen lautet. Behandeln Sie read als Zustellnachweis: Ein Consumer, der auf delivered wartet, bevor er die Nachricht als zugestellt betrachtet, bleibt genau bei den Empfängern hängen, die sie am schnellsten gesehen haben, und einer, der die Zustellrate nur aus delivered berechnet, unterschätzt sie. Der Nachrichten-status bleibt in diesem Fall sent, da nur eine Zustellbestätigung ihn weiterschaltet.
Ein reiner Read-Callback kann trotzdem die anfallende Meta-Gebühr auslösen. Bird verwendet eine einzige Gebührenidentität über die Delivered- und Read-Pfade hinweg; das fehlende Delivery-Event bedeutet keinen kostenlosen Meta-Anteil. Siehe Kosten und Abrechnung.
Die Liste der Event-Typen ist offen: Neue Typen können im Laufe der Zeit hinzukommen. Behandeln Sie einen unbekannten Wert als zukünftiges Event und nicht als Fehler.

Fehler-Events

whatsapp.failed und whatsapp.rejected sind terminal. Eine Rejection bedeutet, dass Bird die Nachricht gestoppt hat, bevor sie an WhatsApp gesendet wurde, sodass keine Kosten anfielen. Ursachen sind unter anderem ein unterdrückter oder abgemeldeter Empfänger, unzureichendes Wallet-Guthaben oder ein Ziel ohne konfigurierten Preis. Ein Failure bedeutet, dass die Nachricht nicht zugestellt wurde, und error.code gibt an, wer das entschieden hat. Die meisten Codes tragen das Urteil von WhatsApp, abgeleitet aus dem gemeldeten Code. internal_error ist die Ausnahme: Es erfasst ein fehlendes nutzbares Absender-Credential oder erschöpfte Verarbeitungswiederholungen. Ein unsicherer Transportversuch beweist nicht, dass Meta die Anfrage nie erhalten hat. meta_error_code enthält den Code von WhatsApp, sofern verfügbar, und ein internal_error-Failure hat konstruktionsbedingt keinen.
Beide Events enthalten ein error-Objekt mit einem stabilen Bird-code, einer menschenlesbaren description, einem optionalen meta_error_code und occurred_at. Das Objekt erscheint in API-Einträgen und Webhook-Payloads nur für diese Event-Typen.

Reaktions-Events

Eine Emoji-Reaktion annotiert eine bestehende Nachricht. Sie erzeugt keine whatsapp.received-Nachricht. Bird sendet whatsapp.reacted, wenn ein Kontakt eine Reaktion hinzufügt, ändert oder entfernt, wie unter Reaktionen beschrieben. Reaktionen, die von Ihrer Geschäftsnummer gesendet werden, lösen diesen Webhook nicht aus.
Das Reaktionsprotokoll der Nachricht, auf die reagiert wurde, zeichnet Änderungen sowohl durch den Kontakt als auch durch Ihre Geschäftsnummer auf: Hinzufügungen, Ersetzungen und Entfernungen.
Ein Fall wird nirgends erfasst. Bird ordnet eine Reaktion über eine Provider-ID zu, die 15 Tage aufbewahrt wird, während WhatsApp eine Reaktion auf eine bis zu 30 Tage alte Nachricht akzeptiert. Eine Reaktion auf eine mehr als 15 Tage alte Nachricht kann daher nicht zugeordnet werden und erreicht weder das Protokoll noch reactions. Eine Nachricht ohne Einträge ist somit kein Beweis, dass niemand darauf reagiert hat.
Lesen Sie dieses Protokoll mit GET /v1/whatsapp/messages/{message_id}/reaction-events, neueste zuerst. Ein Eintrag nennt das Emoji, wer die Änderung vorgenommen hat, und einen status von received, sent, failed oder rejected; ein failed- oder rejected-Eintrag trägt den Grund auf error. Eine Reaktion wird nie berechnet, daher ist kein Failure dort ein Abrechnungs-Failure. Für den aktuellen Stand der Nachricht statt der Änderungshistorie lesen Sie ihre reactions mit GET /v1/whatsapp/messages/{message_id}, das das Protokoll auf einen Eintrag pro Absender zusammenfasst.

Unterdrückungs-Events

Über den Lebenszyklus einzelner Nachrichten hinaus meldet ein Event eine Änderung an der Unterdrückungsliste des Workspace: whatsapp_suppression.created feuert, wenn eine Unterdrückung eröffnet wird. Der Payload enthält die suppression_id, die unterdrückte address im E.164-Format, die waba, auf die der Block beschränkt ist (null, wenn er den gesamten Workspace abdeckt, unabhängig davon, welches Konto sendet), den reason und den workspace_id, sodass Ihr eigenes System neue Sperren ohne Polling erkennen kann. Nur Eröffnungen lösen ein Event aus: Das Beenden einer Unterdrückung tut dies noch nicht. Lesen Sie daher die Liste erneut, bevor Sie einen gespiegelten Block als weiterhin bestehend behandeln:
Codebeispiel
{
  "type": "whatsapp_suppression.created",
  "timestamp": "2026-08-25T14:52:03.192524705Z",
  "data": {
    "suppression_id": "was_01krdgeqcxet5s7t44vh8rt9mg",
    "address": "+14155550100",
    "waba": null,
    "reason": "manual",
    "workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf"
  }
}
Ein Opt-out, das der Empfänger selbst erklärt hat, ist eine Präferenz und keine Unterdrückung und löst stattdessen preference.revoked aus.

Events über die API lesen

GET /v1/whatsapp/messages/{message_id}/events gibt die Timeline in chronologischer Reihenfolge zurück. Die begrenzte Liste ist nicht paginiert. Das Lesen von Events erfordert einen API-Schlüssel mit whatsapp:read:
const { data } = await bird.whatsapp.listEvents("wa_abc123");
for (const event of data) console.log(event.type, event.occurred_at);
Eine Nachricht, die akzeptiert, gesendet, zugestellt und gelesen wurde, gibt vier Events zurück:
Codebeispiel
{
  "data": [
    {
      "id": "ev_01ky7q6a1fejfbvs0myn41hj41",
      "occurred_at": "2026-07-23T14:48:34.71Z",
      "type": "whatsapp.accepted"
    },
    {
      "id": "ev_01ky7q6a2denvtd6jg1vqwmg13",
      "occurred_at": "2026-07-23T14:48:35.671Z",
      "type": "whatsapp.sent"
    },
    {
      "id": "ev_01ky7q6a2zff9r2qm74mmg1g6z",
      "occurred_at": "2026-07-23T14:48:36.642Z",
      "type": "whatsapp.delivered"
    },
    {
      "id": "ev_01ky7q6c21frssf0vj8h50qysw",
      "occurred_at": "2026-07-23T14:48:38.65Z",
      "type": "whatsapp.read"
    }
  ]
}
Übergeben Sie type, um genau einen öffentlichen Event-Typ zurückzugeben, z. B. ?type=whatsapp.failed oder ?type=whatsapp.read. Lassen Sie ihn weg, um die vollständige Timeline zu erhalten.
Dieselbe Timeline zeigt die Seite WhatsApp-Protokoll an, wenn Sie eine Nachricht öffnen.
Das WhatsApp-Nachrichtendetailblatt im Bird-Dashboard, geöffnet für eine zugestellte bird_order_confirmation-Nachricht: Der Events-Tab zeigt die Lebenszyklus-Timeline der Nachricht mit Accepted, Sent, Delivered und Read, jeweils mit Zeitstempel, über der abgedunkelten Nachrichtenliste

Webhooks

Abonnieren Sie whatsapp.accepted, whatsapp.sent, whatsapp.delivered, whatsapp.read, whatsapp.failed, whatsapp.rejected, whatsapp.received und whatsapp.reacted über die Seite Webhooks oder die Webhooks-API. Der Webhooks-Leitfaden behandelt Endpunkte, Signaturen und Wiederholungsversuche.
whatsapp.received liefert den Inhalt der Nachricht zusätzlich zum oben beschriebenen Envelope, sodass ein Endpunkt auf eine eingehende Nachricht reagieren kann, ohne sie erneut abzurufen. Ein Tippen auf eine interaktive Nachricht kommt als interactive_reply an, und in_reply_to_message_id nennt die Nachricht, auf die geantwortet wird:
Codebeispiel
{
  "data": {
    "direction": "inbound",
    "from": {
      "display_name": "Alex Rivera",
      "phone_number": "+14155550100",
      "username": "alexr"
    },
    "in_reply_to_message_id": "wam_01ky7qbvswf3fvyaw3az90391c",
    "interactive_reply": {
      "list": {
        "description": "Next day to 2 days",
        "slug": "priority_express",
        "text": "Priority Mail Express"
      },
      "type": "list"
    },
    "metadata": null,
    "tags": null,
    "to": { "phone_number": "+13124495569" },
    "whatsapp_id": "wam_01ky8b3xq4gd7pmzn2ka51f7te",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b"
  },
  "timestamp": "2026-07-23T14:52:04.118Z",
  "type": "whatsapp.received"
}
Die anderen Content-Arme folgen derselben Einer-von-diesen-Struktur: text, image, video, audio, sticker, document, location, contact_cards und unsupported für eine Art, die API nicht modelliert. GET /v1/whatsapp/messages/{message_id} dokumentiert jeden einzelnen.

Reaktionen

whatsapp.reacted feuert, wenn ein WhatsApp-Nutzer auf eine Ihrer Nachrichten reagiert. Es ist das einzige WhatsApp-Event, das nicht zur Zustellungs-Timeline einer Nachricht gehört: Es erscheint nicht in GET /v1/whatsapp/messages/{message_id}/events, und dort gibt es nichts, wonach Sie es filtern könnten.
whatsapp_id nennt die Nachricht, auf die reagiert wurde, nicht die Reaktion, und emoji ist die Änderung, die der Nutzer vorgenommen hat. Ein Nutzer, der reagiert, sein Emoji ändert und dann die Reaktion zurücknimmt, erzeugt drei Events zu dieser einen Nachricht. WhatsApp sendet keine Entfernung zwischen den ersten beiden, sodass eine Änderung als einzelnes Event mit dem neuen Emoji eintrifft.
Codebeispiel
{
  "data": {
    "emoji": "👍",
    "from": { "phone_number": "+14155550100" },
    "to": { "phone_number": "+13124495569" },
    "whatsapp_id": "wam_01ky8b3xq4gd7pmzn2ka51f7te",
    "workspace_id": "ws_01ky7m235keycbnwyajabe1a6b"
  },
  "timestamp": "2026-07-23T14:52:11.000Z",
  "type": "whatsapp.reacted"
}
WhatsApp meldet die Reaktionszeit sekundengenau, sodass zwei dieser drei Events denselben timestamp teilen können. Sortieren nach diesem Wert ergibt keine Reihenfolge, ebenso wenig die Zustellreihenfolge, die durch Wiederholungsversuche unzuverlässig ist. Handeln Sie auf Basis der Reaktion, die jedes Event trägt, als die Änderung, die es beschreibt. Rekonstruieren Sie die Abfolge nicht aus den Events und behandeln Sie das zuletzt eintreffende nicht als die aktuelle Reaktion der Nachricht, denn weder die Zeitstempel noch die Eingangsreihenfolge stützen das. Lesen Sie die Nachricht zurück für die bestehenden Reaktionen: GET /v1/whatsapp/messages/{message_id} gibt einen Eintrag pro Absender in reactions zurück, und das Reaktionsprotokoll der Nachricht enthält jede Änderung.
emoji ist vorhanden und null, wenn der Nutzer seine Reaktion zurückgenommen hat; ein Null ist also die Entfernung selbst und kein fehlender Wert. Das Emoji wird genau so geliefert, wie WhatsApp es gesendet hat, und wird nicht normalisiert, sodass und ❤️ als unterschiedliche Strings bei Ihnen ankommen.

Nächste Schritte