Webhooks

Transaction hooks

Register a hook that runs before or after a transaction.

Transaction hooks let you plug custom logic into your transaction lifecycle, before or after a transaction is processed, without changing how Blnk records the ledger entry.

Register an endpoint for each stage you care about, and Blnk calls it with the transaction context so you can validate, enrich, or trigger side effects at the right moment.

Hook types

Blnk supports two hook types:

  • PRE_TRANSACTION: before a transaction is applied
  • POST_TRANSACTION: after a transaction is applied

PRE_TRANSACTION runs before a transaction is applied.

Use it for validation, fraud checks, or enriching transaction data while the transaction is still in an early state, for example QUEUED.

{
  "event": "transaction.applied",
  "hook_type": "PRE_TRANSACTION",
  "data": {
    "precise_amount": 20000,
    "amount": 200,
    "precision": 100,
    "transaction_id": "txn_9c2f1a8e-4b3d-4c91-9f2a-1a7b6c5d4e3f",
    "parent_transaction": "",
    "source": "bln_7f91a1ae-6073-4b7a-952c-23abf94a6634",
    "destination": "bln_59b83b9c-842c-427f-91eb-43cdeaf5c01a",
    "reference": "INV-2025-0008912",
    "currency": "GBP",
    "description": "Wallet top-up",
    "status": "QUEUED",
    "hash": "623e05ceb7ca7b02a9318092de7b75d8c668628e2db76c42b621b61055b10b3e",
    "allow_overdraft": true,
    "inflight": false,
    "skip_queue": false,
    "created_at": "2025-02-23T09:43:49.623494+01:00",
    "scheduled_for": "0001-01-01T00:00:00Z",
    "inflight_expiry_date": "0001-01-01T00:00:00Z"
  }
}

POST_TRANSACTION runs after a transaction is applied.

Use it for notifications, workflow updates, or forwarding data to external systems once the transaction has been processed.

{
  "event": "transaction.applied",
  "hook_type": "POST_TRANSACTION",
  "data": {
    "precise_amount": 20000,
    "amount": 200,
    "precision": 100,
    "transaction_id": "txn_9c2f1a8e-4b3d-4c91-9f2a-1a7b6c5d4e3f",
    "parent_transaction": "",
    "source": "bln_7f91a1ae-6073-4b7a-952c-23abf94a6634",
    "destination": "bln_59b83b9c-842c-427f-91eb-43cdeaf5c01a",
    "reference": "INV-2025-0008912",
    "currency": "GBP",
    "description": "Wallet top-up",
    "status": "APPLIED",
    "hash": "623e05ceb7ca7b02a9318092de7b75d8c668628e2db76c42b621b61055b10b3e",
    "allow_overdraft": true,
    "inflight": false,
    "skip_queue": false,
    "created_at": "2025-02-23T09:43:49.623494+01:00",
    "scheduled_for": "0001-01-01T00:00:00Z",
    "inflight_expiry_date": "0001-01-01T00:00:00Z"
  }
}

Getting started

Register a hook

Register your webhook URL with the Hooks API:

curl -X POST "http://localhost:5001/hooks" \
  -H "X-blnk-key: YOUR_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pre-transaction validation",
    "url": "https://api.example.com/blnk/hooks/pre",
    "type": "PRE_TRANSACTION",
    "active": true,
    "timeout": 30,
    "retry_count": 3
  }'
const response = await blnk.Hooks.create({
  name: 'Pre-transaction validation',
  url: 'https://api.example.com/blnk/hooks/pre',
  type: 'PRE_TRANSACTION',
  active: true,
  timeout: 30,
  retry_count: 3,
});
hook, _, _ := client.Hooks.Create(
    blnkgo.CreateHookRequest{
        Name: "Pre-transaction validation",
        URL: "https://api.example.com/blnk/hooks/pre",
        Type: blnkgo.HookTypePreTransaction,
        Active: true,
        Timeout: 30,
        RetryCount: 3,
    },
)
response = blnk.hooks.create({
  "name": "Pre-transaction validation",
  "url": "https://api.example.com/blnk/hooks/pre",
  "type": "PRE_TRANSACTION",
  "active": True,
  "timeout": 30,
  "retry_count": 3,
})
ApiResponse<JsonNode> response = blnk.hooks().create(
    CreateHookData.create()
        .name("Pre-transaction validation")
        .url("https://api.example.com/blnk/hooks/pre")
        .type(HookType.PRE_TRANSACTION)
        .active(true)
        .timeout(30)
        .retryCount(3)
);
FieldDescription
nameHuman-readable label for operators and logs.
urlAbsolute HTTPS or HTTP URL that accepts requests from Blnk.
typeEither PRE_TRANSACTION or POST_TRANSACTION.
activeWhen false, Blnk skips this hook until you enable it again.
timeoutMaximum time in seconds Blnk waits for your endpoint to respond per attempt.
retry_countMaximum additional attempts after a failed delivery.
{
  "hook_id": "hk_890bdbc4-467f-4670-8424-5667e41daf29",
  "name": "Pre-transaction validation",
  "url": "https://api.example.com/blnk/hooks/pre",
  "type": "PRE_TRANSACTION",
  "active": true,
  "timeout": 30,
  "retry_count": 3
}

Test the hook

Create a transaction with. Blnk calls your hook URL when the transaction reaches the stage you registered, PRE or POST.

Confirm your endpoint receives an event with the expected payload and returns 2xx.

curl -X POST "http://localhost:5001/transactions" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 200,
    "precision": 100,
    "reference": "INV-2025-0008912",
    "currency": "GBP",
    "source": "@Funding",
    "destination": "@UserWallet",
    "description": "Wallet top-up"
  }'
const response = await blnk.Transactions.create({
  amount: 200,
  precision: 100,
  reference: 'INV-2025-0008912',
  currency: 'GBP',
  source: '@Funding',
  destination: '@UserWallet',
  description: 'Wallet top-up',
});
transaction, _, _ := client.Transaction.Create(
    blnkgo.CreateTransactionRequest{
        ParentTransaction: blnkgo.ParentTransaction{
            Amount: 200,
            Precision: 100,
            Reference: "INV-2025-0008912",
            Currency: "GBP",
            Source: "@Funding",
            Destination: "@UserWallet",
            Description: "Wallet top-up",
        },
    },
)
response = blnk.transactions.create({
  "amount": 200,
  "precision": 100,
  "reference": "INV-2025-0008912",
  "currency": "GBP",
  "source": "@Funding",
  "destination": "@UserWallet",
  "description": "Wallet top-up",
})
ApiResponse<JsonNode> response = blnk.transactions().create(
    CreateTransactions.create()
        .amount(200)
        .precision(100)
        .reference("INV-2025-0008912")
        .currency("GBP")
        .source("@Funding")
        .destination("@UserWallet")
        .description("Wallet top-up"));

Your hook endpoint should log or persist the delivery. If nothing arrives, confirm the hook is active and the URL is reachable from Blnk Core.

Retries and timeouts

When your hook endpoint is unreachable, times out, or returns a non-2xx response, Blnk retries the delivery with backoff-up to the retry_count you set when registering the hook.

timeout sets how long Blnk waits for your endpoint on each attempt.

retry_count sets the maximum additional attempts after a failed delivery.

A delivery that failed while your receiver was down can arrive minutes later once the endpoint recovers.