Skip to main content

Overview

Superbank sends webhook notifications to your registered endpoints when events occur on your account. This guide covers how to set up, test, and debug your webhook integration.

Supported Events

EventDescription
liquidity_pool.createdA new liquidity pool was provisioned
liquidity_pool.updatedPool balance or status changed
liquidity_pool.deletedA liquidity pool was deactivated
payment.createdA new payment was created
payment.updatedA payment status changed
settlement_request.createdA new settlement request was created
settlement_request.updatedA settlement request status changed

Webhook Payload Format

All webhook deliveries use a consistent JSON envelope with snake_case field names. The data object varies by event type.
{
  "event": "<event_type>",
  "data": { ... },
  "timestamp": "2026-01-26T15:48:08.700Z"
}

Headers

Each delivery includes these headers:
HeaderDescription
Content-Typeapplication/json
X-Superbank-SignatureHMAC-SHA256 signature: sha256=<hex>
X-Superbank-EventEvent type (e.g., payment.updated)

Event Payloads

Payment Events

payment.created

{
  "event": "payment.created",
  "data": {
    "id": "04621f85-bd40-46a9-a9a9-9fe14be09354",
    "type": "PAYIN",
    "status": "PENDING",
    "fee": "0.50000000",
    "source": {
      "account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "amount": "100.00000000",
      "currency": "USDC",
      "rail": "SOLANA",
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "transaction_hash": null
    },
    "destination": {
      "account_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "amount": "100.00000000",
      "currency": "USD",
      "rail": "ACH",
      "wallet_address": null,
      "transaction_hash": null
    },
    "created_at": "2026-01-26T14:12:08.354Z"
  },
  "timestamp": "2026-01-26T14:12:08.700Z"
}

payment.updated

{
  "event": "payment.updated",
  "data": {
    "id": "04621f85-bd40-46a9-a9a9-9fe14be09354",
    "type": "PAYIN",
    "status": "COMPLETED",
    "fee": "0.50000000",
    "settlement_request_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "source": {
      "account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "amount": "100.00000000",
      "currency": "USDC",
      "rail": "SOLANA",
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "transaction_hash": "5aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4yZ5aB6c"
    },
    "destination": {
      "account_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "amount": "100.00000000",
      "currency": "USD",
      "rail": "ACH",
      "wallet_address": null,
      "transaction_hash": null
    },
    "updated_at": "2026-01-26T15:48:08.670Z"
  },
  "timestamp": "2026-01-26T15:48:08.700Z"
}

Liquidity Pool Events

liquidity_pool.created

{
  "event": "liquidity_pool.created",
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "currency_code": "USDC",
    "rail": "SOLANA",
    "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "name": "USDC Pool",
    "status": "ACTIVE",
    "balance": "0.00000000",
    "available_balance": "0.00000000",
    "created_at": "2026-01-26T14:12:08.354Z"
  },
  "timestamp": "2026-01-26T14:12:08.700Z"
}

liquidity_pool.updated

{
  "event": "liquidity_pool.updated",
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "currency_code": "USDC",
    "rail": "SOLANA",
    "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "name": "USDC Pool",
    "status": "ACTIVE",
    "balance": "1500.00000000",
    "available_balance": "1200.00000000",
    "reserved": "200.00000000",
    "updated_at": "2026-01-26T15:48:08.670Z"
  },
  "timestamp": "2026-01-26T15:48:08.700Z"
}

liquidity_pool.deleted

{
  "event": "liquidity_pool.deleted",
  "data": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "currency_code": "USDC",
    "rail": "SOLANA",
    "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
    "deleted_at": "2026-01-26T16:00:00.000Z"
  },
  "timestamp": "2026-01-26T16:00:00.100Z"
}

Settlement Request Events

settlement_request.created

{
  "event": "settlement_request.created",
  "data": {
    "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "type": "INSTANT_ONRAMP",
    "infrastructure_provider": "DEVELOPER",
    "status": "REQUEST_STARTED",
    "amount": "100.00",
    "source": null,
    "destination": {
      "currency": "USDC",
      "rail": "SOLANA",
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "beneficiary": {
        "type": "BUSINESS",
        "business_name": "Test Corp",
        "address": { "country_code": "US" }
      }
    },
    "created_at": "2026-01-26T14:12:08.354Z"
  },
  "timestamp": "2026-01-26T14:12:08.700Z"
}

settlement_request.updated

{
  "event": "settlement_request.updated",
  "data": {
    "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "type": "INSTANT_ONRAMP",
    "infrastructure_provider": "DEVELOPER",
    "status": "SETTLEMENT_COMPLETED",
    "amount": "100.00000000",
    "source": null,
    "destination": {
      "currency": "USDC",
      "rail": "SOLANA",
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "account_holder": {
        "type": "BUSINESS",
        "business_name": "Test Corp",
        "address": { "country_code": "US" }
      }
    },
    "updated_at": "2026-01-26T15:48:08.670Z"
  },
  "timestamp": "2026-01-26T15:48:08.700Z"
}

Signature Verification

Every webhook delivery is signed using your endpoint’s secret. Verify the X-Superbank-Signature header to ensure the request is authentic. The signature is computed as sha256=HMAC-SHA256(secret, request_body).

Node.js

const crypto = require('crypto');

function verifyWebhookSignature(secret, payload, signatureHeader) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  const received = signatureHeader.replace('sha256=', '');
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(received, 'hex')
  );
}

// Express middleware example
app.post('/webhooks/superbank', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-superbank-signature'];
  const isValid = verifyWebhookSignature(
    process.env.WEBHOOK_SECRET,
    req.body,
    signature
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  // Handle event...
  res.status(200).send('OK');
});

Python

import hmac
import hashlib

def verify_webhook_signature(secret: str, payload: bytes, signature_header: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()

    received = signature_header.replace('sha256=', '')
    return hmac.compare_digest(expected, received)

# Flask example
@app.route('/webhooks/superbank', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Superbank-Signature')
    is_valid = verify_webhook_signature(
        os.environ['WEBHOOK_SECRET'],
        request.data,
        signature
    )

    if not is_valid:
        return 'Invalid signature', 401

    event = request.get_json()
    # Handle event...
    return 'OK', 200

Quick Testing with webhook.site

webhook.site gives you an instant public URL to receive and inspect webhook deliveries — no signup required.

Step 1: Get a test URL

  1. Go to webhook.site
  2. A unique URL is generated automatically (e.g., https://webhook.site/abc-123-def)
  3. Copy the URL

Step 2: Register the URL as a webhook endpoint

cURL
curl --request POST \
  --url https://api-sandbox.superbank.co/v0/webhooks \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "url": "https://webhook.site/YOUR_UNIQUE_ID"
  }'
Save the secret from the response — you’ll need it for signature verification.

Step 3: Trigger a test event

Perform an action on the sandbox API that generates a webhook event. For example, create a settlement request:
cURL
curl --request POST \
  --url https://api-sandbox.superbank.co/v0/settlement-requests \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "amount": 10,
    "destination": {
      "currency": "USDC",
      "rail": "SOLANA",
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "beneficiary": {
        "type": "BUSINESS",
        "address": { "country_code": "US" },
        "business_name": "Test Corp"
      }
    }
  }'

Step 4: Inspect the delivery

Go back to webhook.site to see:
  • The full JSON payload with snake_case fields
  • HTTP headers including X-Superbank-Signature and X-Superbank-Event
  • Request timing and metadata

Local Development with ngrok

To receive webhooks on your local machine during development, use ngrok to create a public tunnel to your localhost.

Step 1: Start your local webhook handler

# Start your app that listens for webhooks (e.g., on port 3000)
node server.js

Step 2: Start ngrok

ngrok http 3000
ngrok provides a public URL (e.g., https://abc123.ngrok-free.app) that forwards to localhost:3000.

Step 3: Register the ngrok URL

cURL
curl --request POST \
  --url https://api-sandbox.superbank.co/v0/webhooks \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "url": "https://abc123.ngrok-free.app/webhooks/superbank"
  }'
Now when you trigger events in the sandbox, they’ll be delivered directly to your local handler.

Retry Behavior

If your endpoint returns a non-2xx response or times out (30 seconds), Superbank will retry delivery with exponential backoff:
AttemptDelayCumulative
1Immediate
21 minute1 minute
35 minutes6 minutes
415 minutes21 minutes
51 hour~1.3 hours
61 day~1.3 days
72 days~3.3 days
84 days~7.3 days
91 week~14.3 days
102 weeks~28.3 days
After 10 failed attempts, the delivery is marked as permanently failed. Ensure your endpoint responds with 200 OK quickly — perform heavy processing asynchronously.