Webhooks

Configure real-time notifications for message events, delivery receipts, and incoming messages using webhooks.

Overview

Webhooks allow your application to receive real-time HTTP POST notifications when events occur in your AmanahAgent account. Instead of polling our API for updates, webhooks push data to your application as events happen.

✅ Benefits

  • Real-time event notifications
  • Reduced API polling overhead
  • Better user experience
  • Automatic retry mechanism

🔧 Requirements

  • HTTPS endpoint (SSL required)
  • Return 200 status code
  • Respond within 30 seconds
  • Handle duplicate events

Setup and Configuration

1. Create Webhook Endpoint

Node.js/Express Example

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Webhook endpoint
app.post('/webhooks/amanahagent', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  
  // Verify signature
  if (verifySignature(req.body, signature, secret)) {
    const event = req.body;
    
    console.log('Received event:', event.event);
    console.log('Event data:', event.data);
    
    // Process the event
    handleWebhookEvent(event);
    
    res.status(200).send('OK');
  } else {
    res.status(401).send('Unauthorized');
  }
});

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(JSON.stringify(payload));
  const calculatedSignature = 'sha256=' + hmac.digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(calculatedSignature)
  );
}

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Python/Flask Example

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

app = Flask(__name__)

@app.route('/webhooks/amanahagent', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-Webhook-Signature')
    secret = os.environ.get('WEBHOOK_SECRET')
    
    # Verify signature
    if verify_signature(request.data, signature, secret):
        event = request.json
        
        print(f"Received event: {event['event']}")
        print(f"Event data: {event['data']}")
        
        # Process the event
        handle_webhook_event(event)
        
        return jsonify({'status': 'success'}), 200
    else:
        return jsonify({'error': 'Unauthorized'}), 401

def verify_signature(payload, signature, secret):
    calculated_signature = 'sha256=' + hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, calculated_signature)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3000)

2. Configure Webhook via API

POST https://api.amanahagent.cloud/v1/webhooks

Request Parameters

ParameterTypeRequiredDescription
urlstringRequiredHTTPS URL to receive webhook events
eventsarrayRequiredEvent types to subscribe to
secretstringOptionalSecret for webhook signature verification

Example Request

curl -X POST https://api.amanahagent.cloud/v1/webhooks   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"   -d '{
    "url": "https://your-app.com/webhooks/amanahagent",
    "events": [
      "message.sent",
      "message.delivered",
      "message.read",
      "message.received"
    ],
    "secret": "your-webhook-secret"
  }'

Event Types and Payload Structures

message.sent

Triggered when a message is successfully sent to WhatsApp servers.

Payload Structure

{
  "event": "message.sent",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "message_id": "msg_abc123xyz789",
    "status": "sent",
    "to": "+1234567890",
    "type": "text",
    "message": "Hello from AmanahAgent!",
    "sent_at": "2024-01-15T10:30:00Z",
    "credits_used": 1
  }
}

message.delivered

Triggered when a message is successfully delivered to the recipient's device.

Payload Structure

{
  "event": "message.delivered",
  "timestamp": "2024-01-15T10:30:15Z",
  "data": {
    "message_id": "msg_abc123xyz789",
    "status": "delivered",
    "to": "+1234567890",
    "delivered_at": "2024-01-15T10:30:15Z"
  }
}

message.read

Triggered when a message is read by the recipient (if read receipts are enabled).

Payload Structure

{
  "event": "message.read",
  "timestamp": "2024-01-15T10:35:20Z",
  "data": {
    "message_id": "msg_abc123xyz789",
    "status": "read",
    "to": "+1234567890",
    "read_at": "2024-01-15T10:35:20Z"
  }
}

message.failed

Triggered when a message fails to be delivered.

Payload Structure

{
  "event": "message.failed",
  "timestamp": "2024-01-15T10:30:30Z",
  "data": {
    "message_id": "msg_abc123xyz789",
    "status": "failed",
    "to": "+1234567890",
    "error": {
      "code": "INVALID_PHONE_NUMBER",
      "message": "The phone number is not valid"
    },
    "failed_at": "2024-01-15T10:30:30Z"
  }
}

message.received

Triggered when you receive an incoming message from a user.

Payload Structure

{
  "event": "message.received",
  "timestamp": "2024-01-15T10:40:00Z",
  "data": {
    "message_id": "msg_incoming_xyz123",
    "from": "+1234567890",
    "type": "text",
    "message": "Hello! I need help with my order.",
    "received_at": "2024-01-15T10:40:00Z",
    "contact": {
      "name": "John Doe",
      "profile_picture": "https://example.com/profile.jpg"
    }
  }
}

Security and Verification

⚠️ Important Security Note

Always verify webhook signatures to ensure the requests are coming from AmanahAgent and haven't been tampered with.

Signature Verification

Each webhook request includes a X-Webhook-Signature header containing an HMAC-SHA256 signature of the request body.

Verification Process

  1. Get the signature from the X-Webhook-Signature header
  2. Create an HMAC-SHA256 hash of the raw request body using your webhook secret
  3. Prepend "sha256=" to your calculated hash
  4. Compare the signatures using a timing-safe comparison function

Verification Examples

JavaScript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload, 'utf8');
  const calculatedSignature = 'sha256=' + hmac.digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(calculatedSignature)
  );
}

// Usage
const isValid = verifyWebhookSignature(
  JSON.stringify(req.body),
  req.headers['x-webhook-signature'],
  process.env.WEBHOOK_SECRET
);

if (!isValid) {
  return res.status(401).send('Invalid signature');
}
Python
import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
    calculated_signature = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, calculated_signature)

# Usage
is_valid = verify_webhook_signature(
    request.data.decode('utf-8'),
    request.headers.get('X-Webhook-Signature'),
    os.environ['WEBHOOK_SECRET']
)

if not is_valid:
    return jsonify({'error': 'Invalid signature'}), 401

IP Whitelisting

For additional security, you can whitelist the following IP addresses to only accept webhook requests from AmanahAgent:

• 52.89.214.238
• 54.218.53.128
• 52.32.178.7

Testing Webhooks

Development Tools

🌐 ngrok

Expose your local development server to receive webhooks

ngrok http 3000

🔧 Webhook.site

Inspect and debug webhook payloads

https://webhook.site

Test Event

You can trigger a test webhook event to verify your endpoint is working correctly:

POST /webhooks/{webhook_id}/test

Example Request

curl -X POST https://api.amanahagent.cloud/v1/webhooks/webhook_123/test   -H "Authorization: Bearer YOUR_API_KEY"   -H "Content-Type: application/json"

Webhook Logs

Monitor webhook delivery attempts, response codes, and retry information:

GET /webhooks/{webhook_id}/logs

Troubleshooting Common Issues

❌ Webhook Not Receiving Events

  • Ensure your endpoint is publicly accessible via HTTPS
  • Check that your server is returning a 200 status code
  • Verify the webhook is configured for the correct events
  • Check your firewall settings and IP whitelisting

⚠️ Signature Verification Failing

  • Ensure you're using the raw request body for signature calculation
  • Verify your webhook secret is correct
  • Check that you're using the correct hash algorithm (HMAC-SHA256)
  • Use timing-safe comparison functions to prevent timing attacks

🔄 Duplicate Events

  • Implement idempotency using the message_id or event timestamp
  • Store processed event IDs to avoid duplicate processing
  • Handle retries gracefully by returning 200 for already processed events

⏱️ Timeout Issues

  • Respond with 200 status within 30 seconds
  • Process webhook events asynchronously if needed
  • Queue heavy processing tasks for later execution
  • Optimize your endpoint for fast response times

Best Practices

✅ Do

  • Always verify webhook signatures
  • Return 200 status code quickly
  • Implement idempotency checks
  • Use HTTPS endpoints
  • Log webhook events for debugging
  • Handle retries gracefully
  • Process events asynchronously

❌ Don't

  • Skip signature verification
  • Take longer than 30 seconds to respond
  • Process the same event multiple times
  • Use HTTP (non-SSL) endpoints
  • Ignore error handling
  • Block the webhook response with heavy processing
  • Expose webhook secrets in logs