Skip to content

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 TypeDescriptionWhen Triggered
transaction.createdTransaction initiatedWhen a debit/credit transaction is first created
transaction.pendingTransaction pending bank processingWhen transaction is queued for bank submission
transaction.processingTransaction submitted to bankWhen transaction is sent to the bank for processing
transaction.completedTransaction successfully processedWhen the bank confirms successful completion
transaction.failedTransaction failedWhen the bank rejects or transaction processing fails
transaction.reversedTransaction reversed/refundedWhen 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 success
  • transaction.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):

javascript
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:

bash
# 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

HeaderDescriptionExample
X-FLUID-SignatureHMAC-SHA256 signature for verificationa3f8b7c...
X-FLUID-EventEvent type that triggered the webhooktransaction.completed
X-FLUID-Delivery-IDUnique delivery attempt identifier12345
X-FLUID-TimestampUnix timestamp when webhook was sent1748793600
Content-TypeAlways application/jsonapplication/json
User-AgentFLUID webhook identifierFLUID-Webhooks/1.0

Payload

json
{
  "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

  1. Verify Signatures: Always validate the X-FLUID-Signature header to prevent spoofed requests
  2. Respond Quickly: Acknowledge receipt with HTTP 2xx within 30 seconds
  3. Process Asynchronously: Queue webhook processing for long-running tasks
  4. Handle Duplicates: Use event_id or transaction.uuid for idempotency
  5. Log Everything: Store webhook payloads and delivery IDs for debugging
  6. Return 2xx Status: Only return success status after validating the signature and storing the event

❌ Don'ts

  1. Don't Process Synchronously: Avoid blocking the response with heavy business logic
  2. Don't Return Errors for Business Logic: Return 2xx even if your business logic fails (log the issue instead)
  3. Don't Ignore Signatures: Never trust webhook data without signature verification
  4. Don't Rely on Ordering: Webhooks may arrive out of order; use timestamps
  5. 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:

AttemptDelayTotal Time Elapsed
1 (initial)0s0s
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:

  1. HMAC Signatures: Every webhook includes a SHA256 HMAC signature
  2. HTTPS Only: Production webhooks require TLS 1.3+
  3. IP Whitelisting (optional): Restrict webhooks to FLUID's IP addresses
  4. 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

IssueCauseSolution
Webhook not receivedEndpoint misconfiguredVerify URL is correct and accessible
Signature verification failsWrong secret usedConfirm you're using the correct webhook secret
TimeoutsSlow endpoint responseProcess asynchronously; respond quickly
Abandoned webhooksPersistent endpoint failureCheck server logs; fix endpoint issues

Next Steps

Event Types →

Explore all available webhook events and when they're triggered.

Payload Structure →

Understand the complete webhook payload schema and fields.

Signature Verification →

Implement secure signature verification in your language.

Retry Logic →

Learn how FLUID handles failed deliveries and retries.

Need Help?


See Also