Skip to content

Webhooks ​

Webhooks avisam o seu sistema na hora em que algo acontece no WhatsApp da empresa.

Cadastrar ​

No painel, Integrações → Novo webhook (ou POST /api/v1/webhooks com uma chave webhooks:manage): informe uma URL https:// pública e escolha os eventos. Guarde o segredo (whsec_...) mostrado — ele aparece uma vez e serve para verificar a assinatura.

Os eventos levam o texto das mensagens

Aponte webhooks só para sistemas da sua empresa. Endereços privados (localhost, 10.x, 192.168.x…) são recusados.

Eventos ​

EventoQuando
message.receivedUm cliente mandou mensagem
message.sentSaiu uma mensagem da empresa (painel, robô, API, MCP ou o próprio celular)
message.status_updatedAPI oficial: uma mensagem enviada foi entregue (delivered), lida (read) ou falhou (failed)
conversation.updatedMudou o responsável ou o status da conversa (open, pending, resolved)
conversation.ratedO cliente respondeu a pesquisa de satisfação (score de 1 a 5)
connection.updatedO WhatsApp conectou, desconectou ou está conectando
conversation.handoff_requestedO robô passou a conversa para a equipe: o cliente pediu uma pessoa ou o robô não soube responder

Formato ​

http
POST /seu-endpoint
Content-Type: application/json
User-Agent: FaliaWebhooks/0.3.0
X-Proton-Event: message.received
X-Proton-Signature: t=1730000000000,v1=5f2b...
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-a3ce929d0e0e4736-01

{
  "id": "evt_8c1d...",
  "type": "message.received",
  "created_at": 1730000000000,
  "company_id": 7,
  "data": { "id": "3EB0...", "conversation_id": "5511999990000@s.whatsapp.net", "from_me": false, "text": "Quero um orçamento", "sent_at": 1730000000000 }
}

connection.updated traz data: { "state": "open" | "close" | "connecting" }.

message.status_updated traz data: { "id": "wamid...", "conversation_id": "5511999990000@s.whatsapp.net", "status": "failed", "error": "131047: Re-engagement message" } (error é null quando não falhou). Só existe para empresas na API oficial.

conversation.handoff_requested traz data: { "conversation_id": "5511999990000@s.whatsapp.net", "reason": "cliente_pediu" | "robo_nao_soube" }. O robô fica calado nessa conversa até alguém clicar em Retomar robô no painel.

Responda 2xx em até 10 s (processe depois, numa fila). Se falhar, tentamos de novo em 1 min, 5 min, 30 min, 2 h e 12 h; depois a entrega fica como falhou e pode ser reenviada no painel (Integrações → Entregas → Reenviar). A entrega é pelo menos uma vez: use o id do evento para não processar duas vezes. Registre o traceparent nos seus logs.

Verificar a assinatura ​

Calcule HMAC-SHA256(segredo, "<t>.<corpo bruto>") em hexadecimal e compare com v1. Rejeite se t tiver mais de 5 minutos.

js
import crypto from 'node:crypto'

export function verify(secret, header, rawBody) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  if (Math.abs(Date.now() - Number(t)) > 5 * 60_000) return false
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}
python
import hashlib, hmac, time

def verify(secret: str, header: str, raw_body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() * 1000 - int(parts["t"])) > 5 * 60_000:
        return False
    msg = parts["t"].encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
apex
public static Boolean verify(String secret, String header, String rawBody) {
    Map<String, String> p = new Map<String, String>();
    for (String kv : header.split(',')) { p.put(kv.substringBefore('='), kv.substringAfter('=')); }
    Long t = Long.valueOf(p.get('t'));
    if (Math.abs(System.currentTimeMillis() - t) > 5 * 60 * 1000) return false;
    Blob mac = Crypto.generateMac('hmacSHA256', Blob.valueOf(p.get('t') + '.' + rawBody), Blob.valueOf(secret));
    return EncodingUtil.convertToHex(mac) == p.get('v1');
}
php
function verify(string $secret, string $header, string $rawBody): bool {
    parse_str(str_replace(',', '&', $header), $p);
    if (abs(round(microtime(true) * 1000) - (int)$p['t']) > 300000) return false;
    $expected = hash_hmac('sha256', $p['t'] . '.' . $rawBody, $secret);
    return hash_equals($expected, $p['v1']);
}

Falia é um produto Proton Code Studio