Notification Service
When a user signs up, trigger a Welcome Email, an SMS alert, and a Slack DM simultaneously.
From immediate acknowledgment to idempotency and signature verification, learn the production-grade strategies for webhook-triggered automation using BotMatrix Trigger Hub.
Trigger Hub v2.0
Never block the sender while you process a payload. The best practice is to send an immediate HTTP 200 OK, then process the data in the background. This prevents timeouts and ensures the webhook server stays responsive.
In BotMatrix, we use a dedicated Trigger Hub to receive the request. The Trigger Hub immediately returns 200 OK to the upstream service, then pushes the raw payload into a RabbitMQ (or Kafka) queue for the worker nodes to consume.
const triggerHub = new TriggerHub({ endpoint: '/webhooks/order-events' });
await triggerHub.listen('POST', async (req, res) => {
// 1. Acknowledge immediately
res.status(200).send('Accepted');
// 2. Fire and forget into the queue
queueService.publish('order-processing', req.body);
});
Webhooks can be sent multiple times due to retries or network blips. To prevent double-processing (e.g., charging a customer twice), you must enforce idempotency.
The upstream service usually sends an X-Idempotency-Key header. Your bot logic must check if this key has been processed before. If it exists, skip execution and return the cached result.
const handleWebhook = async (req) => {
const key = req.headers['x-idempotency-key'];
// Check Redis for processed key
const exists = await redis.get(`processed:${key}`);
if (exists) {
// Return cached response to prevent duplicate work
return JSON.parse(exists);
}
// Process logic...
const result = processOrder(req.body);
// Store result in Redis with 24h expiry
await redis.setex(`processed:${key}`, 86400, JSON.stringify(result));
return result;
};
Without verification, you are vulnerable to man-in-the-middle attacks where a malicious actor sends fake requests to your bot.
Always verify the X-Signature header using HMAC-SHA256. The upstream provider signs the raw payload body with a shared secret. Your bot must replicate this calculation and compare it to the header.
import { createHmac } from 'crypto';
const verifySignature = (body, signature, secret) => {
const expected = createHmac('sha256', secret)
.update(JSON.stringify(body))
.digest('hex');
// BotMatrix SDK handles this internally
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
};
Not every payload will process successfully. Instead of crashing, implement exponential backoff retries.
If a task fails, wait 2^retry_count seconds before trying again. After the third failure, move the item to a Dead-Letter Queue (DLQ). This prevents your bot from spamming the upstream service while still allowing you to inspect the failed logs manually.
const retryLogic = async (payload, retries = 0) => {
try {
await processPayload(payload);
} catch (err) {
if (retries < 3) {
const delay = Math.pow(2, retries) * 1000;
console.log(`Retry ${retries} in ${delay}ms`);
await sleep(delay);
return retryLogic(payload, retries + 1);
}
// Move to DLQ
await dlqService.push(payload);
}
};
When a user signs up, trigger a Welcome Email, an SMS alert, and a Slack DM simultaneously.
A single inventory update triggers an inventory update bot, a pricing re-calculation bot, and an analytics tracking bot.
Fanout complex orders to shipping, billing, and support bots in parallel to reduce total time-to-ship.
Don't start from scratch. Download our pre-configured templates for the patterns above.
BotMatrix Trigger Hub + RabbitMQ integration template.
Pre-built middleware for Redis-based deduplication.
Visual pipeline configuration for exponential backoff.