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 Galatex Softwares