Sign inGet started

Eventos WhatsApp

Bird registra eventos para mensagens WhatsApp de entrada e de saída. Uma linha do tempo de saída mostra o que aconteceu depois que um envio retornou 202: aceitação, repasse para WhatsApp, entrega, leitura ou falha. Uma linha do tempo de entrada registra quando Bird recebeu a mensagem.

O envelope de evento

Eventos de entrega, mensagem recebida e reação WhatsApp usam o envelope de webhook padrão: um type, um timestamp e um objeto data específico do tipo.
Exemplo de código
{
  "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"
}
Cada payload público de webhook WhatsApp para uma mensagem traz whatsapp_id, workspace_id, direction, from, to, tags e metadata. whatsapp.reacted é a exceção, porque uma reação é uma anotação em uma mensagem e não uma mensagem própria; Reações abaixo descreve seu formato. Um endereço pode incluir um phone_number E.164, um ID de usuário com escopo de negócios da Meta em bsuid, ou ambos. Uma mensagem recebida de um usuário WhatsApp também traz o perfil que ele publica, em username e display_name. tags e metadata são null quando o envio não incluiu nenhum. Uma mensagem enviada como resposta também traz in_reply_to_message_id em cada evento de saída na sua linha do tempo, de whatsapp.accepted até whatsapp.read, whatsapp.failed ou whatsapp.rejected, indicando a mensagem que ela responde.
O API de eventos retorna registros de linha do tempo mais enxutos com um id, type e um timestamp occurred_at. O ID da mensagem já está na URL da solicitação.

Eventos de ciclo de vida

Os eventos aparecem em ordem cronológica. Uma mensagem de saída pode parar em whatsapp.failed ou whatsapp.rejected, e o evento whatsapp.read aparece apenas se o destinatário abrir a mensagem. Um timeline de entrada começa com whatsapp.received e pode registrar whatsapp.read depois que o espaço de trabalho marca a mensagem como lida.
EventoSignificado
whatsapp.acceptedBird aceitou a solicitação de envio. É o que o 202 reportou.
whatsapp.sentBird repassou a mensagem para a rede WhatsApp.
whatsapp.deliveredWhatsApp confirmou a entrega no dispositivo do destinatário.
whatsapp.readO destinatário abriu a mensagem.
whatsapp.failedA mensagem não foi entregue. error.code diz o que a impediu.
whatsapp.rejectedBird recusou a mensagem antes de enviá-la. Não houve cobrança.
whatsapp.receivedBird recebeu uma mensagem de entrada de um contato.
Callbacks delivered ou read aplicáveis podem acionar a parte da Meta no preço. Payloads de evento WhatsApp não carregam custo. Leia a mensagem de volta com GET /v1/whatsapp/messages/{message_id} para ver quanto custou. Veja Custo e cobrança.
Marcar uma mensagem recebida como lida registra whatsapp.read no timeline, mas não emite um webhook de confirmação de leitura. A mensagem recebida mantém o status received e registra read_at depois que WhatsApp aceita a confirmação.
whatsapp.read não altera o status da mensagem. Uma mensagem entregue permanece delivered; a mensagem também registra a leitura em read_at.
whatsapp.delivered pode ser totalmente pulado. Quando o destinatário já está com o chat aberto no dispositivo, a Meta reporta a leitura sem jamais reportar uma entrega, então o timeline fica whatsapp.acceptedwhatsapp.sentwhatsapp.read sem whatsapp.delivered entre eles. Trate read como prova de entrega: um consumidor que espera por delivered antes de considerar que a mensagem chegou vai travar justamente nos destinatários que a viram mais rápido, e um que calcula taxa de entrega apenas com delivered a sub-reporta. O status da mensagem permanece sent nesse caso, pois somente um recibo de entrega o avança.
Um callback somente de leitura ainda pode acionar a taxa aplicável da Meta. Bird usa uma mesma identidade de cobrança nos caminhos de entrega e leitura; a ausência do evento de entrega não implica um componente Meta gratuito. Veja Custo e cobrança.
A lista de tipos de evento é aberta: novos tipos podem ser adicionados ao longo do tempo, então trate um valor não reconhecido como um evento futuro em vez de um erro.

Eventos de falha

whatsapp.failed e whatsapp.rejected são terminais. Uma rejeição significa que Bird bloqueou a mensagem antes de enviá-la para WhatsApp, então ela não foi cobrada. As causas incluem um destinatário suprimido ou que optou por não receber, saldo insuficiente na carteira ou um destino sem preço configurado. Uma falha significa que a mensagem não foi entregue, e error.code diz quem decidiu isso. A maioria dos códigos traz o veredito de WhatsApp, mapeado a partir do código que ela reportou. internal_error é a exceção: registra uma credencial de remetente utilizável ausente ou tentativas de processamento esgotadas. Uma tentativa de transporte incerta não prova que a Meta nunca recebeu a solicitação. meta_error_code contém o código de WhatsApp quando disponível, e uma falha internal_error não possui nenhum por definição.
Ambos os eventos contêm um objeto error com um Bird code estável, um description legível, um meta_error_code opcional e occurred_at. O objeto aparece nos registros API e nos payloads de webhook apenas para esses tipos de evento.

Eventos de reação

Uma reação com emoji anota uma mensagem existente. Ela não cria uma mensagem whatsapp.received. Bird emite whatsapp.reacted quando um contato adiciona, altera ou remove uma reação, conforme descrito em Reações. Reações enviadas pelo seu número comercial não emitem esse webhook. Veja Enviando reações para adicionar, substituir ou remover sua reação, e Recebendo reações para exemplos de webhook e REST API. Reações de contatos não abrem uma janela de atendimento ao cliente.
O log de reações da mensagem que recebeu a reação registra alterações feitas tanto pelo contato quanto pelo seu número comercial: adições, substituições e remoções.
Um caso não é registrado em lugar nenhum. Bird associa uma reação à sua mensagem por meio de um ID de provedor que mantém por 15 dias, enquanto WhatsApp aceita uma reação em uma mensagem com até 30 dias, então uma reação feita em uma mensagem mais antiga que isso não pode ser associada e não chega nem ao log nem a reactions. Uma mensagem sem entradas, portanto, não é prova de que ninguém reagiu a ela.
Leia esse log com GET /v1/whatsapp/messages/{message_id}/reaction-events, do mais recente para o mais antigo. Uma entrada indica o emoji, quem fez a alteração e um status de received, sent, failed ou rejected; uma entrada failed ou rejected traz o motivo em error. Cada entrada tem um ID de reação (war_…) e um timestamp occurred_at. Uma remoção tem emoji: null. Alterações pendentes não possuem entrada até que seu resultado seja conhecido. Uma reação nunca é cobrada, então nenhuma falha ali é de cobrança. Para ver o que está atualmente na mensagem em vez do histórico de alterações, leia suas reactions com GET /v1/whatsapp/messages/{message_id}, que reduz o log a uma entrada por remetente.

Eventos de supressão

Além do ciclo de vida por mensagem, um evento reporta uma alteração na lista de supressão do espaço de trabalho: whatsapp_suppression.created dispara quando uma supressão é aberta. O payload traz o suppression_id, o address suprimido em formato E.164, o waba ao qual o bloqueio é limitado (null quando cobre todo o espaço de trabalho, independentemente de qual conta envie), o reason e o workspace_id, para que seu próprio sistema possa ver novos bloqueios sem polling. Apenas aberturas disparam um evento: encerrar uma supressão ainda não dispara, então releia a lista antes de tratar um bloqueio espelhado como ainda vigente:
Exemplo de código
{
  "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"
  }
}
Um opt-out que o próprio destinatário declarou é uma preferência em vez de uma supressão, e dispara preference.revoked no lugar.

Lendo eventos a partir da API

GET /v1/whatsapp/messages/{message_id}/events retorna o timeline em ordem cronológica. A lista limitada não é paginada. Ler eventos requer uma chave API com whatsapp:read:
const { data } = await bird.whatsapp.listEvents("wa_abc123");
for (const event of data) console.log(event.type, event.occurred_at);
Uma mensagem que foi aceita, enviada, entregue e lida retorna quatro eventos:
Exemplo de código
{
  "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"
    }
  ]
}
Passe type para retornar um tipo exato de evento público, como ?type=whatsapp.failed ou ?type=whatsapp.read. Omita para o timeline completo.
Esse mesmo timeline é o que a página do log WhatsApp renderiza quando você abre uma mensagem.
A aba de detalhes de mensagem WhatsApp no dashboard Bird, aberta para uma mensagem bird_order_confirmation entregue: a aba Events mostrando o timeline de ciclo de vida por mensagem de Accepted, Sent, Delivered e Read, cada um com seu timestamp, sobre a lista de mensagens esmaecida

Webhooks

Assine whatsapp.accepted, whatsapp.sent, whatsapp.delivered, whatsapp.read, whatsapp.failed, whatsapp.rejected, whatsapp.received e whatsapp.reacted na página Webhooks ou pela API de webhooks. O Guia de webhooks cobre endpoints, assinaturas e tentativas de reenvio.
whatsapp.received traz o conteúdo da mensagem além do envelope acima, para que um endpoint possa agir sobre uma mensagem recebida sem precisar lê-la novamente. Um toque em uma mensagem interativa chega como interactive_reply, e in_reply_to_message_id indica a mensagem que ela responde:
Exemplo de código
{
  "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"
}
Os outros braços de conteúdo seguem o mesmo formato de um-destes: text, image, video, audio, sticker, document, location, contact_cards e unsupported para um tipo que o API não modela. GET /v1/whatsapp/messages/{message_id} documenta cada um.

Reações

whatsapp.reacted dispara quando um usuário WhatsApp reage a uma de suas mensagens. É o único evento WhatsApp que não faz parte do timeline de entrega de uma mensagem: ele não aparece em GET /v1/whatsapp/messages/{message_id}/events, e não há nada para filtrá-lo ali.
whatsapp_id indica a mensagem que recebeu a reação, não a reação em si, e emoji é a alteração que o usuário fez. Um usuário que reage, muda o emoji e depois remove a reação produz três eventos nessa mesma mensagem. WhatsApp não envia uma remoção entre os dois primeiros, então uma mudança chega como um único evento com o novo emoji.
Exemplo de código
{
  "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 reporta o horário da reação com precisão de segundo, então dois desses três eventos podem compartilhar um mesmo timestamp. Ordená-los por ele não vai sequenciá-los, e a ordem de entrega também não, já que tentativas de reenvio a tornam não confiável. Aja sobre a reação que cada evento traz, como a alteração que ele descreve. Não reconstrua a sequência a partir dos eventos nem trate o último a chegar como a reação vigente da mensagem, porque nem os timestamps nem a ordem de chegada suportam isso. Leia a mensagem de volta para ver as reações vigentes: GET /v1/whatsapp/messages/{message_id} retorna uma entrada por remetente em reactions, e o log de reações da mensagem tem cada alteração.
emoji está presente e é null quando o usuário removeu sua reação, então um null é a remoção em si, não um valor ausente. O emoji é entregue exatamente como WhatsApp o enviou e não é normalizado, então e ❤️ chegam como strings diferentes.

Próximos passos