> ## 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

> Events sent by TESA, payload format, signature verification and retries

<Warning>
  Draft: specification in progress. Endpoints and fields may change.
</Warning>

Webhooks notify your integration when something changes in your cash position, with no need to poll the API. Each destination is registered with `POST /webhooks` and receives its own secret for verifying the signature.

## Events

| Event | When it is sent |
| - | - |
| `deposito.confirmado` | A USDC deposit to a linked wallet was confirmed on-chain |
| `alocacao.proposta` | TESA created an allocation or rebalancing proposal, pending approval |
| `alocacao.aprovada` | The proposal was approved and the transactions are ready for signing by the custodian |
| `resgate.concluido` | The redeemed USDC reached the destination wallet, with on-chain confirmation |
| `rendimento.apurado` | The yield for a period was closed |
| `cobranca.emitida` | The 5% charge on the period's yield was issued (or the enterprise plan percentage) |

The proposal is that a destination can subscribe to all events or to a list of them only.

## Payload format

All events share the same envelope. The `dados` field carries the resource in the same format returned by the API.

```json Illustrative example 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" }
    ]
  }
}
```

## Signature verification

Each delivery includes the `Tesa-Signature` header in the format `t=<timestamp>,v1=<signature>`. The signature is the hex-encoded HMAC-SHA256 of the string `<timestamp>.<raw body>`, using the destination's secret.

```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>
  Verify the signature over the raw body, before any JSON parsing, and reject events older than 5 minutes.
</Warning>

## Delivery and retries

* The destination must respond with any `2xx` status within 10 seconds. Heavy processing should happen after the response.
* Without a `2xx`, TESA retries with increasing backoff: 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours. After that, the event is marked as failed.
* Failed events can be viewed and resent from the dashboard.
* Delivery is at least once: the same event may arrive more than once. Use the event `id` to discard duplicates.
* Arrival order is not guaranteed. Use `criado_em` and the resource `status`, or query the API, before acting.

<Note>
  A webhook is a notification, not a command. An `alocacao.aprovada` event does not move funds by itself: funds move only after signing by the company's custodian.
</Note>
