Sign inGet started

WhatsApp-events

Bird registreert events voor inkomende en uitgaande WhatsApp-berichten. Een uitgaande tijdlijn laat zien wat er gebeurde nadat een verzending 202 teruggaf: acceptatie, overdracht aan WhatsApp, bezorging, lezen of falen. Een inkomende tijdlijn registreert wanneer Bird het bericht ontving.

De event-envelope

Publieke uitgaande bezorgingsevents gebruiken de standaard webhook-envelope: een type, een timestamp en een typespecifiek data-object.
Codevoorbeeld
{
  "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"
}
Elke publieke WhatsApp-webhookpayload voor een bericht bevat whatsapp_id, workspace_id, direction, from, to, tags en metadata. whatsapp.reacted is de uitzondering, omdat een reactie een annotatie op een bericht is en geen eigen bericht; Reacties hieronder beschrijft de structuur. Een adres kan een E.164-phone_number, een Meta business-scoped user-ID in bsuid, of beide bevatten. Een bericht dat van een WhatsApp-gebruiker is ontvangen, bevat ook het profiel dat ze publiceren, in username en display_name. tags en metadata zijn null als de verzending er geen bevatte. Een bericht dat als antwoord is verzonden, bevat ook in_reply_to_message_id op elk uitgaand event in de tijdlijn, van whatsapp.accepted via whatsapp.read, whatsapp.failed of whatsapp.rejected, met het bericht waarop het antwoordt.
De events-API retourneert compactere tijdlijnrecords met een id, type en occurred_at-tijdstempel. Het bericht-ID staat al in de request-URL.

Lifecycle-events

Events verschijnen in chronologische volgorde. Een uitgaand bericht kan stoppen bij whatsapp.failed of whatsapp.rejected, en whatsapp.read verschijnt alleen als de ontvanger het bericht opent. Een inkomend bericht heeft één whatsapp.received-tijdlijnevent.
EventBetekenis
whatsapp.acceptedBird heeft het verzendverzoek geaccepteerd. Dit is wat de 202 meldde.
whatsapp.sentBird heeft het bericht overgedragen aan het WhatsApp-netwerk.
whatsapp.deliveredWhatsApp heeft bezorging aan het apparaat van de ontvanger bevestigd.
whatsapp.readDe ontvanger heeft het bericht geopend.
whatsapp.failedHet bericht is niet bezorgd. error.code geeft aan wat het tegenhield.
whatsapp.rejectedBird heeft het bericht geweigerd vóór verzending. Er zijn geen kosten in rekening gebracht.
whatsapp.receivedBird heeft een inkomend bericht van een contactpersoon ontvangen.
Toepasselijke delivered- of read-callbacks kunnen Meta's aandeel van de prijs triggeren. WhatsApp-eventpayloads bevatten geen kosten. Lees het bericht terug met GET /v1/whatsapp/messages/{message_id} om te zien wat het kostte. Zie Kosten en facturering.
whatsapp.read verandert de bericht-status niet. Een bezorgd bericht blijft delivered; het bericht registreert het lezen ook in read_at.
whatsapp.delivered kan volledig worden overgeslagen. Wanneer de ontvanger de chat al open heeft op het apparaat, meldt Meta het lezen zonder ooit een bezorging te melden, waardoor de tijdlijn whatsapp.acceptedwhatsapp.sentwhatsapp.read luidt zonder whatsapp.delivered ertussen. Beschouw read als bewijs van bezorging: een consumer die wacht op delivered voordat hij het bericht als aangekomen beschouwt, blijft hangen bij precies de ontvangers die het het snelst zagen, en een consumer die een bezorgpercentage berekent op basis van alleen delivered rapporteert het te laag. De bericht-status blijft sent in dit geval, omdat alleen een bezorgbevestiging deze doet opschuiven.
Een callback die alleen een leesbevestiging meldt kan nog steeds de toepasselijke Meta-vergoeding triggeren. Bird gebruikt één vergoedingsidentiteit voor zowel het delivered- als het read-pad; het ontbreken van het delivery-event betekent niet dat de Meta-component gratis is. Zie Kosten en facturering.
De lijst met eventtypen is open: er kunnen in de loop van de tijd nieuwe typen worden toegevoegd, dus behandel een onbekende waarde als een toekomstig event in plaats van een fout.

Faalevents

whatsapp.failed en whatsapp.rejected zijn terminaal. Een rejection betekent dat Bird het bericht tegenhield voordat het naar WhatsApp werd verzonden, dus het werd niet in rekening gebracht. Oorzaken zijn onder meer een onderdrukte of afgemelde ontvanger, onvoldoende walletsaldo, of een bestemming zonder geconfigureerde prijs. Een failure betekent dat het bericht niet is afgeleverd, en error.code vermeldt wie dat besliste. De meeste codes bevatten het oordeel van WhatsApp, afgeleid van de code die het rapporteerde. internal_error is de uitzondering: het registreert een ontbrekende bruikbare afzendercredential of uitgeputte verwerkingspogingen. Een onzekere transportpoging bewijst niet dat Meta het verzoek nooit heeft ontvangen. meta_error_code bevat de code van WhatsApp wanneer beschikbaar, en een internal_error-failure heeft er per definitie geen.
Beide events bevatten een error-object met een stabiele Bird code, een leesbare description, een optionele meta_error_code en occurred_at. Het object verschijnt alleen bij deze eventtypen in API-records en webhookpayloads.

Reactie-events

Een emojireactie annoteert een bestaand bericht. Het maakt geen whatsapp.received-bericht aan. Bird zendt whatsapp.reacted uit wanneer een contact een reactie toevoegt, wijzigt of verwijdert, zoals beschreven in Reacties. Reacties verzonden door je zakelijke nummer zenden die webhook niet uit.
Het reactielog van het bericht waarop gereageerd is, registreert wijzigingen door zowel het contact als je zakelijke nummer: toevoegingen, vervangingen en verwijderingen.
Eén geval wordt nergens geregistreerd. Bird koppelt een reactie aan het bericht via een provider-ID dat het 15 dagen bewaart, terwijl WhatsApp een reactie op een bericht tot 30 dagen oud accepteert. Een reactie waarvoor de bewaartermijn van de provider-ID is verlopen, kan daardoor niet worden gekoppeld en bereikt noch het log, noch reactions. Een bericht zonder vermeldingen is daarom geen bewijs dat niemand erop reageerde.
Lees dat log met GET /v1/whatsapp/messages/{message_id}/reaction-events, nieuwste eerst. Een vermelding noemt de emoji, wie de wijziging maakte, en een status van received, sent, failed of rejected; een failed- of rejected-vermelding bevat de reden op error. Een reactie wordt nooit in rekening gebracht, dus geen failure daarin is een factureringsincident. Lees voor wat er op dit moment op het bericht staat in plaats van de wijzigingsgeschiedenis de reactions met GET /v1/whatsapp/messages/{message_id}, dat het log samenvouwt tot één vermelding per afzender.

Suppressie-events

Naast de levenscyclus per bericht rapporteert één event een wijziging in de suppressielijst van de werkruimte: whatsapp_suppression.created wordt geactiveerd wanneer een suppressie opent. De payload bevat de suppression_id, het onderdrukte address in E.164-formaat, de waba waartoe de blokkering beperkt is (null wanneer deze de hele werkruimte dekt, ongeacht welk account verzendt), de reason en de workspace_id, zodat je eigen systeem nieuwe blokkeringen kan zien zonder te pollen. Alleen openingen activeren een event: het beëindigen van een suppressie doet dat nog niet, dus lees de lijst opnieuw voordat je een gespiegelde blokkering als nog actief behandelt:
Codevoorbeeld
{
  "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"
  }
}
Een opt-out die de ontvanger zelf aangaf is een voorkeur in plaats van een suppressie, en activeert preference.revoked in plaats daarvan.

Events lezen vanuit de API

GET /v1/whatsapp/messages/{message_id}/events retourneert de tijdlijn in chronologische volgorde. De begrensde lijst is niet gepagineerd. Het lezen van events vereist een API-sleutel met whatsapp:read:
const { data } = await bird.whatsapp.listEvents("wa_abc123");
for (const event of data) console.log(event.type, event.occurred_at);
Een bericht dat geaccepteerd, verzonden, afgeleverd en gelezen is, retourneert vier events:
Codevoorbeeld
{
  "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"
    }
  ]
}
Geef type mee om één exact publiek eventtype te retourneren, zoals ?type=whatsapp.failed of ?type=whatsapp.read. Laat het weg voor de volledige tijdlijn.
Dezelfde tijdlijn is wat de WhatsApp-log-pagina weergeeft wanneer je een bericht opent.
Het WhatsApp-berichtdetailvenster in het Bird-dashboard, geopend voor een afgeleverd bird_order_confirmation-bericht: het tabblad Events met de levenscyclustijdlijn per bericht van Accepted, Sent, Delivered en Read, elk met het bijbehorende tijdstempel, over de grijze berichtenlijst

Webhooks

Abonneer je op whatsapp.accepted, whatsapp.sent, whatsapp.delivered, whatsapp.read, whatsapp.failed, whatsapp.rejected, whatsapp.received en whatsapp.reacted vanuit de Webhooks-pagina of de webhooks API. De Webhooks-gids behandelt endpoints, signatures en retries.
whatsapp.received bevat de inhoud van het bericht bovenop het bovenstaande envelope, zodat een endpoint op een inkomend bericht kan reageren zonder het terug te lezen. Een tik op een interactief bericht komt binnen als interactive_reply, en in_reply_to_message_id noemt het bericht dat het beantwoordt:
Codevoorbeeld
{
  "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"
}
De andere contenttakken volgen dezelfde een-van-deze-vorm: text, image, video, audio, sticker, document, location, contact_cards en unsupported voor een type dat de API niet modelleert. GET /v1/whatsapp/messages/{message_id} documenteert elk type.

Reacties

whatsapp.reacted wordt geactiveerd wanneer een WhatsApp-gebruiker reageert op een van je berichten. Het is het enige WhatsApp-event dat geen deel uitmaakt van de afleveringstijdlijn van een bericht: het verschijnt niet in GET /v1/whatsapp/messages/{message_id}/events, en er is niets om het daar op te filteren.
whatsapp_id noemt het bericht waarop gereageerd is, niet de reactie, en emoji is de wijziging die de gebruiker maakte. Een gebruiker die reageert, de emoji wijzigt en vervolgens de reactie terugneemt, produceert drie events op dat ene bericht. WhatsApp stuurt geen verwijdering tussen de eerste twee, dus een wijziging komt binnen als één event met de nieuwe emoji.
Codevoorbeeld
{
  "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 rapporteert de reactietijd tot op de seconde, dus twee van die drie events kunnen dezelfde timestamp delen. Sorteren op dat veld ordent ze niet, en de aflevervolgorde evenmin, die door retries onbetrouwbaar is. Handel op de reactie die elk event bevat, als de wijziging die het beschrijft. Reconstrueer de volgorde niet uit de events en behandel het laatst aangekomen event niet als de huidige reactie op het bericht, want noch de tijdstempels noch de aankomstvolgorde ondersteunen dat. Lees het bericht terug voor de reacties die staan: GET /v1/whatsapp/messages/{message_id} retourneert één vermelding per afzender in reactions, en het reactielog van het bericht bevat elke wijziging.
emoji is aanwezig en null wanneer de gebruiker de reactie terugnam, dus een null is de verwijdering zelf en geen ontbrekende waarde. De emoji wordt precies afgeleverd zoals WhatsApp het verzond en wordt niet genormaliseerd, dus en ❤️ bereiken je als verschillende strings.

Vervolgstappen