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

# Webhooks

> Eventos enviados pela TESA, formato do payload, verificação de assinatura e reenvio

<Warning>
  Rascunho: especificação em definição. Endpoints e campos podem mudar.
</Warning>

Webhooks avisam a integração quando algo muda no caixa, sem necessidade de consultar a API em intervalos. Cada destino é cadastrado com `POST /webhooks` e recebe um segredo próprio para verificar a assinatura.

## Eventos

| Evento | Quando é enviado |
| - | - |
| `deposito.confirmado` | Um depósito de USDC numa carteira vinculada foi confirmado on-chain |
| `alocacao.proposta` | A TESA criou uma proposta de alocação ou de rebalanceamento, pendente de aprovação |
| `alocacao.aprovada` | A proposta foi aprovada e as transações estão prontas para assinatura no custodiante |
| `resgate.concluido` | O USDC do resgate chegou à carteira de destino, com confirmação on-chain |
| `rendimento.apurado` | O rendimento de um período foi fechado |
| `cobranca.emitida` | A cobrança de 5% sobre o yield do período foi emitida (ou o percentual do plano enterprise) |

A proposta é que o destino possa assinar todos os eventos ou apenas uma lista deles.

## Formato do payload

Todos os eventos têm o mesmo envelope. O campo `dados` traz o recurso no mesmo formato devolvido pela API.

```json Exemplo ilustrativo theme={null}
{
  "id": "evt_7h2k9q",
  "tipo": "alocacao.proposta",
  "criado_em": "2026-09-01T09:30:05Z",
  "ambiente": "producao",
  "organizacao_id": "org_1a2b",
  "dados": {
    "id": "prop_8f2a1c",
    "tipo": "rebalanceamento",
    "status": "pendente",
    "movimentos": [
      { "de": "USDY", "para": "BUIDL", "valor": "42105.50" }
    ]
  }
}
```

## Verificação de assinatura

Cada envio traz o cabeçalho `Tesa-Signature` no formato `t=<timestamp>,v1=<assinatura>`. A assinatura é o HMAC-SHA256, em hexadecimal, da string `<timestamp>.<corpo bruto>` com o segredo do destino.

```javascript Node.js theme={null}
import crypto from "node:crypto";

function verificar(corpoBruto, cabecalho, segredo) {
  const partes = Object.fromEntries(
    cabecalho.split(",").map((p) => p.split("="))
  );
  const esperado = crypto
    .createHmac("sha256", segredo)
    .update(`${partes.t}.${corpoBruto}`)
    .digest("hex");

  const dentroDaJanela = Math.abs(Date.now() / 1000 - Number(partes.t)) < 300;
  const confere = crypto.timingSafeEqual(
    Buffer.from(esperado),
    Buffer.from(partes.v1)
  );
  return dentroDaJanela && confere;
}
```

<Warning>
  Verifique a assinatura sobre o corpo bruto, antes de qualquer parse de JSON, e recuse eventos com mais de 5 minutos.
</Warning>

## Entrega e reenvio

* O destino deve responder com qualquer status `2xx` em até 10 segundos. O processamento pesado deve acontecer depois da resposta.
* Sem `2xx`, a TESA reenvia com espera crescente: 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas e 12 horas. Depois disso o evento fica marcado como falho.
* Eventos falhos podem ser consultados e reenviados pelo painel.
* A entrega é pelo menos uma vez: o mesmo evento pode chegar mais de uma vez. Use o `id` do evento para descartar duplicatas.
* A ordem de chegada não é garantida. Use `criado_em` e o `status` do recurso, ou consulte a API, antes de agir.

<Note>
  Webhook é aviso, não comando. Um evento `alocacao.aprovada` não move fundos sozinho: a movimentação só acontece depois da assinatura no custodiante da empresa.
</Note>
