Salami Gateway

API Documentation
Back to Dashboard

Webhooks Configuration

Configure webhooks to receive real-time notifications when events occur in your Salami Gateway. Webhooks push data to your server when payments are received, SMS messages arrive, or other events trigger.

Table of Contents

  1. Overview
  2. Payment Webhooks
  3. SMS Webhooks
  4. Webhook Configuration
  5. Payload Formats
  6. Security
  7. Retry Behavior
  8. Status Codes
  9. Best Practices

Overview

Webhooks provide push-based notifications instead of polling. When a configured event occurs, Salami sends an HTTP POST request to your specified URL with the event data.

Supported Webhook Types

Type Module Events
Payment Webhook Payments Transaction received, status changed, reversal
SMS Webhook SMS Message received, delivery report

How Webhooks Work

Event occurs (e.g., payment received)
    |
    v
Salami checks webhook rules (keyword, field, comparator)
    |
    v
Match found? --> POST to callback_url with payload
    |
    v
Your server processes the webhook
    |
    v
Returns 200 OK (or webhook is retried)

Payment Webhooks

Payment webhooks notify your server when transactions occur on your payment apps.

Webhook Fields

Field Type Description
id integer Webhook rule ID
keyword string Value to match against
comparator string How to match (equals, contains, starts_with, regex)
field string Transaction field to check
callback_url string Your endpoint URL
app_id integer Payment app ID
webhook_secret string Secret for signature verification

Comparators

Comparator Description Example
equals Exact match field: "status", keyword: "STATUS_COMPLETED"
contains Substring match field: "account", keyword: "INV-"
starts_with Prefix match field: "phone", keyword: "2547"
regex Regular expression field: "account", keyword: "^INV-\\d{4}$"
* Match all events Triggers on every transaction

Matchable Fields

Field Description Example Values
status Transaction status STATUS_COMPLETED, STATUS_FAILED
phone Payer phone number 254712345678
account Account/reference INV-2026-001
amount Transaction amount 1500.00
transaction_id Provider transaction ID ABC123XYZ
type Transaction type C2B, B2C, B2B

Example: Notify on All Completed Payments

{
  "field": "status",
  "comparator": "equals",
  "keyword": "STATUS_COMPLETED",
  "callback_url": "https://yourserver.com/webhooks/payment",
  "webhook_secret": "whsec_abc123def456"
}

Example: Notify on Specific Account Prefix

{
  "field": "account",
  "comparator": "starts_with",
  "keyword": "SCHOOL-",
  "callback_url": "https://yourserver.com/webhooks/school-payments",
  "webhook_secret": "whsec_school123"
}

Payment Webhook Payload

{
  "event": "transaction.completed",
  "timestamp": "2026-03-29T18:30:00Z",
  "webhook_id": 5,
  "data": {
    "id": 12345,
    "transaction_id": "ABC123XYZ",
    "reference": "INV-2026-001",
    "amount": 1500.00,
    "currency": "KES",
    "payer_account": "254712345678",
    "payer_name": "John Doe",
    "payee_account": "174379",
    "payee_name": "My Business",
    "status": "STATUS_COMPLETED",
    "transaction_type": "C2B",
    "payment_provider": "MpesaKeC2B",
    "transaction_time": "2026-03-29T18:30:00Z",
    "narration": "Payment for Invoice INV-2026-001"
  },
  "signature": "sha256=abc123def456..."
}

SMS Webhooks

SMS webhooks notify your server when messages are received or delivery reports arrive.

Webhook Fields

Field Type Description
id integer Webhook rule ID
keyword string Value to match against
comparator string How to match
field string Message field to check
callback_url string Your endpoint URL
app_id integer SMS app ID
webhook_secret string Secret for signature verification

Matchable Fields

Field Description Example Values
from Sender phone number 254712345678
message Message content BALANCE, SUBSCRIBE
status Delivery status delivered, failed
direction Message direction inbound, outbound

Example: Notify on All Incoming Messages

{
  "field": "direction",
  "comparator": "equals",
  "keyword": "inbound",
  "callback_url": "https://yourserver.com/webhooks/sms",
  "webhook_secret": "whsec_sms456"
}

Example: Keyword-Based SMS Webhook

{
  "field": "message",
  "comparator": "starts_with",
  "keyword": "BALANCE",
  "callback_url": "https://yourserver.com/webhooks/balance-check",
  "webhook_secret": "whsec_balance789"
}

SMS Webhook Payload

Incoming Message:

{
  "event": "message.received",
  "timestamp": "2026-03-29T18:35:00Z",
  "webhook_id": 8,
  "data": {
    "id": 501,
    "sms_app_id": 1,
    "from": "+254712345678",
    "to": "+254798765432",
    "message": "BALANCE check my account",
    "direction": "inbound",
    "received_at": "2026-03-29T18:35:00Z"
  },
  "signature": "sha256=xyz789abc012..."
}

Delivery Report:

{
  "event": "message.delivered",
  "timestamp": "2026-03-29T18:36:00Z",
  "webhook_id": 9,
  "data": {
    "id": 12345,
    "sms_app_id": 1,
    "to": "+254712345678",
    "status": "delivered",
    "delivered_at": "2026-03-29T18:35:30Z",
    "parts": 1,
    "cost": 0.80
  },
  "signature": "sha256=def456ghi789..."
}

Webhook Configuration

Webhooks are configured through the Salami dashboard:

  1. Navigate to the relevant module (Payments or SMS)
  2. Select your app
  3. Go to Webhooks tab
  4. Click Add Webhook
  5. Configure the rule:
    • Field: Which transaction/message field to match
    • Comparator: How to compare (equals, contains, etc.)
    • Keyword: The value to match against
    • Callback URL: Your server endpoint
    • Webhook Secret: Auto-generated or custom secret

Configuration Fields

Field Required Description
Field Yes The data field to match against
Comparator Yes Match type (equals, contains, starts_with, regex, *)
Keyword Yes Value to match (use * to match everything)
Callback URL Yes Your HTTPS endpoint
Webhook Secret No Secret for payload signing (recommended)

Payload Formats

Common Envelope

All webhook payloads follow this structure:

{
  "event": "event.type",
  "timestamp": "ISO 8601 timestamp",
  "webhook_id": 5,
  "data": {
    // Event-specific data
  },
  "signature": "sha256=..."
}

Event Types

Event Module Description
transaction.completed Payments Payment completed
transaction.failed Payments Payment failed
transaction.reversed Payments Payment reversed
transaction.pending Payments Payment initiated
message.received SMS Incoming SMS received
message.delivered SMS Outbound SMS delivered
message.failed SMS SMS delivery failed

Security

Webhook Secrets

Each webhook can have a webhook_secret used to sign payloads. Verify signatures to ensure webhooks are genuinely from Salami.

Signature Verification

Salami signs webhook payloads using HMAC-SHA256:

signature = HMAC-SHA256(webhook_secret, request_body)

The signature is included in:

PHP Verification Example

<?php

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$secret = 'your_webhook_secret';

$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expectedSignature, $signature)) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

// Signature valid -- process the webhook
$data = json_decode($payload, true);

Node.js Verification Example

const crypto = require('crypto');

app.post('/webhooks/payment', (req, res) => {
  const payload = JSON.stringify(req.body);
  const signature = req.headers['x-webhook-signature'];
  const secret = 'your_webhook_secret';

  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process webhook
  const event = req.body.event;
  const data = req.body.data;

  res.status(200).json({ received: true });
});

Python Verification Example

import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/payment', methods=['POST'])
def webhook():
    payload = request.get_data()
    signature = request.headers.get('X-Webhook-Signature', '')
    secret = b'your_webhook_secret'

    expected = 'sha256=' + hmac.new(secret, payload, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return jsonify({'error': 'Invalid signature'}), 401

    data = request.get_json()
    # Process webhook
    return jsonify({'received': True}), 200

Security Recommendations

  1. Always verify signatures -- Never process unsigned webhooks in production
  2. Use HTTPS -- Only configure HTTPS callback URLs
  3. IP allowlisting -- If possible, restrict incoming webhook requests to Salami IPs
  4. Idempotency -- Process each webhook only once (use webhook_id + timestamp as a dedup key)
  5. Secret rotation -- Periodically rotate webhook secrets

Retry Behavior

When your server does not return a 2xx response, Salami retries the webhook:

Retry Schedule

Attempt Delay Total Time
1 Immediate 0 minutes
2 1 minute 1 minute
3 5 minutes 6 minutes
4 30 minutes 36 minutes
5 2 hours ~2.5 hours
6 12 hours ~14.5 hours
7 (final) 24 hours ~38.5 hours

Retry Conditions

Your Response Salami Action
200-299 Success, no retry
400-499 Client error, no retry (except 408, 429)
408 (Timeout) Retries with backoff
429 (Rate Limited) Retries with backoff
500-599 Server error, retries with backoff
Connection refused Retries with backoff
Timeout (30s) Retries with backoff

Best Response Practices

  1. Return 200 immediately -- Process webhooks asynchronously
  2. Respond within 30 seconds -- Salami times out after 30 seconds
  3. Queue for processing -- Acknowledge receipt, then process in background
  4. Return 400 for invalid -- Return 4xx to stop retries on permanently invalid payloads

Status Codes

Your webhook endpoint should return:

Code Meaning Salami Action
200 Success Webhook marked as delivered
201 Created Webhook marked as delivered
204 No Content Webhook marked as delivered
400 Bad Request No retry (permanent failure)
401 Unauthorized No retry
500 Server Error Retry with backoff
503 Unavailable Retry with backoff

Best Practices

  1. Respond quickly -- Return 200 immediately, process asynchronously
  2. Verify signatures -- Always validate webhook authenticity
  3. Handle duplicates -- Design idempotent handlers
  4. Log everything -- Store raw webhook payloads for debugging
  5. Monitor failures -- Set up alerts for consecutive delivery failures
  6. Use HTTPS -- Never use HTTP callback URLs
  7. Test webhooks -- Use the Salami dashboard to send test webhook payloads
  8. Separate endpoints -- Use different URLs for payments vs SMS webhooks

Webhook Logs

View webhook delivery history in the Salami dashboard:

Logs include:

Related Documentation


Back to: Account Tokens | Payments API


Need help? Contact us at support@dgl.co.ke
© 2026 Deadan Group Limited. All rights reserved.
⚡ API Explorer
LIVE
// Response will appear here...