Webhooks Overview
Webhooks allow you to receive real-time notifications when events occur in the FLUID Network. Instead of continuously polling our API for updates, webhooks push event data to your server as soon as an action happens.
What Are Webhooks?
A webhook is an HTTP POST request that FLUID sends to your specified endpoint URL when a subscribed event occurs. Each webhook contains detailed information about the event in JSON format, along with security headers for verification.
Example Flow:
Why Use Webhooks?
Real-Time Updates
Receive instant notifications when transactions complete, fail, or change status—no polling required.
Reduced API Calls
Eliminate the need for continuous status polling, reducing API usage and improving rate limit efficiency.
Better User Experience
Update your users immediately when their transactions are processed, providing a seamless payment experience.
Reliable Delivery
FLUID automatically retries failed webhook deliveries up to 5 times with exponential backoff.
Available Events
FLUID webhooks support transaction lifecycle events:
| Event Type | Description | When Triggered |
|---|---|---|
transaction.created | Transaction initiated | When a debit/credit transaction is first created |
transaction.pending | Transaction pending bank processing | When transaction is queued for bank submission |
transaction.processing | Transaction submitted to bank | When transaction is sent to the bank for processing |
transaction.completed | Transaction successfully processed | When the bank confirms successful completion |
transaction.failed | Transaction failed | When the bank rejects or transaction processing fails |
transaction.reversed | Transaction reversed/refunded | When a completed transaction is reversed |
Most Common Events
For typical payment provider integrations, you'll primarily use:
transaction.completed- Update your records and notify customers of successtransaction.failed- Handle failures and inform customers
See Event Types for detailed event descriptions and examples.
Quick Start
1. Configure Your Webhook Endpoint
Create an HTTPS endpoint on your server that:
- Accepts POST requests
- Returns HTTP 2xx status codes for successful receipt
- Responds within 30 seconds (default timeout)
- Verifies webhook signatures (recommended)
Example endpoint (Node.js/Express):
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
app.post('/webhooks/fluid', (req, res) => {
// 1. Extract webhook headers
const signature = req.headers['x-fluid-signature'];
const eventType = req.headers['x-fluid-event'];
const deliveryId = req.headers['x-fluid-delivery-id'];
// 2. Verify signature (IMPORTANT!)
const webhookSecret = process.env.FLUID_WEBHOOK_SECRET;
const computedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(JSON.stringify(req.body))
.digest('hex');
if (signature !== computedSignature) {
console.error('Invalid webhook signature');
return res.status(401).json({ error: 'Invalid signature' });
}
// 3. Process the webhook event
console.log(`Received event: ${eventType}`);
console.log('Payload:', req.body);
// Handle different event types
switch (eventType) {
case 'transaction.completed':
handleTransactionCompleted(req.body);
break;
case 'transaction.failed':
handleTransactionFailed(req.body);
break;
// ... other event types
}
// 4. Respond quickly (acknowledge receipt)
res.status(200).json({ received: true });
});
function handleTransactionCompleted(payload) {
const transaction = payload.data.transaction;
console.log(`Transaction ${transaction.uuid} completed successfully`);
// Update your database
// Send customer notification
// Trigger business logic
}
function handleTransactionFailed(payload) {
const transaction = payload.data.transaction;
console.log(`Transaction ${transaction.uuid} failed: ${transaction.error_message}`);
// Update your database
// Notify customer of failure
}
app.listen(3000);2. Register Your Webhook URL
Contact your FLUID integration manager to configure:
- Webhook URL: Your HTTPS endpoint (e.g.,
https://api.yourdomain.com/webhooks/fluid) - Webhook Secret: A secure random string for signature verification (we'll generate one)
- Event Subscriptions: Which events you want to receive (default: all transaction events)
- Max Attempts: Number of retry attempts on failure (default: 5)
- Timeout: Request timeout in seconds (default: 30)
HTTPS Required
Webhook URLs must use HTTPS in production for security. HTTP URLs are only supported in development/sandbox environments.
3. Test Your Integration
Use the test transaction flow to verify webhook delivery:
# Create a test transaction
curl https://sandbox-api.fluid-network.com/api/v1/payment-providers/debit-transactions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+233241234567",
"bank_identifier": "ECO",
"amount": 10.00,
"currency": "GHS",
"narration": "Webhook test",
"partner_reference": "test-webhook-001"
}'
# Monitor your webhook endpoint for:
# 1. transaction.created
# 2. transaction.pending
# 3. transaction.processing
# 4. transaction.completed (or transaction.failed)Webhook Structure
Every webhook request includes:
Headers
| Header | Description | Example |
|---|---|---|
X-FLUID-Signature | HMAC-SHA256 signature for verification | a3f8b7c... |
X-FLUID-Event | Event type that triggered the webhook | transaction.completed |
X-FLUID-Delivery-ID | Unique delivery attempt identifier | 12345 |
X-FLUID-Timestamp | Unix timestamp when webhook was sent | 1748793600 |
Content-Type | Always application/json | application/json |
User-Agent | FLUID webhook identifier | FLUID-Webhooks/1.0 |
Payload
{
"event": "transaction.completed",
"event_id": "evt_a3f8b7c2d1e9f4g5",
"timestamp": "2025-01-28T14:23:45Z",
"api_version": "v1",
"data": {
"transaction": {
"id": "dt_12345",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"reference": "FLU-20250128-ABC123",
"partner_reference": "ORDER-12345",
"status": "completed",
"amount": 100.0,
"currency": "GHS",
"fee": 1.5,
"narration": "Payment for Order #12345",
"bank": {
"name": "Example Bank Ghana",
"identifier": "EXB",
"country_code": "GH"
},
"customer": {
"name": "Kwame Mensah",
"phone_number": "+233241234567"
},
"created_at": "2025-01-28T14:23:12Z",
"completed_at": "2025-01-28T14:23:45Z"
},
"previous_status": "processing",
"metadata": {
"order_id": "12345",
"customer_email": "kwame@example.com"
}
}
}See Payload Structure for complete field documentation.
Best Practices
✅ Do's
- Verify Signatures: Always validate the
X-FLUID-Signatureheader to prevent spoofed requests - Respond Quickly: Acknowledge receipt with HTTP 2xx within 30 seconds
- Process Asynchronously: Queue webhook processing for long-running tasks
- Handle Duplicates: Use
event_idortransaction.uuidfor idempotency - Log Everything: Store webhook payloads and delivery IDs for debugging
- Return 2xx Status: Only return success status after validating the signature and storing the event
❌ Don'ts
- Don't Process Synchronously: Avoid blocking the response with heavy business logic
- Don't Return Errors for Business Logic: Return 2xx even if your business logic fails (log the issue instead)
- Don't Ignore Signatures: Never trust webhook data without signature verification
- Don't Rely on Ordering: Webhooks may arrive out of order; use timestamps
- Don't Use HTTP: Always use HTTPS in production
Retry Logic
If your endpoint returns a non-2xx status code or times out, FLUID automatically retries with exponential backoff:
| Attempt | Delay | Total Time Elapsed |
|---|---|---|
| 1 (initial) | 0s | 0s |
| 2 | ~2s | ~2s |
| 3 | ~4s | ~6s |
| 4 | ~8s | ~14s |
| 5 | ~16s | ~30s |
| 6 (final) | ~32s | ~62s |
After 5 retry attempts (6 total delivery attempts), the webhook is marked as abandoned and requires manual investigation.
Retriable Status Codes
FLUID automatically retries these HTTP status codes:
- 408 - Request Timeout
- 429 - Too Many Requests (rate limited)
- 500-599 - Server Errors
Client errors (4xx except 408/429) are not retried and marked as failed immediately.
See Retry Logic for detailed retry behavior.
Security
Webhook security is critical. FLUID implements multiple security layers:
- HMAC Signatures: Every webhook includes a SHA256 HMAC signature
- HTTPS Only: Production webhooks require TLS 1.3+
- IP Whitelisting (optional): Restrict webhooks to FLUID's IP addresses
- Timestamp Validation: Reject old webhook requests to prevent replay attacks
See Signature Verification for implementation details.
Monitoring & Debugging
Check Webhook Delivery Status
Contact your integration manager to access webhook delivery logs:
- View all webhook attempts and responses
- Check retry history and failure reasons
- Filter by event type, status, or time range
- Access full request/response payloads
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Webhook not received | Endpoint misconfigured | Verify URL is correct and accessible |
| Signature verification fails | Wrong secret used | Confirm you're using the correct webhook secret |
| Timeouts | Slow endpoint response | Process asynchronously; respond quickly |
| Abandoned webhooks | Persistent endpoint failure | Check server logs; fix endpoint issues |
Next Steps
Need Help?
- Integration Support: support@fluidnetwork.africa
- Technical Issues: Submit an issue
- Webhook Testing: Use sandbox environment with test data
See Also
- Error Handling Guide - Handle webhook errors gracefully
- Security Best Practices - Complete security guide
- API Reference - API endpoints reference