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
Parameter | Type | Required | Description |
---|---|---|---|
url | string | Required | HTTPS URL to receive webhook events |
events | array | Required | Event types to subscribe to |
secret | string | Optional | Secret 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
- Get the signature from the
X-Webhook-Signature
header - Create an HMAC-SHA256 hash of the raw request body using your webhook secret
- Prepend "sha256=" to your calculated hash
- 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