# Entrega e retentativas

## Como a entrega chega

Cada evento é enviado como um `POST` HTTP com o corpo em JSON:

```http
POST /seu-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Quyta-Webhooks
Quyta-Webhook-Signature: 6b3a...c91f

{"id":"550e8400-...","type":"contract.created","version":"1","created_at":"...","data":{...}}
```

| Header | Descrição |
| --- | --- |
| `Content-Type` | Sempre `application/json`. |
| `User-Agent` | Sempre `Quyta-Webhooks`. Útil para filtrar em logs e no WAF. |
| `Quyta-Webhook-Signature` | HMAC SHA-256 do corpo. Presente **apenas** se a assinatura tiver um segredo configurado. Veja [Segurança](/webhooks/seguranca). |

## O que conta como sucesso

A entrega é considerada bem-sucedida **somente** se o seu endpoint responder um status
HTTP na faixa **200–299**.

Qualquer outra coisa é falha e entra em retentativa:

- `3xx` — **redirecionamentos não são seguidos.** Se a sua URL mudou, atualize a
  assinatura no painel; um `301` apontando para o novo endereço não resolve.
- `4xx` — inclusive `401` e `404`. Uma URL errada falha em todas as tentativas e é
  descartada ao fim delas.
- `5xx` — erro no seu servidor.
- Timeout, DNS que não resolve, TLS inválido, conexão recusada.

## Timeout

A Quyta aguarda **30 segundos** por resposta. Passou disso, a entrega é marcada como falha
e será retentada.

Trinta segundos parece bastante, mas é um teto, não um alvo. Responda em milissegundos:
valide a assinatura, enfileire o evento, devolva `200`. Deixe o processamento pesado —
gravar no ERP, chamar outros serviços — para fora do ciclo da requisição.

```javascript
app.post("/webhooks/quyta", express.raw({ type: "application/json" }), (req, res) => {
  if (!assinaturaValida(req.body, req.get("Quyta-Webhook-Signature"))) {
    return res.sendStatus(401);
  }
  fila.publicar(req.body.toString("utf8")); // rápido
  res.sendStatus(200);                       // responde já
});
```

## Retentativas

Uma entrega que falha é tentada novamente até **5 vezes no total**, com esperas crescentes
entre as tentativas:

| Tentativa | Espera desde a anterior | Tempo acumulado |
| --- | --- | --- |
| 1ª | — | imediata |
| 2ª | 5 segundos | ~5 s |
| 3ª | 10 segundos | ~15 s |
| 4ª | 30 segundos | ~45 s |
| 5ª | 60 segundos | ~1 min 45 s |

Esgotadas as cinco tentativas, o evento é **descartado** — não há retentativa manual
automática nem fila de eventos mortos acessível pela API. Uma indisponibilidade do seu
endpoint acima de cerca de dois minutos custa o evento.

:::warning
Essa janela é curta. Se o seu receptor ficar fora do ar por uma manutenção de dez minutos,
tudo que ocorrer nesse intervalo se perde. Duas defesas:

- **Mantenha o receptor separado** do resto da aplicação — um endpoint que só valida e
  enfileira quase não tem motivo para cair junto com o sistema principal.
- **Reconcilie periodicamente.** Uma varredura diária de `GET /debts` conferindo status
  contra a sua base recupera o que tiver escapado. Veja
  [Sincronizando a carteira](/guias/sincronizacao).
:::

## Entregas repetidas

Uma retentativa pode duplicar um evento que, na verdade, chegou — o caso clássico é o seu
endpoint processar tudo corretamente e só então estourar o timeout ao responder.

Por isso, **trate o processamento como idempotente**. O campo `id` do payload é um UUID
único por evento; guarde os já processados e descarte repetições:

```javascript
async function processar(evento) {
  const novo = await db.eventosQuyta.insertIgnore({ id: evento.id });
  if (!novo) return; // já processado

  switch (evento.type) {
    case "contract.installment.paid":
      await darBaixa(evento.data.installment_paid);
      break;
    // ...
  }
}
```

Sem essa proteção, uma retentativa pode virar uma baixa contábil em dobro.

## Acompanhando as entregas

O painel da Quyta mostra o histórico de cada assinatura: evento enviado, horário, código
de resposta do seu endpoint e tempo de resposta. É o primeiro lugar a olhar quando um
evento não aparece do seu lado — ele responde de imediato se o problema foi de envio ou
de processamento.

## Lista de verificação

Antes de colocar o receptor em produção:

- [ ] URL em **HTTPS**, com certificado válido e acessível publicamente.
- [ ] Responde **2xx** em poucos milissegundos, sem processamento síncrono.
- [ ] **Valida a assinatura** antes de confiar no conteúdo.
- [ ] **Deduplica** pelo `id` do evento.
- [ ] Ignora tipos de evento desconhecidos — sem quebrar — para tolerar eventos novos.
- [ ] Registra o corpo recebido, para permitir reprocessamento manual.
- [ ] Não depende de redirecionamento: a URL cadastrada é a URL final.
