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 Type | Description |
|---|---|
| fiat.received | A deposit was received into a virtual or reserved account. |
| fiat.sent | A transfer or withdrawal completed successfully. |
| fiat.failed | A 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
- Always verify the webhook signature before processing.
- Return a
200 OKimmediately after successfully handling an event. - Log all webhook payloads for debugging and reconciliation.
- Avoid blocking or long-running operations inside the webhook route.
- Use webhooks as the source of truth — they’re more reliable than manual polling.
🧩 Recommended Setup
-
Endpoint Example:
https://yourdomain.com/webhooks/fiat -
Firewall Configuration:
Only accept requests from Tsara’s IPs (available in your dashboard settings). -
Webhook Secret Rotation:
Regularly rotate your secret key for improved security.
🔗 Related Pages
- Virtual Bank Accounts — Manage your NGN accounts.
- Reserved Accounts — Create customer-specific sub-accounts.
- Bank Transfers — Automate payouts and withdrawals.
- Customers — Link transactions to verified customers.