Webhook Notification

TemboPlus delivers webhook notifications once transactions are reflected in your collection accounts, via HTTP POST requests to your configured webhook URL.

πŸ’‘ What to Expect

Webhook notifications are sent shortly after transactions are reflected in your collection accounts. Reflection times depend on how the originating and receiving banks process and settle payments.

The actual reflection time may vary depending on several factors:

  • Payment channel and amount – Smaller payments are often settled instantly through a faster payment channel, whereas larger or interbank transfers may take longer to clear.

  • Bank processing schedules – Some banks apply incoming credits immediately after settlement, others during clearing windows.

  • Non-working days (RTGS only) – Real-time gross settlement (TISS) payments are not processed on weekends or public holidays and will be applied on the next business day.

TemboPlus dispatches webhook notifications once your account balance is updated, ensuring your system receives reliable transaction events as soon as they are available.

For detailed information on payment channels, cutoff times, and clearing schedules in Tanzania, you can request the official Bank Payment Processing Reference from TemboPlus Support.

πŸ“¨ Webhook Request Format

Your webhook endpoint will receive HTTP POST requests with these characteristics:

  • Method: POST

  • Content-Type: application/json

  • Headers: Standard HTTP headers plus x-request-id for tracing

Request Body Structure

{
  "timestamp": "2025-09-15T12:34:56+03:00",
  "signature": "base64-encoded-hmac-sha256",
  "payload": "stringified-json-of-transaction-data"
}

Transaction Data (inside payload string)

{
  "event": "transaction.created",
  "account": {
    "accountNo": "1234567890",
    "accountName": "Business Account Ltd",
    "bankId": "uuid-of-bank"
  },
  "transaction": {
    "id": "unique-transaction-id",
    "transactionId": "bank-transaction-id-or-null",
    "reference": "TXN-REF-123456",
    "paymentReference": "PAY-REF-789",
    "transactionDate": "2025-09-15T10:30:00+03:00",
    "creditOrDebit": "CREDIT",
    "currency": "TZS",
    "amountCredit": 50000.00,
    "amountDebit": 0,
    "availableBalance": 150000.00,
    "currentBalance": 150000.00,
    "description": "Customer payment received"
  },
  "counterparty": null
}

πŸ” Security Verification

HMAC Signature Validation Process

Each webhook includes a cryptographic signature that you must verify:

  1. Extract Components: Get timestamp, signature, and payload from request body

  2. Create Signing Content: Concatenate timestamp + payload as a string

  3. Calculate HMAC: Use HMAC-SHA256 with your provided hash key (base64 decode the key first)

  4. Encode Result: Base64 encode the HMAC result

  5. Compare: The calculated signature must exactly match the received signature

Signature Validation Algorithm

signing_content = timestamp + payload
calculated_hmac = HMAC-SHA256(base64_decode(your_hash_key), signing_content)
expected_signature = base64_encode(calculated_hmac)
is_valid = (expected_signature === received_signature)

Example Implementation (Node.js)

const crypto = require('crypto');

function validateWebhook(envelope, yourHashKey) {
  const { timestamp, signature, payload } = envelope;
  
  // Create signing content by concatenating timestamp and payload
  const signingContent = timestamp + payload;
  
  // Calculate HMAC-SHA256 signature
  const expectedSignature = crypto
    .createHmac('sha256', Buffer.from(yourHashKey, 'base64'))
    .update(signingContent, 'utf-8')
    .digest('base64');
  
  // Compare signatures
  return signature === expectedSignature;
}

πŸ—οΈ Implementation Requirements

1. Endpoint Configuration

  • Protocol: HTTPS only (HTTP not supported)

  • Method: Accept POST requests

  • Content-Type: Parse application/json

  • Response Time: Respond within 30 seconds

  • Response Code: Return HTTP 200 for successful processing

2. Request Processing Flow

  1. Receive Request: Accept POST with JSON body

  2. Validate Signature: Verify HMAC signature (reject if invalid)

  3. Parse Payload: Extract transaction data from payload string

  4. Check Duplicates: Handle potential duplicate deliveries

  5. Process Transaction: Execute your business logic

  6. Respond: Return HTTP 200 success response

3. Idempotency Handling

Webhooks may be delivered multiple times. Implement duplicate detection using:

  • Transaction ID: Use the unique transaction.id field

  • Storage: Track processed transaction IDs in database/cache

  • Response: Return HTTP 200 for already-processed transactions

πŸ”„ Delivery Behavior

Retry Policy

Our system automatically retries failed webhook deliveries:

  • Success Criteria: HTTP status codes 200-299

  • Permanent Failures: HTTP 400-499 (except 408 Request Timeout, 429 Too Many Requests)

  • Temporary Failures: HTTP 500-599, network errors, timeouts

  • Retry Schedule: Exponential backoff starting at 2 seconds

  • Maximum Attempts: Up to 20 retries over 24 hours

Expected Response Format

Your endpoint should return a simple HTTP 200 success response:

{
  "message": "Transaction processed successfully",
  "transactionId": "unique-transaction-id"
}

πŸ’» Implementation Example

Conceptual Flow (Language Agnostic)

1. Receive HTTP POST request
2. Parse JSON request body
3. Validate HMAC signature
   - If invalid: Return HTTP 401
4. Extract transaction data from payload
5. Check if transaction already processed
   - If duplicate: Return HTTP 200
6. Execute business logic:
   - Update account balances
   - Trigger notifications
   - Log transaction
   - Update records
7. Mark transaction as processed
8. Return HTTP 200 success

Node.js Implementation Example

const express = require('express');
const app = express();
app.use(express.json());

// In-memory storage (use database in production)
const processedTransactions = new Set();

app.post('/webhook/transactions', async (req, res) => {
  try {
    // 1. Validate webhook signature
    if (!validateWebhook(req.body, YOUR_HASH_KEY)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // 2. Parse transaction data
    const data = JSON.parse(req.body.payload);
    const transactionId = data.transaction.id;
    
    // 3. Handle duplicates
    if (processedTransactions.has(transactionId)) {
      return res.status(200).json({ message: 'Already processed' });
    }
    
    // 4. Process transaction
    await processTransaction(data);
    
    // 5. Mark as processed
    processedTransactions.add(transactionId);
    
    // 6. Return success
    res.status(200).json({ 
      message: 'Transaction processed',
      transactionId 
    });
    
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

async function processTransaction(webhookData) {
  const { account, transaction } = webhookData;
  
  // Your business logic here
  console.log(`Processing ${transaction.creditOrDebit} of ${transaction.currency} ${transaction.amountCredit || transaction.amountDebit} on account ${account.accountNo}`);
  
  // Example actions:
  // - Update local account balance
  // - Send customer notification
  // - Trigger reconciliation process
  // - Log to audit trail
}

βš™οΈ Setup Requirements

Partner Configuration

  1. Webhook URL: Provide your HTTPS endpoint URL

  2. Hash Key: You'll receive a base64-encoded signing key

  3. Account Allocation: Ensure accounts are allocated to your partner account

Testing Your Implementation

Test Request Format

POST https://your-domain.com/webhook/transactions
Content-Type: application/json

{
  "timestamp": "2025-09-15T12:00:00+03:00",
  "signature": "calculated-signature-here",
  "payload": "{\"event\":\"transaction.created\",\"account\":{\"accountNo\":\"TEST123\",\"accountName\":\"Test Account\",\"bankId\":\"test-bank\"},\"transaction\":{\"id\":\"TEST-001\",\"reference\":\"TEST-REF\",\"transactionDate\":\"2025-09-15T11:30:00+03:00\",\"creditOrDebit\":\"CREDIT\",\"currency\":\"TZS\",\"amountCredit\":1000,\"amountDebit\":0,\"currentBalance\":5000,\"description\":\"Test payment\"}}"
}

βœ… Implementation Checklist

Essential Requirements

Recommended Enhancements

πŸ› Troubleshooting

Common Issues

No webhooks received

  • Verify endpoint URL is publicly accessible via HTTPS

  • Confirm accounts are properly allocated to your partner

  • Check that endpoint responds with HTTP 200

Signature validation failures

  • Ensure hash key is stored and used correctly

  • Verify signing content is exactly timestamp + payload (string concatenation)

  • Confirm payload is the raw JSON string, not a parsed object

  • Check that hash key is base64-decoded before HMAC calculation

Duplicate processing

  • Implement idempotency using the unique transaction.id

  • Store processed IDs in persistent storage (database, not memory)

  • Always return HTTP 200 for already-processed transactions

Processing timeouts

  • Ensure webhook processing completes within 30 seconds

  • Consider async processing for complex business logic

  • Return HTTP 200 immediately, then process in background

πŸ“ž Support

For webhook integration assistance, contact our technical support team with:

  • Your partner identifier

  • Webhook endpoint URL

  • Sample request/response logs

  • Error messages or unexpected behavior details


πŸ”’ Security Note: Always validate webhook signatures before processing transaction data to ensure authenticity and prevent unauthorized access to your system.

Last updated