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

# Webhooks

> Receba eventos de delivery com assinatura HMAC.

# Webhooks

Cadastre um URL HTTPS para receber eventos de status das mensagens enviadas pela API.

Scope necessário: `webhooks:manage`.

## Eventos suportados

| Evento              | Quando                   |
| ------------------- | ------------------------ |
| `message.sent`      | Aceito pelo provedor     |
| `message.delivered` | Entregue no aparelho     |
| `message.read`      | Lido (quando disponível) |
| `message.failed`    | Falha de envio/delivery  |

`message.queued` e `message.inbound` ainda não estão disponíveis no cadastro.

## CRUD

| Método   | Path                           |
| -------- | ------------------------------ |
| `POST`   | `/api/public/v1/webhooks`      |
| `GET`    | `/api/public/v1/webhooks`      |
| `PATCH`  | `/api/public/v1/webhooks/{id}` |
| `DELETE` | `/api/public/v1/webhooks/{id}` |

### Criar

```bash theme={null}
curl -sS -X POST https://api.aideskbr.com/api/public/v1/webhooks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/aidesk",
    "event_types": ["message.delivered", "message.read", "message.failed"]
  }'
```

Guarde o `secret` (`whsec_...`) — ele **não** volta no GET.

No `PATCH`, `event_types` é obrigatório e **substitui** a lista inteira. `url` é opcional. O secret não é regenerado.

## Payload

```json theme={null}
{
  "event_type": "message.delivered",
  "event_id": "uuid-estavel-no-retry",
  "message_id": "uuid",
  "client_message_id": "seu-id",
  "delivery_state": "delivered",
  "channel_id": "uuid",
  "thread_id": "uuid",
  "contact_id": "uuid",
  "occurred_at": "2026-07-22T15:00:00.000Z",
  "sequence": 3
}
```

* `sequence` — monotônico por mensagem; **maior venceu** se chegar fora de ordem.
* `event_id` — estável entre retries; use para dedupe no seu lado.
* Latência típica v1: até \~1 minuto.

## Assinatura

Headers em cada POST:

| Header               | Valor                                  |
| -------------------- | -------------------------------------- |
| `X-Aidesk-Signature` | `sha256=<hex HMAC-SHA256 do body raw>` |
| `X-Aidesk-Timestamp` | Unix seconds                           |

Use o body **raw** (antes do `JSON.parse`).

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

function verifyAideskWebhook(req, secret, { maxDriftSec = 300 } = {}) {
  const ts = Number(req.headers['x-aidesk-timestamp']);
  const sig = String(req.headers['x-aidesk-signature'] || '');
  if (!Number.isFinite(ts)) return false;
  if (Math.abs(Date.now() / 1000 - ts) > maxDriftSec) return false;

  const rawBody = req.rawBody; // Buffer/string bruto
  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(sig);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

Em Express:

```js theme={null}
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf;
  },
}));
```

## Segurança da URL

* Apenas `https`
* Bloqueio de IPs privados, loopback, link-local e metadata (anti-SSRF)
* A mesma checagem roda de novo antes de cada entrega (mitiga DNS rebinding)

Veja também o [API Reference](/api-reference/create-webhook) no playground.
