> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nupapia.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receba mensagens em tempo real, sem precisar de polling

Em vez de ficar consultando a API de tempos em tempos, você pode cadastrar uma URL sua e a Nupapia avisa toda vez que uma mensagem nova acontece numa conversa — do cliente ou do atendente/IA da empresa.

<Note>
  O cadastro do webhook é feito dentro da Nupapia, em **Configurações → Integrações**, junto com o token — não existe endpoint de API pra configurar isso. Cada token de integração tem seu próprio webhook (URL e segredo), então se você tem mais de um sistema integrado, cada um recebe só o que é dele.
</Note>

## O que você recebe

Toda mensagem nova gera um `POST` pra sua URL:

```json theme={null}
{
  "event": "chat.message",
  "data": {
    "message": { "id": 123, "content": "Olá!", "sender_type": "client", "...": "..." },
    "conversation_id": "4821",
    "client_id": "9931",
    "direction": "inbound"
  },
  "timestamp": "2026-08-20T19:28:44+00:00"
}
```

`direction` é `inbound` (mensagem do cliente) ou `outbound` (mensagem do atendente/IA da empresa) — filtre do seu lado se só precisar de um dos dois sentidos.

## Verificando a assinatura

Todo envio vem com o cabeçalho `X-Nupapia-Signature: sha256=<hash>`, calculado sobre o corpo exato da requisição (a string, não o objeto já reinterpretado) usando o segredo gerado quando o webhook foi criado:

```js theme={null}
const crypto = require('crypto');

function isValid(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const received = signatureHeader.replace('sha256=', '');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}
```

<Warning>
  Sempre calcule o hash sobre o corpo bruto da requisição (antes de fazer `JSON.parse`) — reserializar o JSON pode gerar bytes diferentes do que foi assinado, e a verificação falha mesmo com o payload "certo".
</Warning>

## Timeout e reentrega

O envio tem timeout de 10 segundos. Se sua URL não responder (ou responder com erro), essa entrega específica não é reenviada automaticamente — o polling (`GET /chat/conversations?...&updated_at`) continua funcionando como rede de segurança se você precisar reconciliar mensagens perdidas.
