Webhooks

Receive real-time notifications when events occur.

Overview

Configure webhook endpoints to receive real-time POST notifications when events occur in your account.

Events

EventDescription
DEPOSIT_ASSET_SUCCESSA deposit was completed successfully.
PAYOUT_SUCCESSFULA PIX payout was sent successfully.
PAYOUT_FAILEDA PIX payout failed.
KYC_APPROVEDKYC for an end-user was approved.
KYC_REJECTEDKYC for an end-user was rejected.
KYC_EXPIREDKYC attempt expired without submission.
KYC_FAILEDKYC attempt failed during processing.

Payload

Every webhook delivery sends a JSON body with the following structure:

{
  "event": "PAYOUT_SUCCESSFUL",
  "data": { ... }
}

Headers

Each request includes these headers for verification:

HeaderDescription
X-Hodle-SignatureHMAC signature of the payload using your webhook secret.
X-Hodle-TimestampUnix timestamp (seconds) when the request was sent.

Plus any custom headers you configured on the webhook.

Verifying Signatures

Use the X-Hodle-Signature and X-Hodle-Timestamp headers to verify that the request came from Hodle.

Verify webhook signature
import { createHmac } from 'crypto'

const verifyWebhook = (payload: string, signature: string, timestamp: string, secret: string) => {
  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex')

  return expected === signature
}

Retry Policy

Webhook deliveries are attempted once. A response with a 2xx status code is considered successful. Non-2xx responses are logged as failures.

PAYOUT_SUCCESSFUL

Sent when a PIX payout completes successfully after a Lightning invoice is paid.

PAYOUT_SUCCESSFUL
{
  "event": "PAYOUT_SUCCESSFUL",
  "data": {
    "success": true,
    "invoice": "lnbc32310n1p5u2g2qsp5xq6j2rhspx7es5c0dymwrn2wcam6ay2vpgft65njm9pe6te93fgqpp57yym5t9vqw85a8zszx2e59xhr69yum0dd9udz26vgzgptwtpfplshp5uwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2sxq9z0rgqcqpnrzjqvmhlzgnvate6rxlc2pnhser58gp298w5sx53n8gd5c78xpz8cxtxrj7rvqqgfqqqqqqqqqqqqqq05qqyg9qxpqysgqdn0n0h52k8wv5mvsy5lm4ew075u24g2nr6ys7sn5276me8583arkyahkv25d08ngvwx0cwdre3eg6jtjpus4j7xs0gznctwzmtjk20qql2p832",
    "valueInSatoshis": 276190,
    "pixKey": "13d3109f-3a1e-4c56-b76d-d2db7213b9f2",
    "valueInBrl": "1000.00",
    "fee": "170.00",
    "quote": {
      "brlAmount": "1000.00",
      "btcAmount": 0.0027619041618037344,
      "satoshis": 276190,
      "btcToBrlRate": 362069.04418686405
    }
  }
}

Fields

FieldTypeDescription
successbooleanWhether the payout was successful.
invoicestringThe Lightning invoice that was paid.
valueInSatoshisnumberAmount received in satoshis.
pixKeystringThe PIX key where BRL was sent.
valueInBrlstringValue in BRL.
feestringFee charged in BRL.
quote.brlAmountstringBRL amount quoted.
quote.btcAmountnumberBTC amount.
quote.satoshisnumberAmount in satoshis.
quote.btcToBrlRatenumberBTC to BRL exchange rate at the time.

PAYOUT_FAILED

Sent when a PIX payout fails.

PAYOUT_FAILED
{
  "event": "PAYOUT_FAILED",
  "data": {
    "success": false,
    "invoice": "lnbc10u1pj...",
    "valueInSatoshis": 2450,
    "pixKey": "user@email.com",
    "valueInBrl": "10.00",
    "fee": "0.20",
    "error": "PIX key not found"
  }
}

KYC_APPROVED

Sent when KYC finishes and is approved by the provider.

KYC_APPROVED
{
  "event": "KYC_APPROVED",
  "data": {
    "attemptId": "att_a1b2c3...",
    "level": "FULL",
    "approvedAt": "2026-05-08T22:31:11.000Z"
  }
}

After this event, /api/deposit/asset and /api/wallet/payout become available.

KYC_REJECTED

Sent when the KYC provider rejects the documents.

KYC_REJECTED
{
  "event": "KYC_REJECTED",
  "data": {
    "attemptId": "att_a1b2c3...",
    "reason": "DOCUMENT_MISMATCH"
  }
}

KYC_EXPIRED

Sent when a hosted KYC attempt times out before submission.

KYC_EXPIRED
{
  "event": "KYC_EXPIRED",
  "data": {
    "attemptId": "att_a1b2c3...",
    "expiredAt": "2026-05-08T22:31:11.000Z"
  }
}

KYC_FAILED

Sent when an internal error occurs while processing a KYC attempt.

KYC_FAILED
{
  "event": "KYC_FAILED",
  "data": {
    "attemptId": "att_a1b2c3...",
    "error": "Internal provider error"
  }
}

DEPOSIT_ASSET_SUCCESS

Sent when a deposit completes successfully.

DEPOSIT_ASSET_SUCCESS
{
  "event": "DEPOSIT_ASSET_SUCCESS",
  "data": {
    "success": true,
    "value": 5000,
    "asset": "LIGHTNING",
    "externalId": "my-order-123",
    "fee": 100,
    "fxRateAtTx": 362069.04
  }
}

Fields

FieldTypeDescription
successbooleanWhether the deposit was successful.
valuenumberAmount in BRL cents.
assetstringAsset type (DEPIX, LBTC, LIGHTNING, USDT).
externalIdstringThe external ID sent in the deposit request, or an auto-generated UUID.
feenumberFee charged in BRL cents.
fxRateAtTxnumberExchange rate at the time of the transaction.