Webhooks
Webhooks permitem que sua aplicação reaja a eventos em tempo real. A Fortbix
envia uma requisição POST para a URL que você cadastrar sempre que um evento
inscrito ocorrer.
Como cadastrar
O cadastro de webhooks não é feito via API com a sua api-key. A api-key serve apenas para autenticar as chamadas aos endpoints da API. Webhooks são gerenciados no Portal do Desenvolvedor, com a sua sessão autenticada.
Acesse o portal e configure seus webhooks:
https://app.fortbix.com/integracao/webhooks
No portal você escolhe o ambiente (Sandbox ou Produção), informa a URL de
callback (HTTPS) e seleciona os eventos desejados. Ao criar o webhook, a Fortbix
gera um signing secret (prefixo whsec_) que é exibido apenas uma vez —
guarde-o em local seguro. É esse valor que você usa para validar a assinatura
das notificações (o WEBHOOK_SECRET dos exemplos abaixo).
Caso perca o secret, use as opções de revelar ou rotacionar secret na própria tela do webhook no portal.
Eventos Disponíveis
Os eventos são organizados por categoria, conforme exibido no portal.
Conta
| Evento | Descrição |
|---|---|
transaction.created | Transação criada |
transaction.processing | Transação em processamento |
transaction.settled | Transação liquidada |
transaction.failed | Falha na transação |
transaction.reversed | Transação estornada |
Garantias
| Evento | Descrição |
|---|---|
collateral.created | Garantia criada |
collateral.liquidated | Garantia liquidada |
collateral.margin_call | Margin call |
collateral.ltv_alert | Alerta de LTV |
collateral.removed | Garantia removida |
Cadastros
| Evento | Descrição |
|---|---|
wallet.registered | Carteira registrada |
wallet.verified | Carteira verificada |
beneficiary.created | Beneficiário criado — reservado, em breve |
beneficiary.approved | Beneficiário aprovado — reservado, em breve |
Cripto
| Evento | Descrição |
|---|---|
crypto.deposit | Depósito de cripto |
Estrutura da Requisição
Cada notificação chega com os seguintes headers:
| Header | Descrição |
|---|---|
X-Webhook-Event-Id | ID único do evento |
X-Webhook-Event-Type | Tipo do evento (ex: transaction.settled) |
X-Webhook-Signature | Assinatura HMAC-SHA256 no formato sha256=<hash> |
X-Webhook-Delivery-Id | ID único desta entrega (útil para idempotência) |
E o corpo (JSON) segue o formato:
{
"eventId": "d5a51404-84d7-4080-bff2-39b3d3935ca7",
"eventType": "transaction.settled",
"occurredAt": "2024-05-12T15:30:00Z",
"environment": "production",
"resource": {
"type": "transaction",
"id": "txn_789"
},
"data": {
"amount": 50000,
"currency": "USD"
}
}
O campo environment indica o ambiente de origem do evento (production ou
sandbox). O conteúdo de resource e data varia conforme o evento — veja a
seção Payload por evento.
Payload por evento
Todos os eventos seguem o mesmo envelope (eventId, eventType, occurredAt,
environment, resource, data). O que muda entre eles é o resource e o
conteúdo de data. Abaixo, o formato de data de cada evento emitido.
Conta — transaction.*
Eventos: transaction.created, transaction.processing, transaction.settled,
transaction.failed, transaction.reversed. Todos compartilham o mesmo formato
(resource.type = "transaction").
{
"eventType": "transaction.settled",
"resource": { "type": "transaction", "id": "txn_789" },
"data": {
"transactionId": "txn_789",
"status": "settled",
"accountId": "acc_123",
"amount": "50000",
"currency": "USD"
}
}
| Campo | Tipo | Descrição |
|---|---|---|
transactionId | string | Identificador da transação |
status | string | Status atual da transação |
accountId | string | Conta global associada |
amount | string | Valor da transação |
currency | string | Moeda (ex: USD, BRL) |
Garantias — collateral.created
Emitido quando uma garantia é criada a partir de um contrato
(resource.type = "collateral").
{
"eventType": "collateral.created",
"resource": { "type": "collateral", "id": "42" },
"data": {
"contractId": 100,
"guaranteeId": 42,
"status": "PENDING",
"requiredFiatAmount": 250000,
"fiatCurrency": "BRL"
}
}
| Campo | Tipo | Descrição |
|---|---|---|
contractId | number | ID do contrato |
guaranteeId | number | ID da garantia |
status | string | Status da garantia |
requiredFiatAmount | number | Valor fiduciário exigido |
fiatCurrency | string | Moeda do valor exigido |
Garantias — collateral.ltv_alert / collateral.liquidated
Emitidos pelo motor de LTV quando o índice atinge o limite de alerta
(collateral.ltv_alert) ou de liquidação (collateral.liquidated). Mesmo
formato de data (resource.type = "collateral", id = ID do contrato).
{
"eventType": "collateral.ltv_alert",
"resource": { "type": "collateral", "id": "100" },
"data": {
"contractId": 100,
"status": "ALERT",
"currentLtv": 0.78,
"alertLtv": 0.75,
"releaseLiquidationLtv": 0.85,
"trigger": "PRICE_CHANGE",
"reason": "LTV acima do limite de alerta"
}
}
| Campo | Tipo | Descrição |
|---|---|---|
contractId | number | ID do contrato |
status | string | Status do LTV (ALERT, RELEASE_LIQUIDATION) |
currentLtv | number | LTV atual (fração, ex: 0.78 = 78%) |
alertLtv | number | Limite de alerta |
releaseLiquidationLtv | number | Limite de liquidação |
trigger | string | Origem do recálculo (ex: PRICE_CHANGE) |
reason | string | Descrição do motivo |
Cadastros — wallet.registered / wallet.verified
wallet.registered é emitido ao cadastrar uma carteira externa;
wallet.verified ao aprová-la. Mesmo formato (resource.type = "wallet").
{
"eventType": "wallet.verified",
"resource": { "type": "wallet", "id": "15" },
"data": {
"walletId": 15,
"address": "0xabc...123",
"network": "ETHEREUM",
"status": "APPROVED",
"fireblocksExternalWalletId": "fb_ext_987"
}
}
| Campo | Tipo | Descrição |
|---|---|---|
walletId | number | ID interno da carteira |
address | string | Endereço on-chain |
network | string | Rede da carteira (ex: ETHEREUM) |
status | string | Status (PENDING, APPROVED) |
fireblocksExternalWalletId | string | ID da carteira externa no provedor |
Cripto — crypto.deposit
Emitido quando um depósito de cripto é confirmado em uma vault de custódia
(resource.type = "transaction").
{
"eventType": "crypto.deposit",
"resource": { "type": "transaction", "id": "555" },
"data": {
"transactionId": 555,
"txHash": "0xdef...456",
"amount": "1.25",
"tokenSymbol": "ETH",
"network": "ETHEREUM",
"fromAddress": "0xfrom...111",
"toAddress": "0xto...222",
"status": "CONFIRMED",
"confirmedAt": "2026-05-31T12:36:35.000Z"
}
}
| Campo | Tipo | Descrição |
|---|---|---|
transactionId | number | ID da transação de depósito |
txHash | string | Hash da transação on-chain |
amount | string | Quantidade depositada |
tokenSymbol | string | Símbolo do token (ex: ETH, USDC) |
network | string | Rede do depósito |
fromAddress | string | Endereço de origem |
toAddress | string | Endereço de destino (vault) |
status | string | Status do depósito |
confirmedAt | string | Data/hora da confirmação (ISO 8601) |
Validar Assinatura
A assinatura é o HMAC-SHA256 do corpo bruto (raw body) da requisição, calculado com o seu signing secret. Compare-a em tempo constante:
const crypto = require('crypto')
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
const received = signatureHeader.split('=')[1]
return crypto.timingSafeEqual(
Buffer.from(received),
Buffer.from(expected)
)
}
// Uso (Express com body bruto)
app.post(
'/webhooks/fortbix',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-webhook-signature']
const rawBody = req.body // Buffer/string bruto, não use JSON.stringify
if (!verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(rawBody)
handleWebhookEvent(event)
res.send('OK')
}
)
O
WEBHOOK_SECRETé o signing secret (whsec_...) que você obteve ao criar o webhook no portal. Ele é diferente da api-key.
Reentrega
Se sua endpoint não responder com sucesso (status fora da faixa 2xx) ou exceder
o timeout de 10 segundos, a entrega é considerada falha e a Fortbix reenvia o
evento automaticamente com novas tentativas. Use o X-Webhook-Delivery-Id e o
X-Webhook-Event-Id para tratar entregas duplicadas de forma idempotente.
Você pode acompanhar e reenviar entregas manualmente pela aba de entregas (deliveries) no portal.
Testar Localmente
Use o ngrok para expor seu localhost e cadastre a URL gerada no portal
(ambiente Sandbox):
ngrok http 3000
# Seu URL: https://abc123.ngrok.io
# Cadastre https://abc123.ngrok.io/webhooks/fortbix em:
# https://app.fortbix.com/integracao/webhooks