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
WhatsApp-Events für Zustellung, eingehende Nachrichten und Reaktionen verwenden den standardmäßigen 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 das whatsapp.read-Event erscheint nur, wenn der Empfänger die Nachricht öffnet. Eine eingehende Timeline beginnt mit whatsapp.received und kann whatsapp.read aufzeichnen, nachdem Ihr Workspace die Nachricht als gelesen markiert hat.
| Event | Bedeutung |
|---|---|
| whatsapp.accepted | Bird hat die Sendeanforderung angenommen. Das ist, was die 202 zurückgemeldet hat. |
| whatsapp.sent | Bird hat die Nachricht an das WhatsApp-Netzwerk übergeben. |
| whatsapp.delivered | WhatsApp hat die Zustellung an das Gerät des Empfängers bestätigt. |
| whatsapp.read | Der Empfänger hat die Nachricht geöffnet. |
| whatsapp.failed | Die Nachricht wurde nicht zugestellt. error.code gibt an, was sie aufgehalten hat. |
| whatsapp.rejected | Bird hat die Nachricht vor dem Senden abgelehnt. Sie wurde nicht berechnet. |
| whatsapp.received | Bird 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.
Eine eingehende Nachricht als gelesen markieren zeichnet whatsapp.read in der Timeline auf, löst aber keinen Lesebestätigungs-Webhook aus. Die eingehende Nachricht behält ihren received-Status und zeichnet read_at auf, nachdem WhatsApp die Bestätigung akzeptiert hat.
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 komplett ü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. Die Timeline liest sich dann als whatsapp.accepted → whatsapp.sent → whatsapp.read ohne whatsapp.delivered dazwischen. Behandeln Sie read als Zustellungsnachweis: Ein Consumer, der auf delivered wartet, bevor er die Nachricht als angekommen betrachtet, hängt genau bei den Empfängern, die sie am schnellsten gesehen haben, und einer, der eine Zustellrate nur aus delivered berechnet, unterschätzt sie. Der Nachrichten-status bleibt in diesem Fall sent, da nur eine Zustellbestätigung ihn weiterbewegt.
Ein Nur-Lese-Callback kann trotzdem die geltende Meta-Gebühr auslösen. Bird verwendet eine einheitliche Gebührenidentität über die Zustell- und Lesepfade hinweg; das fehlende Zustellevent bedeutet keine kostenfreie Meta-Komponente. Siehe Kosten und Abrechnung.
Die Event-Typ-Liste ist offen: Neue Typen können im Laufe der Zeit hinzukommen. Behandeln Sie einen unbekannten Wert als zukünftiges Event und nicht als Fehler.
Fehlschlag-Events
whatsapp.failed und whatsapp.rejected sind terminal. Eine Ablehnung bedeutet, dass Bird die Nachricht gestoppt hat, bevor sie an WhatsApp gesendet wurde, daher wurde sie nicht berechnet. Ursachen sind unter anderem ein unterdrückter oder abgemeldeter Empfänger, unzureichendes Wallet-Guthaben oder ein Ziel ohne konfigurierten Preis. Ein Fehlschlag bedeutet, dass die Nachricht nicht zugestellt wurde, und error.code gibt an, wer das entschieden hat. Die meisten Codes enthalten das Urteil von WhatsApp, gemappt aus dem gemeldeten Code. internal_error ist die Ausnahme: Es zeichnet fehlende nutzbare Absender-Anmeldedaten oder erschöpfte Verarbeitungs-Retries auf. Ein unsicherer Transportversuch beweist nicht, dass Meta die Anfrage nie erhalten hat. meta_error_code enthält den Code von WhatsApp, wenn verfügbar, und ein internal_error-Fehlschlag 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 erstellt keine whatsapp.received-Nachricht. Bird löst whatsapp.reacted aus, wenn ein Kontakt eine Reaktion hinzufügt, ändert oder entfernt, wie in Reaktionen beschrieben. Von Ihrer Geschäftsnummer gesendete Reaktionen lösen diesen Webhook nicht aus. Siehe Reaktionen senden zum Hinzufügen, Ersetzen oder Entfernen Ihrer Reaktion, und Reaktionen empfangen für Webhook- und REST API-Beispiele. Kontaktreaktionen öffnen kein Kundenservice-Fenster.
Das Reaktionsprotokoll der reagierten Nachricht zeichnet Änderungen sowohl durch den Kontakt als auch durch Ihre Geschäftsnummer auf: Hinzufügungen, Ersetzungen und Entfernungen.
Ein Fall wird nirgendwo aufgezeichnet. Bird ordnet eine Reaktion ihrer Nachricht über eine Provider-ID zu, die 15 Tage lang gespeichert wird, während WhatsApp eine Reaktion auf eine Nachricht bis zu 30 Tage nach dem Senden akzeptiert. Eine Reaktion auf eine ältere Nachricht kann daher nicht zugeordnet werden und erreicht weder das Protokoll noch reactions. Eine Nachricht ohne Einträge ist daher 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 enthält den Grund in error. Jeder Eintrag hat eine Reaktions-ID (war_…) und einen occurred_at-Zeitstempel. Eine Entfernung hat emoji: null. Ausstehende Änderungen haben keinen Eintrag, bis ihr Ergebnis feststeht. Eine Reaktion wird nie berechnet, daher ist kein Fehlschlag dort ein Abrechnungsfehlschlag. Für die aktuell auf der Nachricht stehenden Reaktionen statt der Änderungshistorie lesen Sie deren reactions mit GET /v1/whatsapp/messages/{message_id}, das das Protokoll auf einen Eintrag pro Absender zusammenfasst.
Unterdrückungs-Events
Über den Nachrichten-Lebenszyklus hinaus meldet ein Event eine Änderung an der Unterdrückungsliste des Workspace: whatsapp_suppression.created wird ausgelöst, wenn eine Unterdrückung beginnt. Der Payload enthält die suppression_id, die unterdrückte address im E.164-Format, die waba, auf die die Sperre beschränkt ist (null, wenn sie den gesamten Workspace abdeckt, unabhängig davon, welches Konto sendet), die reason und die workspace_id, damit Ihr eigenes System neue Sperren ohne Polling sehen 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 eine gespiegelte Sperre als noch 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);events = client.whatsapp.list_events("wa_abc123")
for event in events.data:
print(event.type, event.occurred_at)events, err := client.Whatsapp.ListEvents(context.Background(), "wam_01krdgeqcxet5s7t44vh8rt9mg", bird.WhatsappListEventsParams{})
if err != nil {
log.Fatal(err)
}
for _, e := range events.Data {
fmt.Println(e.Id, e.Type)
}$events = $bird->whatsapp->listEvents('wamid_01krdgeqcxet5s7t44vh8rt9mg');
foreach ($events->getData() ?? [] as $event) {
echo $event->getType(), ' ', $event->getId(), "\n";
}bird whatsapp list-events <message-id>curl https://us1.platform.bird.com/v1/whatsapp/messages/wam_.../events \
-H "Authorization: Bearer $BIRD_API_KEY"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 für die vollständige Timeline.
Dieselbe Timeline ist das, was die Seite WhatsApp-Protokoll anzeigt, wenn Sie eine Nachricht öffnen.

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 Retries.
whatsapp.received enthält den Nachrichteninhalt zusätzlich zum oben beschriebenen Envelope, sodass ein Endpunkt auf eine eingehende Nachricht reagieren kann, ohne sie zurückzulesen. Ein Tippen auf eine interaktive Nachricht kommt als interactive_reply an, und in_reply_to_message_id benennt 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 Inhalts-Arme folgen derselben Einer-davon-Struktur: text, image, video, audio, sticker, document, location, contact_cards und unsupported für eine Art, die die API nicht modelliert. GET /v1/whatsapp/messages/{message_id} dokumentiert jede einzelne.
Reaktionen
whatsapp.reacted wird ausgelöst, wenn ein WhatsApp-Nutzer auf eine Ihrer Nachrichten reagiert. Es ist das einzige WhatsApp-Event, das nicht zur Zustell-Timeline einer Nachricht gehört: Es erscheint nicht in GET /v1/whatsapp/messages/{message_id}/events, und es gibt dort nichts, wonach Sie es filtern könnten.
whatsapp_id benennt 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 auf dieser einen Nachricht. WhatsApp sendet keine Entfernung zwischen den ersten beiden, sodass eine Änderung als einzelnes Event mit dem neuen Emoji ankommt.
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. Eine Sortierung danach ordnet sie nicht, ebenso wenig die Zustellreihenfolge, die durch Retries unzuverlässig ist. Handeln Sie auf Basis der Reaktion jedes Events als die beschriebene Änderung. Rekonstruieren Sie nicht die Reihenfolge aus den Events und behandeln Sie nicht das zuletzt eingetroffene als die aktuelle Reaktion der Nachricht, da weder die Zeitstempel noch die Ankunftsreihenfolge dies stützen. Lesen Sie die Nachricht zurück, um die aktuellen Reaktionen zu erhalten: 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-Wert 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
- Reaktionen empfangen: Kontaktreaktions-Webhooks verarbeiten und aktuelle Reaktionen lesen
- Reaktionen senden: Ihre Reaktion hinzufügen, ersetzen oder entfernen
- Nachricht als gelesen markieren: eine eingehende Nachricht bestätigen und Tippen anzeigen
- WhatsApp-Protokoll: die Nachrichten-Detailansicht, die diese Timeline darstellt
- WhatsApp-Nachrichten senden: wo der Lebenszyklus einer Nachricht beginnt
- Webhooks-Leitfaden: Endpunkte, Signaturen, Retries und der vollständige Event-Katalog
Verwandte Ressourcen
Weiter mit der Dokumentation, Anleitungen und Beispielen zu diesem Thema. Die Ressourcen sind auf Englisch.
Anleitung ansehenConnecting WhatsApp to Bird: from buying a number to a live channelDas Konzept verstehenWhat is the 24-hour customer service window on WhatsApp?Das Tool verwendenWhatsApp message builderDie Funktion erkundenWhatsApp
Übung ausprobieren und ein Implementierungs-Briefing erhalten