Webhooks

Fiat webhooks enable your application to receive real-time notifications for events such as deposits, transfers, and withdrawals on your NGN virtual bank accounts.
They are essential for synchronizing your platform’s ledger, confirming payments, and automating settlements.


🚀 Overview

Event TypeDescription
fiat.receivedA deposit was received into a virtual or reserved account.
fiat.sentA transfer or withdrawal completed successfully.
fiat.failedA transfer or withdrawal failed to process.

Tsara sends these webhooks securely to your configured endpoint, along with a signature header for validation.


🧾 Example Payloads

1️⃣ fiat.received

Triggered when a customer sends money into a Tsara bank account (main or reserved).

{
  "id": "evt_101",
  "type": "fiat.received",
  "created_at": "2025-10-17T12:00:00Z",
  "data": {
    "bank_id": "bank_123",
    "reserved_account_id": "rsv_456",
    "amount": 50000,
    "currency": "NGN",
    "customer": {
      "id": "cus_789",
      "name": "John Doe"
    },
    "reference": "deposit_001",
    "status": "success"
  }
}

2️⃣ fiat.sent

Triggered when you complete a bank transfer or payout.

{
  "id": "evt_102",
  "type": "fiat.sent",
  "created_at": "2025-10-17T12:15:00Z",
  "data": {
    "transfer_id": "trf_001",
    "bank_id": "bank_123",
    "amount": 25000,
    "currency": "NGN",
    "reference": "payout_001",
    "status": "success",
    "destination": {
      "bank_name": "Wema Bank",
      "account_number": "1234567890"
    }
  }
}

3️⃣ fiat.failed

Triggered when a payout or transfer fails.

{
  "id": "evt_103",
  "type": "fiat.failed",
  "created_at": "2025-10-17T12:20:00Z",
  "data": {
    "transfer_id": "trf_002",
    "bank_id": "bank_123",
    "amount": 20000,
    "currency": "NGN",
    "reference": "payout_002",
    "status": "failed",
    "error": "Insufficient balance"
  }
}

🛡️ Signature Verification

Every webhook request contains a signature header for authenticity:

X-Tsara-Signature: <HMAC-SHA512 hash>

You must verify this signature using your Webhook Secret Key from the dashboard.

Example — PHP

<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_TSARA_SIGNATURE'] ?? '';
$expected = hash_hmac('sha512', $payload, TSARA_WEBHOOK_SECRET);

if (!hash_equals($expected, $signature)) {
    http_response_code(400);
    exit('Invalid signature');
}

$event = json_decode($payload, true);
if ($event['type'] === 'fiat.received') {
    // Handle incoming payment
}

http_response_code(200);
?>

🔁 Retry Policy

  • Tsara retries webhook deliveries when your server doesn’t respond with 2xx.
  • Retries occur with exponential backoff (e.g., 1s → 5s → 30s).
  • Each event is idempotent, meaning it can be safely retried multiple times.

Use the event.id or reference to prevent duplicate processing.


🧠 Best Practices

  1. Always verify the webhook signature before processing.
  2. Return a 200 OK immediately after successfully handling an event.
  3. Log all webhook payloads for debugging and reconciliation.
  4. Avoid blocking or long-running operations inside the webhook route.
  5. Use webhooks as the source of truth — they’re more reliable than manual polling.

🧩 Recommended Setup

  1. Endpoint Example:
    https://yourdomain.com/webhooks/fiat

  2. Firewall Configuration:
    Only accept requests from Tsara’s IPs (available in your dashboard settings).

  3. Webhook Secret Rotation:
    Regularly rotate your secret key for improved security.


🔗 Related Pages