Skip to content

Webhooks ​

Register a webhook URL via the API (POST /v1/webhooks) to receive real-time notifications for banking, crypto and OTC events.

Managing Webhooks ​

All routes use your regular OAuth Bearer token. The secret signs every delivery (HMAC-SHA256) and is never returned by the API.

MethodRouteDescription
POST/v1/webhooksRegister a webhook: { "url": "https://...", "events": [...], "secret": "min 32 chars" } → 201 with webhookId
GET/v1/webhooksList your subscriptions (secrets omitted)
PUT/v1/webhooks/{webhookId}Update url, events, secret and/or active
DELETE/v1/webhooks/{webhookId}Unregister — deliveries stop immediately

The url must be HTTPS. events is a non-empty subset of the event types below; each event is delivered to every active subscription that includes its type.

Event Types ​

EventDescription
pix-payment-inIncoming PIX payment received
pix-payment-outOutgoing PIX payment confirmed
pix-reversal-inIncoming PIX reversal received
pix-reversal-outOutgoing PIX reversal confirmed
spb-transfer-inIncoming TED transfer received
spb-transfer-outOutgoing TED transfer confirmed
onboarding-createAccount onboarding status change
onboarding-backgroundcheckBackground check finished — including refusal, with the reason
onboarding-documentscopyKYC documents processed
onboarding-fileKYC file ready for review
onboarding-proposalProposal status updated
onboarding-biometricsBiometric verification URL available/updated
crypto-cash-inOnchain crypto deposit received
otc.trade.settledOTC trade settled (final)
otc.trade.failedOTC trade failed (final)

Delivery Format ​

Each delivery is a POST to your URL. Event metadata travels in the headers; the request body is the event data itself (no envelope):

HeaderDescription
X-Webhook-EventEvent type
X-Webhook-Event-IdUnique event id (UUID) — use for idempotency
X-Webhook-TimestampDispatch time (ISO 8601, UTC)
X-Webhook-SignatureHex HMAC-SHA256 of the raw body, signed with your secret (no sha256= prefix)

Validate the signature on every request (timing-safe compare of the hex digest computed over the exact raw body bytes) and deduplicate by X-Webhook-Event-Id.

Banking Webhook (pix-*, spb-*) ​

Body shape may vary slightly per banking provider; PIX/TED events typically look like:

json
{
  "entity": "pix-payment-in",
  "status": "COMPLETED",
  "body": {
    "transactionId": "E384040820262603101234567890",
    "endToEndId": "E384040820262603101234567890",
    "amount": 150.00,
    "creditParty": {
      "name": "João da Silva",
      "taxId": "48059890093"
    },
    "debitParty": {
      "name": "Maria Souza",
      "taxId": "12345678901"
    }
  }
}

Route events by the X-Webhook-Event header, not by body fields.

Onboarding Webhook ​

json
{
  "entity": "onboarding-create",
  "status": "CONFIRMED",
  "body": {
    "proposalId": "550e8400-e29b-41d4-a716-446655440001",
    "documentNumber": "48059890093",
    "account": { "account": "30054029183", "branch": "0001" }
  }
}
Onboarding StatusDescription
PROCESSINGKYC verification in progress
CONFIRMEDAccount created successfully
ERRORAccount creation failed

Biometrics Webhook (onboarding-biometrics) ​

Sent when the biometric verification URL becomes available or changes — the push counterpart of polling GET /v1/bank-accounts/checkOnboarding (which returns the same biometricUrl). Forward the URL to the account holder to complete the selfie/liveness step.

json
{
  "onboardingId": "550e8400-e29b-41d4-a716-446655440001",
  "accountId": "30054029183",
  "documentNumber": "48059890093",
  "status": "PROCESSING",
  "biometricUrl": "https://kyc.provider.com/biometrics/abc123"
}

The onboardingId format varies by banking provider (UUID on some, other formats on others) — treat it as opaque. To correlate with your records, prefer documentNumber (CPF/CNPJ), which GET /checkOnboarding also accepts.

Background Check Refusal Webhook (onboarding-backgroundcheck) ​

Sent when the banking provider refuses a proposal during background check. This can happen hours after POST /v1/bank-accounts/create returned successfully — the check runs asynchronously.

Refusal is terminal: the onboarding is not re-checked afterwards, so this event is your notification. The same reasons remain available in messages[] from GET /v1/bank-accounts/checkOnboarding.

json
{
  "onboardingId": "550e8400-e29b-41d4-a716-446655440001",
  "accountId": null,
  "documentNumber": "29480535000199",
  "status": "ERROR",
  "messages": [
    {
      "id": "onboarding-backgroundcheck",
      "date": "2026-07-14T01:22:00.000Z",
      "message": "Não conseguimos confirmar algumas das informações da sua proposta, para mais informações entre em contato com o SAC!",
      "status": "proposal-reproved",
      "actionRequired": false
    }
  ]
}
FieldDescription
onboardingIdOnboarding id — treat as opaque; prefer documentNumber to correlate
accountIdBank account id, when already assigned
documentNumberHolder document (CPF/CNPJ) — the stable key to match the onboarding to the account holder
statusAlways ERROR — matches the status returned by GET /checkOnboarding
messagesReasons reported by the provider. messages[].message is the text to show the account holder; actionRequired tells whether they can fix and resubmit; url carries the action link when supplied

Crypto Webhook (crypto-cash-in) ​

Sent when a tenant wallet receives an onchain deposit. txId is the onchain transaction hash — use it for idempotency. The amount is a decimal string (full onchain precision).

json
{
  "address": "0x1122334455667788990011223344556677889900",
  "amount": "1000.00",
  "asset": "USDT",
  "txId": "0xdeadbeef...",
  "chain": "polygon-mainnet",
  "network": "EVM",
  "walletId": "a1b2c3d4-0000-0000-0000-000000000000",
  "type": "token"
}
FieldDescription
addressReceiving wallet address
amountDeposited amount (decimal string, full precision)
assetAsset symbol (e.g. USDT, MATIC)
txIdOnchain transaction hash — use for idempotency
chainProvider chain id (e.g. polygon-mainnet)
networkChain family (EVM | BTC | TRON | SOLANA | XRPL)
walletIdWallet identifier in the registry
typenative | token

OTC Webhook (otc.trade.settled, otc.trade.failed) ​

Sent when an OTC trade (see OTC) reaches a terminal state — the push alternative to polling GET /v1/otc/trades/{id}. The trade object is the request body directly. Use the trade id for idempotency.

json
{
  "id": "a1b2c3d4-...",
  "side": "BUY",
  "asset": "USDT",
  "quoteCurrency": "BRL",
  "volume": 17912.5,
  "unitPrice": 5.5825,
  "totalAmount": 100000,
  "walletAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "walletNetwork": "TRON",
  "status": "SETTLED",
  "txHash": "0xabc...",
  "createdAt": "2026-06-18T12:00:00.000Z",
  "executedAt": "2026-06-18T12:03:00.000Z"
}

On failure: "status": "FAILED", "txHash": null, "executedAt": null. For a BUY paid via QR (paymentMethod: PIX_QRCODE), failure also covers PIX_IN_EXPIRED (QR not paid in time) and PIX_IN_PAYER_MISMATCH (PIX paid by a document other than your tenant's CNPJ — refunded manually by support).

FieldDescription
idOTC trade id — matches POST /v1/otc/trades. Use for idempotency
sideBUY (onramp) | SELL (offramp)
assetTraded asset (e.g. USDT)
quoteCurrencyFiat currency (e.g. BRL)
volumeCrypto amount
unitPriceFinal applied price per unit
totalAmountTotal fiat amount
walletAddressDestination (BUY) / custodial source (SELL) wallet
walletNetworkTRON | EVM | POLYGON | ETHEREUM
statusSETTLED | FAILED
txHashOnchain hash when settled; null on failure
createdAtWhen the trade was created
executedAtWhen it settled; null on failure

The payload is provider-neutral — it never exposes the liquidity source, cost, or margin.

Retries & Security ​

  • A delivery succeeds on any HTTP 2xx within 30 seconds
  • Failed deliveries are retried up to 3 more times with exponential backoff (1s → 2s → 4s), then marked FAILED
  • Every attempt is recorded — inspect the delivery log in the BackOffice (Auditoria → Webhooks)
  • Always validate X-Webhook-Signature; respond fast (2xx) and process asynchronously
  • Every webhook has a poll-based counterpart (e.g. checkOnboarding, GET /v1/otc/trades/{id}) to reconcile missed events