Documentação / Webhooks (eventos e payloads)

Webhooks (eventos e payloads)

Entrar

Webhooks (eventos e payloads)

O Pilot Status permite configurar um webhook por ambiente (TEST/LIVE) e vincular esse webhook a uma API key. Quando eventos ocorrerem, o Pilot Status faz um POST JSON para a URL configurada e registra o resultado (status/resposta/duração) nos logs do painel.

Eventos

Identificadores

  • messageId: ID da mensagem na Evolution API/WhatsApp (ex.: key.id). Pode ser null em falhas que acontecem antes da Evolution retornar um ID.
  • internalMessageId: ID interno da mensagem no Pilot Status (sempre presente nos eventos de status outbound).

message.sent

Disparado quando a mensagem é enviada para o provedor.

Payload:

{
  "event": "message.sent",
  "data": {
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "internalMessageId": "cmm04obm46zz0qv4ycjp8x6r2",
    "environment": "TEST",
    "destinationNumber": "+5511999999999",
    "content": "texto enviado",
    "status": "SENT",
    "sentAt": "2026-02-24T15:00:05.000Z"
  }
}

message.delivered

Disparado quando a mensagem é entregue no dispositivo do destinatário (ex.: status DELIVERY_ACK do provedor).

Payload:

{
  "event": "message.delivered",
  "data": {
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "internalMessageId": "cmm04obm46zz0qv4ycjp8x6r2",
    "environment": "TEST",
    "destinationNumber": "+5511999999999",
    "content": "texto enviado",
    "status": "DELIVERED",
    "deliveredAt": "2026-02-24T15:00:05.000Z"
  }
}

message.failed

Disparado quando o envio falha (ex.: erro do provedor). Pode ocorrer tanto durante o processamento de envio quanto via atualização de status do provedor.

Payload:

{
  "event": "message.failed",
  "data": {
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "internalMessageId": "cmm04obm46zz0qv4ycjp8x6r2",
    "environment": "TEST",
    "destinationNumber": "+5511999999999",
    "content": "texto enviado",
    "status": "FAILED",
    "failedAt": "2026-02-24T15:00:05.000Z",
    "errorMessage": "Falha ao enviar mensagem pela Pilot Status."
  }
}

message.read

Disparado quando a mensagem é marcada como lida pelo provedor (ex.: status READ/PLAYED).

Payload:

{
  "event": "message.read",
  "data": {
    "messageId": "A52298BB1619CB5EC464BEFB8A3ACB94",
    "internalMessageId": "cmm04obm46zz0qv4ycjp8x6r2",
    "environment": "TEST",
    "destinationNumber": "+5511999999999",
    "content": "texto enviado",
    "status": "READ",
    "readAt": "2026-02-24T15:00:05.000Z"
  }
}

message.received

Disparado quando o sistema recebe uma mensagem (via provedor).

Observação:

  • Mensagens recebidas em grupos (remoteJid com @g.us no provedor) são entregues como message.group (não como message.received).

Payload:

{
  "event": "message.received",
  "data": {
    "fromMe": false,
    "from": "5511999999999",
    "destinationNumber": "5511888888888",
    "content": "Oi",
    "receivedAt": "2026-02-24T10:30:00Z",
    "messageId": "msg_in_id",
    "environment": "LIVE",
    "whatsappInstanceName": "Cliente 1"
  }
}

message.group

Disparado quando o sistema recebe uma mensagem em um grupo.

Payload:

{
  "event": "message.group",
  "data": {
    "fromNumber": "5511999999999",
    "fromName": "Nome no WhatsApp",
    "fromMe": false,
    "groupName": "Meu Grupo",
    "content": "Mensagem no grupo",
    "environment": "LIVE",
    "messageId": "msg_in_id"
  }
}

message.reply

Disparado quando um usuário responde a uma mensagem enviada pela plataforma, seja respondendo via texto tradicional, seja clicando em um botão interativo de resposta rápida.

O sistema automaticamente correlaciona a resposta com a mensagem original. Quando o usuário clica em um botão, o ID do botão clicado é retornado no campo buttonId.

Payload:

{
  "event": "message.reply",
  "data": {
    "from": "5511999999999",
    "destinationNumber": "5511999999999",
    "content": "Olá, tudo bem? Confirma seu agendamento?",
    "replyContent": "Sim, confirmo",
    "receivedAt": "2024-02-18T10:30:00Z",
    "messageId": "msg_12345",
    "quotedMessageId": "msg_original_123",
    "messageRepliedId": "cmm04obm46zz0qv4ycjp8x6r2",
    "environment": "LIVE",
    "buttonId": "btn_confirm_123"
  }
}

Nota: buttonId só estará presente se a resposta foi originada de um clique em botão. Respostas de texto convencionais não possuirão este campo.

optin.created

Disparado quando o Pilot Status registra um opt-in (consentimento) para um destino em um projeto (ex.: quando o usuário envia optin <projectId> para o número do Pilot Status; a palavra optin é reconhecida sem distinção de maiúsculas/minúsculas).

Payload:

{
  "event": "optin.created",
  "data": {
    "projectId": "proj_abc",
    "environment": "TEST",
    "whatsappInstanceName": "Pilot Status",
    "destinationNumber": "+5511999999999",
    "destinationHash": "8c1f...sha256...",
    "fromName": "Nome no WhatsApp",
    "isFirstOptIn": true,
    "firstOptInAt": "2026-02-24T15:00:05.000Z",
    "lastSeenAt": "2026-02-24T15:00:05.000Z"
  }
}

Observações:

  • O campo fromName é o nome de exibição do WhatsApp do contato que fez o opt-in, quando disponível: primeiro usa o pushName enviado pelo provedor na mensagem recebida; se vier vazio, o Pilot Status tenta obter o nome via Evolution POST /chat/whatsappNumbers/{instance} (mesmo fluxo usado na verificação de números). Se não houver nome ou a retenção de PII exigir ocultar dados, o valor é null.
  • Para números móveis brasileiros (+55…), o sistema pode disparar mais de um POST com optin.created para o mesmo webhook na mesma ação: um payload por variante com e sem o 9º dígito após o DDD (ex.: +5511999999999 e +551199999999), cada um com seu próprio destinationHash. O fromName costuma ser o mesmo nos dois envios. Trate como idempotente por destinationHash (e/ou por par destinationNumber + destinationHash).
  • O evento é disparado para todos os webhooks ativos do tenant do projeto (optInProject.tenantId) que tenham optin.created selecionado, em TEST e LIVEnão é obrigatório o webhook estar ligado a uma API key. O campo projectId no payload identifica qual projeto registrou o opt-in.
  • Webhooks com assinatura por número/instância WhatsApp só recebem o evento se a instância que recebeu a mensagem estiver entre as assinadas (ou se o webhook estiver configurado para “todos os números”).
  • O campo environment no data espelha o ambiente do webhook (TEST ou LIVE), não o ambiente do registro de opt-in no banco.
  • Quando houver API key associada ao mesmo webhook, a retenção (retentionDays) da key pode influenciar redação de PII; sem key, usa-se o padrão do produto.
  • Se a API key tiver retenção retentionDays <= 0, o destinationNumber é enviado em branco e somente destinationHash é incluído (em cada um dos envios, quando houver variantes BR); nesse caso fromName também vem como null.

Observações

  • O webhook é disparado quando estiver ativo e com o evento permitido na lista events (ou cache equivalente).
  • O sistema faz tentativa única por evento e registra o resultado nos logs (não há retry/DLQ no MVP).