Skip to content

Webhooks ​

Webhooks allow Galatext to send real-time notifications to your application when events occur, such as message delivery updates or incoming messages.

Why Use Webhooks? ​

  • Real-time updates — Get notified instantly instead of polling the API
  • Event-driven architecture — Trigger your business logic on message events
  • Reduced latency — Know the moment a message is delivered or fails
  • Two-way messaging — Receive incoming SMS or WhatsApp replies

Supported Events ​

EventDescription
message.pendingMessage accepted and queued
message.sentMessage submitted to carrier
message.deliveredMessage delivered to recipient
message.failedMessage delivery failed
message.expiredMessage expired before delivery
message.incomingIncoming SMS received (two-way)
whatsapp.incomingIncoming WhatsApp message received

Setup Guide ​

Step 1: Create Your Endpoint ​

Build an HTTPS endpoint in your application that can receive POST requests:

javascript
// Express.js example
const express = require('express');
const app = express();

app.post('/webhooks/galatext', express.json(), (req, res) => {
  const event = req.body;

  console.log('Received webhook event:', event.event);

  // Handle different event types
  switch (event.event) {
    case 'message.delivered':
      // Update your database
      console.log(`Message ${event.data.messageId} delivered`);
      break;
    case 'message.failed':
      console.log(`Message ${event.data.messageId} failed:`, event.data.reason);
      break;
    case 'message.incoming':
      console.log(`Incoming SMS from ${event.data.from}: ${event.data.text}`);
      break;
  }

  // Acknowledge receipt - very important!
  res.status(200).json({ received: true });
});

app.listen(3000);
python
# Flask example
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/galatext', methods=['POST'])
def handle_webhook():
    event = request.json
    print(f"Received event: {event['event']}")

    if event['event'] == 'message.delivered':
        msg_id = event['data']['messageId']
        print(f"Message {msg_id} delivered")

    # Return 200 to acknowledge
    return jsonify({"received": True}), 200

Step 2: Configure in Dashboard ​

  1. Log in to the Galatext Dashboard
  2. Navigate to Settings → Webhooks
  3. Click Add Webhook
  4. Enter your endpoint URL (must be HTTPS)
  5. Select events to subscribe to
  6. Optionally set a Secret for signature verification
  7. Click Save

Step 3: Test Your Webhook ​

Use the Send Test button in the dashboard to send a sample payload to your endpoint.

Payload Formats ​

Delivery Report Webhook ​

json
{
  "event": "message.delivered",
  "timestamp": "2025-06-15T14:32:10Z",
  "data": {
    "messageId": "msg_abc123def456",
    "batchId": "batch_xyz789abc012",
    "phoneNumber": "+254712345678",
    "status": "DELIVERED",
    "senderId": "YOURBRAND",
    "reference": "order-12345",
    "deliveredAt": "2025-06-15T14:32:10Z",
    "creditsUsed": 1
  }
}

Incoming SMS Webhook ​

json
{
  "event": "message.incoming",
  "timestamp": "2025-06-15T15:00:00Z",
  "data": {
    "messageId": "inc_msg_001",
    "from": "+254712345678",
    "to": "GALATEXT",
    "text": "STOP",
    "receivedAt": "2025-06-15T15:00:00Z"
  }
}

Incoming WhatsApp Webhook ​

json
{
  "event": "whatsapp.incoming",
  "timestamp": "2025-06-15T16:00:00Z",
  "data": {
    "messageId": "wain_001",
    "from": "+254712345678",
    "type": "text",
    "text": {
      "body": "Hello, I need help with my order"
    },
    "timestamp": "2025-06-15T16:00:00Z"
  }
}

Security & Verification ​

Galatext signs each webhook payload using HMAC-SHA256 with your webhook secret.

Verifying Signatures ​

The signature is sent in the X-Galatext-Signature header:

X-Galatext-Signature: t=1718461200,v1=abc123def456...

The payload format for signature calculation is: {timestamp}.{body} where body is the raw JSON request body.

JavaScript Verification ​

javascript
const crypto = require('crypto');

function verifySignature(req, secret) {
  const signature = req.headers['x-galatext-signature'];
  if (!signature) return false;

  const parts = signature.split(',');
  const timestamp = parts[0].split('=')[1];
  const receivedSig = parts[1].split('=')[1];

  // 5-minute tolerance for clock skew
  const now = Math.floor(Date.now() / 1000);
  if (now - parseInt(timestamp) > 300) return false;

  const payload = `${timestamp}.${JSON.stringify(req.body)}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(receivedSig),
    Buffer.from(expectedSig)
  );
}

Python Verification ​

python
import hmac
import hashlib
import json
from flask import request

def verify_webhook(secret):
    signature = request.headers.get('X-Galatext-Signature')
    if not signature:
        return False

    parts = signature.split(',')
    timestamp = parts[0].split('=')[1]
    received_sig = parts[1].split('=')[1]

    # 5-minute tolerance
    import time
    if time.time() - int(timestamp) > 300:
        return False

    payload = f"{timestamp}.{json.dumps(request.json, separators=(',', ':'))}"
    expected_sig = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(received_sig, expected_sig)

IP Ranges ​

Add the following IP ranges to your allowlist for Galatext webhook requests:

103.xxx.xxx.0/24
167.xxx.xxx.0/24

Contact support for the current list of webhook source IPs.

Retry Policy ​

If your endpoint does not return a 200 OK status within 5 seconds, Galatext retries with exponential backoff:

AttemptDelay
1stImmediate
2nd30 seconds
3rd2 minutes
4th10 minutes
5th30 minutes
6th2 hours

After 6 failed attempts, the event is discarded. You can view failed webhooks in the dashboard under Settings → Webhooks → Logs.

Best Practices ​

  • Respond quickly — Your endpoint should return 200 OK within 5 seconds
  • Process asynchronously — Queue the event for processing and respond immediately
  • Make it idempotent — Handle duplicate webhook deliveries gracefully
  • Verify signatures — Always validate the HMAC signature
  • Use HTTPS only — Never use HTTP for webhook endpoints
  • Log everything — Keep audit logs of all webhook events
  • Set a webhook secret — This enables signature verification

Powered by Galatext Software