Sign inGet started

Eventi WhatsApp

Bird registra eventi per i messaggi WhatsApp in entrata e in uscita. Una timeline in uscita mostra cosa è successo dopo che un invio ha restituito 202: accettazione, passaggio a WhatsApp, consegna, lettura o errore. Una timeline in entrata registra quando Bird ha ricevuto il messaggio.

L'envelope dell'evento

Gli eventi pubblici di consegna in uscita usano l'envelope webhook standard: un type, un timestamp e un oggetto data specifico per tipo.
Esempio di codice
{
  "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"
}
Ogni payload webhook WhatsApp pubblico per un messaggio contiene whatsapp_id, workspace_id, direction, from, to, tags e metadata. whatsapp.reacted è l'eccezione, perché una reazione è un'annotazione su un messaggio e non un messaggio a sé stante; la sezione Reazioni qui sotto ne descrive la struttura. Un indirizzo può includere un phone_number E.164, un ID utente Meta con scope business in bsuid, o entrambi. Un messaggio ricevuto da un utente WhatsApp contiene anche il profilo che pubblica, in username e display_name. tags e metadata sono null quando l'invio non ne conteneva. Un messaggio inviato in risposta contiene anche in_reply_to_message_id su ogni evento in uscita nella sua timeline, da whatsapp.accepted fino a whatsapp.read, whatsapp.failed o whatsapp.rejected, indicando il messaggio a cui risponde.
L'API degli eventi restituisce record di timeline più snelli con un id, type e un timestamp occurred_at. L'ID del messaggio è già nell'URL della richiesta.

Eventi del ciclo di vita

Gli eventi appaiono in ordine cronologico. Un messaggio in uscita può fermarsi a whatsapp.failed o whatsapp.rejected, e whatsapp.read appare solo se il destinatario apre il messaggio. Un messaggio in entrata ha un singolo evento di timeline whatsapp.received.
EventoSignificato
whatsapp.acceptedBird ha accettato la richiesta di invio. È ciò che l'202 ha riportato.
whatsapp.sentBird ha passato il messaggio alla rete WhatsApp.
whatsapp.deliveredWhatsApp ha confermato la consegna al dispositivo del destinatario.
whatsapp.readIl destinatario ha aperto il messaggio.
whatsapp.failedIl messaggio non è stato consegnato. error.code indica cosa lo ha bloccato.
whatsapp.rejectedBird ha rifiutato il messaggio prima di inviarlo. Non è stato addebitato.
whatsapp.receivedBird ha ricevuto un messaggio in entrata da un contatto.
whatsapp.delivered è anche il momento in cui viene addebitata la quota Meta del prezzo del messaggio, anche se l'evento non lo riporta: i payload degli eventi WhatsApp non contengono costi. Rileggi il messaggio con GET /v1/whatsapp/messages/{message_id} per vedere quanto è costato. Vedi Costi e fatturazione.
whatsapp.read non cambia il status del messaggio. Un messaggio consegnato resta delivered; il messaggio registra anche la lettura in read_at.
whatsapp.delivered può essere saltato del tutto. Quando il destinatario ha già la chat aperta sul suo dispositivo, Meta riporta la lettura senza mai riportare una consegna, quindi la timeline risulta whatsapp.acceptedwhatsapp.sentwhatsapp.read senza whatsapp.delivered nel mezzo. Tratta read come prova di consegna: un consumer che aspetta delivered prima di considerare il messaggio arrivato si bloccherà esattamente sui destinatari che lo hanno visto più in fretta, e uno che calcola un tasso di consegna dal solo delivered lo sottostima. Il status del messaggio resta sent in questo caso, perché solo una ricevuta di consegna lo fa avanzare.
Il salto ha anche una conseguenza sulla fatturazione: la quota Meta del prezzo viene addebitata sulla ricevuta delivered, quindi un messaggio letto in questo modo non ha passthrough_amount. Vedi Costi e fatturazione.
La lista dei tipi di evento è aperta: nuovi tipi possono essere aggiunti nel tempo, quindi tratta un valore non riconosciuto come un evento futuro e non come un errore.

Eventi di errore

whatsapp.failed e whatsapp.rejected sono terminali. Un rifiuto significa che Bird ha bloccato il messaggio prima di inviarlo a WhatsApp, quindi non è stato addebitato. Le cause includono un destinatario soppresso o che ha negato il consenso, saldo del wallet insufficiente o una destinazione senza prezzo configurato. Un errore significa che il messaggio non è stato consegnato, e error.code indica chi lo ha deciso. La maggior parte dei codici riporta il verdetto di WhatsApp, mappato dal codice che ha restituito. internal_error è l'eccezione: significa che il messaggio non ha mai raggiunto WhatsApp, perché il numero mittente non aveva credenziali utilizzabili oppure perché Bird ha riprovato l'invio fino a rinunciarvi. meta_error_code contiene il codice di WhatsApp quando disponibile, e un errore internal_error non ne ha per definizione.
Entrambi gli eventi contengono un oggetto error con un Bird code stabile, un description leggibile, un meta_error_code opzionale e occurred_at. L'oggetto appare nei record API e nei payload webhook solo per questi tipi di evento.

Eventi di reazione

Una reazione emoji è un'annotazione su un messaggio e non un messaggio a sé stante, quindi non appare in nessuna delle due timeline. Una reazione non genera alcun evento whatsapp.*, una in entrata non attiva alcun webhook whatsapp.received, e nessun webhook contiene una reazione. Ogni modifica viene invece registrata nel log delle reazioni del messaggio a cui si riferisce: ogni emoji inserita, ogni sostituzione con un'emoji diversa ed ogni rimozione, sia da parte del contatto sia dal tuo numero aziendale.
Un caso non viene registrato da nessuna parte. Bird associa una reazione al suo messaggio tramite un ID del provider che conserva per 15 giorni, mentre WhatsApp accetta una reazione su un messaggio vecchio fino a 30 giorni, quindi una reazione su un messaggio più vecchio non può essere associata e non raggiunge né il log né reactions. Un messaggio senza voci non è perciò prova che nessuno abbia reagito.
Leggi quel log con GET /v1/whatsapp/messages/{message_id}/reaction-events, dal più recente. Una voce riporta l'emoji, chi ha effettuato la modifica e un status di received, sent, failed o rejected; una voce failed o rejected riporta il motivo su error. Una reazione non viene mai addebitata, quindi nessun errore in questo contesto è un problema di fatturazione. Per sapere cosa è attualmente presente sul messaggio anziché lo storico delle modifiche, leggi il suo reactions con GET /v1/whatsapp/messages/{message_id}, che riduce il log a una voce per mittente.

Eventi di soppressione

Oltre al ciclo di vita del singolo messaggio, un evento segnala una modifica alla lista di soppressione dello spazio di lavoro: whatsapp_suppression.created si attiva quando si apre una soppressione. Il payload contiene il suppression_id, il address soppresso in formato E.164, il waba a cui il blocco è limitato (null quando copre l'intero spazio di lavoro, qualunque account invii), il reason e il workspace_id, così il tuo sistema può vedere i nuovi blocchi senza polling. Solo le aperture generano un evento: la chiusura di una soppressione non lo fa ancora, quindi rileggi la lista prima di considerare ancora attivo un blocco replicato:
Esempio di codice
{
  "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"
  }
}
Un opt-out dichiarato dal destinatario stesso è una preferenza e non una soppressione, e attiva preference.revoked al suo posto.

Leggere gli eventi dall'API

GET /v1/whatsapp/messages/{message_id}/events restituisce la timeline in ordine cronologico. La lista limitata non è paginata. La lettura degli eventi richiede una chiave API con whatsapp:read:
const { data } = await bird.whatsapp.listEvents("wa_abc123");
for (const event of data) console.log(event.type, event.occurred_at);
Un messaggio accettato, inviato, consegnato e letto restituisce quattro eventi:
Esempio di codice
{
  "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"
    }
  ]
}
Passa type per ottenere un solo tipo di evento pubblico esatto, ad esempio ?type=whatsapp.failed o ?type=whatsapp.read. Omettilo per la timeline completa.
La stessa timeline è ciò che la pagina log WhatsApp visualizza quando apri un messaggio.
Il pannello dettaglio messaggio WhatsApp nella dashboard Bird, aperto per un messaggio bird_order_confirmation consegnato: la scheda Eventi che mostra la timeline del ciclo di vita del messaggio con Accepted, Sent, Delivered e Read, ciascuno con il proprio timestamp, sopra la lista messaggi oscurata

Webhook

Iscriviti a whatsapp.accepted, whatsapp.sent, whatsapp.delivered, whatsapp.read, whatsapp.failed, whatsapp.rejected, whatsapp.received e whatsapp.reacted dalla pagina Webhooks o dall'API dei webhook. La guida ai webhook copre endpoint, firme e ripetizioni.
whatsapp.received contiene il contenuto del messaggio oltre all'envelope descritto sopra, così un endpoint può agire su un messaggio in entrata senza doverlo rileggere. Un tap su un messaggio interattivo arriva come interactive_reply, e in_reply_to_message_id indica il messaggio a cui risponde:
Esempio di codice
{
  "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"
}
Gli altri rami di contenuto seguono la stessa struttura uno-tra-questi: text, image, video, audio, sticker, document, location, contact_cards e unsupported per un tipo che API non modella. GET /v1/whatsapp/messages/{message_id} documenta ciascuno di essi.

Reazioni

whatsapp.reacted si attiva quando un utente WhatsApp reagisce a uno dei tuoi messaggi. È l'unico evento WhatsApp che non fa parte della timeline di consegna di un messaggio: non appare in GET /v1/whatsapp/messages/{message_id}/events, e non c'è nulla per filtrarlo lì.
whatsapp_id indica il messaggio a cui è stata aggiunta la reazione, non la reazione stessa, e emoji è la modifica effettuata dall'utente. Un utente che reagisce, cambia la sua emoji e poi ritira la reazione produce tre eventi su quel singolo messaggio. WhatsApp non invia una rimozione tra i primi due, quindi un cambio arriva come un singolo evento con la nuova emoji.
Esempio di codice
{
  "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 riporta l'ora della reazione al secondo, quindi due di quei tre eventi possono condividere lo stesso timestamp. Ordinarli per quel valore non li metterà in sequenza, e neppure l'ordine di consegna, che i tentativi ripetuti rendono inaffidabile. Agisci sulla reazione che ogni evento contiene, come la modifica che descrive. Non ricostruire la sequenza dagli eventi e non trattare l'ultimo arrivato come la reazione attuale del messaggio, perché né i timestamp né l'ordine di arrivo lo supportano. Rileggi il messaggio per le reazioni attuali: GET /v1/whatsapp/messages/{message_id} restituisce una voce per mittente in reactions, e il log delle reazioni del messaggio contiene ogni modifica.
emoji è presente e null quando l'utente ha ritirato la sua reazione, quindi un null è la rimozione stessa e non un valore mancante. L'emoji viene consegnata esattamente come WhatsApp l'ha inviata e non è normalizzata, quindi e ❤️ ti arrivano come stringhe diverse.

Prossimi passi