Pular para o conteúdo principal

Referência de eventos de webhook

Webhooks entregam eventos em tempo real para a URL do seu sistema. Esta página documenta cada evento, seu payload e como correlacionar eventos com suas chamadas de API. Para criar e gerenciar webhooks, consulte Configurar webhooks.

Formato do payload

Todo evento chega como JSON:
{
  "event": "message.sent",
  "data": {}
}
Esquema v3 (camelCase). Todos os campos de data são camelCase. Números de telefone são sempre E.164 com + (sem sufixo de dispositivo, sem @s.whatsapp.net / @lid). Timestamps são sempre ISO 8601 no campo único createdAt (o horário em que o evento ocorreu).

Eventos disponíveis

Saída (entrega/status): message.sent, message.delivered, message.read, message.failed Entrada (mensagens recebidas):
  • message.reply — resposta correlacionada
  • message.received — mensagem recebida no número conectado
  • message.group — mensagem recebida em um grupo
  • message.newsletter — mensagem recebida em um canal (@newsletter)
Ciclo de vida do número:
  • number.created — instância criada no Pilot Status
  • number.connected — conectado ao WhatsApp (estado OPEN)
  • number.disconnected — desconexão confirmada após verificação periódica de saúde (não reflete cada breve flutuação de conectividade)
  • number.removed — instância removida
  • number.recovered — número recuperado de um estado degradado/bloqueado
Eventos de saúde (somente assinatura "*"): os eventos de transição de saúde — number.health_blocked, number.health_degraded, number.health_shadowban — e number.recovered não são assináveis individualmente e não aparecem no seletor de eventos. Eles são entregues somente a webhooks assinados com o curinga "*". Chamadas de voz (WhatsApp Business Calling, consulte Chamadas de Voz): Os eventos de chamada normalizados — call.ringing, call.connected, call.ended, call.missed — estão disponíveis em qualquer número com suporte a chamadas (web nativo (não oficial) — e Meta Cloud API). call.permission_updated e o envelope nativo calls são somente da Meta Cloud API.
  • call.ringing — chamada tocando (UIC de entrada ou transição BIC de saída)
  • call.connected — chamada atendida/conectada
  • call.ended — chamada finalizada (status: COMPLETED | FAILED | REJECTED; duration em segundos quando atendida)
  • call.missed — chamada de entrada encerrada sem ser atendida
  • call.permission_updated — (somente Meta Cloud API) resposta a uma solicitação de permissão de chamada (status: NO_PERMISSION | TEMPORARY | PERMANENT)
  • calls — (somente Meta Cloud API) envelope nativo calls da Meta (bruto entry[].changes[].value, carregando SDPs) para sinalização WebRTC personalizada

Eventos da camada de migração

Se você se conectou pela camada de compatibilidade Evolution GO ou Evolution v2, os eventos também são encaminhados ao seu webhook no formato de evento nativo desse provedor (não no esquema normalizado acima). Consulte as listas de eventos nas páginas de camada Evolution GO e Evolution V2.

Identificadores e correlação

Todo evento de mensagem carrega dois IDs distintos:
  • messageId — ID da mensagem do WhatsApp/provedor (ex.: key.id). Pode ser null para falhas que ocorrem antes de o provedor retornar um ID.
  • id — ID interno da mensagem no Pilot Status. Mesmo valor do id no HTTP 202 de POST /v1/messages/send.
  • numberId — o ID público do número (instância) que tratou o evento — o mesmo id exposto por GET /v1/numbers.
  • correlationId — presente quando o evento se correlaciona a um envio anterior (mesmo valor do correlationId do 202).
  • quotedMessageId — em message.reply, o messageId da mensagem original citada (igual ao messageId do message.sent original).

Correlação com POST /v1/messages/send

Após um envio aceito, a API retorna HTTP 202 com id e correlationId:
Campo do 202Equivalente no webhook
idid em message.sent / message.delivered / message.read / message.failed (mesmo valor).
correlationIdPode se repetir em eventos de status de saída e em message.reply / message.received quando correlacionado ao envio.
(não presente no 202)messageId do WhatsApp — só aparece a partir de message.sent.
Em message.reply: quotedMessageId = o messageId do message.sent original; o próprio messageId da resposta é a nova mensagem de entrada. Use quotedMessageId (e correlationId quando presente) para associar a resposta ao seu envio anterior.

Payload de message.*

Campos comuns

CampoPresençaDescrição
eventsemprenome do evento
fromsempreremetente em E.164
tosempredestinatário em E.164
numberIdsempreID público do número que tratou a mensagem
messageIdsempreID da mensagem do WhatsApp/provedor (null antes de o provedor retornar um)
idsempreID interno do Pilot Status (= id do HTTP 202)
typesempretext, image, audio, video, document, location, contacts, sticker ou reaction
fromMesemprebooleano — true em eventos de saída, false em eventos de entrada
createdAtsempreISO 8601 — horário do evento
contentcondicionaltexto da mensagem (ou legenda)
participantNamecondicionalnome do remetente no WhatsApp (recebida / grupo / canal)
correlationIdcondicionalquando correlacionado a um envio anterior
contentRepliedcondicionaltexto da mensagem citada (somente message.reply)
quotedMessageIdcondicionalmessageId da mensagem citada (somente message.reply)
mediaLinkcondicionalURL da mídia, quando o provedor a expõe
mediaTypecondicionaltipo da mídia
mediaCaptioncondicionallegenda da mídia
mediaFilenamecondicionalnome do arquivo da mídia
groupNamecondicionalnome do grupo (somente message.group)
groupIdcondicionalJID do grupo (somente message.group)
newsletterNamecondicionalnome do canal (somente message.newsletter)
newsletterIdcondicionalJID do canal (somente message.newsletter)
errorcondicionalmensagem de erro (somente message.failed)
errorCodecondicionalcódigo de erro estável (somente message.failed)
Semântica de direção de from/to:
  • Saída (sent/delivered/read/failed): to = número de destino (E.164); from = número próprio (presente quando resolvível de forma barata, caso contrário omitido); fromMe = true.
  • Entrada (received/reply): from = contato/remetente (E.164); to = número próprio (E.164); fromMe = false.
  • Grupo / canal (group/newsletter): from = participante (E.164); to = número próprio; mais groupId/groupName ou newsletterId/newsletterName.

message.sent

{
  "event": "message.sent",
  "data": {
    "to": "+5511999999999",
    "from": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": true,
    "content": "sent text",
    "correlationId": "corr_123",
    "createdAt": "2026-02-24T15:00:05.000Z"
  }
}

message.delivered

{
  "event": "message.delivered",
  "data": {
    "to": "+5511999999999",
    "from": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": true,
    "content": "sent text",
    "createdAt": "2026-02-24T15:00:06.000Z"
  }
}

message.read

message.read só dispara quando o destinatário tem os recibos de leitura do WhatsApp ativados.
{
  "event": "message.read",
  "data": {
    "to": "+5511999999999",
    "from": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": true,
    "content": "sent text",
    "createdAt": "2026-02-24T15:00:10.000Z"
  }
}

message.failed

Inclui error e, quando disponível, um errorCode estável (ex.: DELIVER_NOT_CONFIRMED).
{
  "event": "message.failed",
  "data": {
    "to": "+5511999999999",
    "from": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": null,
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": true,
    "content": "sent text",
    "error": "Failed to send message via Pilot Status.",
    "errorCode": "DELIVER_NOT_CONFIRMED",
    "createdAt": "2026-02-24T15:00:05.000Z"
  }
}

message.received

{
  "event": "message.received",
  "data": {
    "from": "+5511999999999",
    "to": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "msg_in_id",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": false,
    "content": "Hi",
    "participantName": "WhatsApp name",
    "createdAt": "2026-02-24T10:30:00.000Z"
  }
}
Exemplo de mídia (quando o provedor expõe a URL — ex.: Meta Cloud API). Para provedores sem uma URL de mídia pública, mediaLink/mediaType/mediaCaption/mediaFilename podem ser omitidos.
{
  "event": "message.received",
  "data": {
    "from": "+5511999999999",
    "to": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "msg_in_id",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "image",
    "fromMe": false,
    "content": "Check this photo",
    "mediaLink": "https://...",
    "mediaType": "image",
    "mediaCaption": "Check this photo",
    "mediaFilename": "photo.jpg",
    "createdAt": "2026-02-24T10:30:00.000Z"
  }
}

message.reply

O novo texto do contato está em content; o texto da mensagem original citada está em contentReplied. Use quotedMessageId para corresponder ao messageId do seu message.sent de saída original.
{
  "event": "message.reply",
  "data": {
    "from": "+5511999999999",
    "to": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "msg_12345",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": false,
    "content": "Yes, I confirm",
    "contentReplied": "Hi! Do you confirm your appointment?",
    "quotedMessageId": "msg_original_123",
    "correlationId": "corr_123",
    "createdAt": "2026-02-24T10:30:00.000Z"
  }
}

message.group

{
  "event": "message.group",
  "data": {
    "from": "+5511999999999",
    "to": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "msg_in_id",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": false,
    "content": "Message in the group",
    "participantName": "WhatsApp name",
    "groupId": "120363123456789012@g.us",
    "groupName": "My Group",
    "createdAt": "2026-02-24T10:30:00.000Z"
  }
}

message.newsletter

{
  "event": "message.newsletter",
  "data": {
    "from": "+5511999999999",
    "to": "+5511888888888",
    "numberId": "cmm0abc123",
    "messageId": "msg_in_id",
    "id": "cmm04obm46zz0qv4ycjp8x6r2",
    "type": "text",
    "fromMe": false,
    "content": "Text in the channel",
    "participantName": "WhatsApp name",
    "newsletterId": "120363123456789012@newsletter",
    "newsletterName": "My Channel",
    "createdAt": "2026-02-24T10:30:00.000Z"
  }
}

Payload de number.*

Campos para number.created / number.connected / number.disconnected / number.removed / number.recovered (e os eventos number.health_blocked / number.health_degraded / number.health_shadowban):
CampoPresençaDescrição
eventsemprenome do evento
numberIdsempreID público do número (mesmo id de GET /v1/numbers)
phonesemprenúmero em E.164 com +
displayNamesemprenome de exibição do número
createdAtsempreISO 8601 — horário do evento
profileNamecondicionalnome do perfil do WhatsApp, quando disponível
errorcondicionalmensagem de erro (eventos de saúde / falha)
errorCodecondicionalcódigo de erro estável, quando presente

number.created

{
  "event": "number.created",
  "data": {
    "numberId": "cmm0abc123",
    "phone": "+5511999999999",
    "displayName": "Main store",
    "createdAt": "2026-02-24T15:00:05.000Z"
  }
}

number.connected

{
  "event": "number.connected",
  "data": {
    "numberId": "cmm0abc123",
    "phone": "+5511999999999",
    "displayName": "Main store",
    "profileName": "Main Store Official",
    "createdAt": "2026-02-24T15:00:05.000Z"
  }
}

number.disconnected

{
  "event": "number.disconnected",
  "data": {
    "numberId": "cmm0abc123",
    "phone": "+5511999999999",
    "displayName": "Main store",
    "createdAt": "2026-02-24T15:10:00.000Z"
  }
}

number.removed

{
  "event": "number.removed",
  "data": {
    "numberId": "cmm0abc123",
    "phone": "+5511999999999",
    "displayName": "Main store",
    "createdAt": "2026-02-24T15:10:00.000Z"
  }
}

Payload de call.*

Diferente de message.* / number.*, os eventos normalizados call.* são planos (sem o wrapper data):
{
  "event": "call.ended",
  "callId": "call_01HZX...",
  "externalCallId": "wacid.ABGG...",
  "direction": "OUTBOUND",
  "status": "COMPLETED",
  "from": "5511888888888",
  "to": "5511999999999",
  "timestamp": "2026-07-03T15:02:05.000Z",
  "duration": 120
}
CampoPresençaDescrição
eventsemprecall.ringing / call.connected / call.ended / call.missed / call.permission_updated
callIdsempre (null em call.permission_updated)id da chamada no Pilot Status (use com GET /v1/calls/{callId})
externalCallIdcondicionalid da chamada na Meta (wacid...) — correlaciona com o envelope nativo calls
directionsempre (null em call.permission_updated)INBOUND (iniciada pelo usuário) ou OUTBOUND (iniciada pela empresa)
statussemprestatus da chamada (RINGING/ACCEPTED/COMPLETED/FAILED/MISSED/REJECTED) ou status de permissão (NO_PERMISSION/TEMPORARY/PERMANENT)
from / tosempre (anulável)as duas partes (dígitos)
timestampsempreISO 8601 — horário do evento
durationcondicionalduração da chamada em segundos (call.ended, apenas quando atendida)
Consulte o fluxo completo de chamadas.

Notas importantes

  • message.newsletter: newsletterName é o nome de exibição do canal quando disponível; caso contrário, pode ser omitido. O identificador do canal é newsletterId (JID completo ...@newsletter). participantName é o autor da mensagem no canal.
  • Mídia: mediaLink/mediaType/mediaCaption/mediaFilename aparecem somente quando o provedor expõe esses dados (ex.: Meta Cloud API). Para provedores sem uma URL de mídia pública, são omitidos.
  • Eventos number.* não são correlacionados a POST /v1/messages/send; use-os para provisionamento/monitoramento. number.disconnected é emitido após a verificação de saúde confirmar a desconexão, não a cada oscilação de conexão.
  • Payloads de webhook do cliente não incluem campos internos como lastMessageId.
  • A entrega de campos sensíveis pode depender da configuração de retenção. Com a retenção desligada, campos condicionais como content podem ficar vazios; IDs e timestamps continuam existindo.
  • O evento message.read (e o status Read na API/Logs) só ocorre quando o destinatário tem os recibos de leitura do WhatsApp ativados.