Webhooks Guide

Overview

Webhooks allow you to receive real-time notifications about events happening in your Kira integration. Instead of polling the API, events are pushed to your server immediately when they occur.


Register Your Webhook URL

Register your webhook endpoint with the Kira API:

Endpoint

POST /webhooks/register

Request

curl -X POST https://api.balampay.com/webhooks/register \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-domain.com/webhooks/kira",
    "secret": "your_webhook_secret",
    "client_uuid": "your-client-uuid"
  }'

Parameters

ParameterTypeRequiredDescription
webhook_urlstringYesYour HTTPS endpoint that will receive webhooks (must be HTTPS in production)
secretstringYesSecret key used to sign webhook payloads for verification
client_uuidstringYesYour unique client identifier

Response

{
  "message": "Webhook registered successfully"
}

Webhook Structure

All webhooks follow the same base structure:

{
  "event": "event.name",
  "data": {
    ...
  }
}
FieldTypeDescription
eventstringThe event type (e.g., user.created, virtual_account.deposit_funds_received)
dataobjectEvent-specific payload

Your endpoint must return a 200 status code to acknowledge receipt. Non-2xx responses will be treated as a delivery failure.


Signature Verification

All webhooks include an x-signature-sha256 header containing an HMAC SHA256 signature computed with the secret you provided during registration. Always verify this signature before processing webhooks.

Verification Steps

  1. Extract the x-signature-sha256 header from the request
  2. Compute the HMAC SHA256 of the JSON-stringified request body using your secret
  3. Compare the computed signature with the received signature using a timing-safe comparison

Example (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature) {
  const secret = process.env.KIRA_WEBHOOK_SECRET;

  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(computedSignature)
  );
}

Important: If you use middleware that parses the JSON body (e.g., express.json()), you must JSON.stringify() the parsed body to verify the signature — do not use the raw buffer.


Best Practices

  1. Return 200 quickly - Acknowledge receipt immediately, process the event asynchronously
  2. Verify signatures - Always validate webhook authenticity before processing
  3. Implement idempotency - Use unique identifiers from the data payload (e.g., event_id) to prevent duplicate processing
  4. Use HTTPS - Webhook URLs must use HTTPS in production
  5. Store secrets securely - Use environment variables, never commit secrets to code

Testing

Local Development with ngrok

# 1. Start your local server
npm run dev

# 2. In another terminal, start ngrok
ngrok http 3000

# 3. Register the ngrok URL as your webhook endpoint
curl -X POST https://api.balampay.com/webhooks/register \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://abc123.ngrok.io/webhooks/kira",
    "secret": "dev_secret_123",
    "client_uuid": "YOUR_CLIENT_UUID"
  }'

Sandbox

In sandbox mode, user verification is automatically approved and webhooks are sent immediately for user creation and verification events.


Support