Webhooks
Beaco can deliver notifications to your systems in real time using outbound HTTP webhooks.
How Webhook Delivery Works
- You submit an event to Beaco.
- A recipient includes
channels: ["webhook"]and awebhook_url. - Beaco sends an HTTP
POSTrequest to that URL. - Beaco records delivery status, retries transient failures, and exposes logs via API.
Configure Webhook Recipients
Include webhook_url in the recipient object when using the webhook channel.
Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/events \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"event_type": "invoice.paid",
"recipients": [{
"channels": ["webhook"],
"webhook_url": "https://example.com/beaco/webhooks"
}],
"payload": {
"invoice_id": "inv_10022",
"amount": "49.00"
}
}'Webhook Payload Format
Beaco sends a JSON body with event and delivery context.
JSON
{
"event_type": "invoice.paid",
"payload": {
"invoice_id": "inv_10022",
"amount": "49.00"
},
"notification_id": "ntf_01j4za3vdy8r98t7b4s2v0x4bz",
"timestamp": "2026-01-12T11:02:17Z"
}Common headers:
| Header | Description |
|---|---|
Content-Type: application/json | JSON payload format |
X-Beaco-Signature-256 | HMAC-SHA256 signature for verification |
Retry Behavior
Beaco retries failed webhook deliveries using exponential backoff with jitter.
| Condition | Behavior |
|---|---|
| Timeout / network failure | Retry |
HTTP 5xx | Retry |
HTTP 2xx | Mark delivered |
| Max retries exceeded | Move to dead-letter state |
See Delivery Pipeline for retry and dead-letter operations.
Verify Webhook Signatures (HMAC-SHA256)
Use a shared secret and verify each request signature.
Verification steps:
- Read raw request body.
- Compute
HMAC_SHA256(secret, raw_body). - Compare with
X-Beaco-Signature-256using constant-time comparison.
Node.js / Express Example
JavaScript
import crypto from "crypto";
import express from "express";
const app = express();
const webhookSecret = process.env.BEACON_WEBHOOK_SECRET;
app.use(
express.json({
verify: (req, _res, buf) => {
req.rawBody = buf;
},
}),
);
app.post("/beaco/webhooks", (req, res) => {
const signature = req.header("X-Beaco-Signature-256");
if (!signature || !webhookSecret) {
return res.status(401).send("Missing signature");
}
const digest = crypto.createHmac("sha256", webhookSecret).update(req.rawBody).digest("hex");
const expected = `sha256=${digest}`;
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
if (!valid) {
return res.status(401).send("Invalid signature");
}
// Process asynchronously in your job queue.
res.status(204).end();
});Best Practices
- Return
2xxquickly (under a few seconds). - Process asynchronously after acknowledging receipt.
- Use idempotency keyed by
notification_idto avoid duplicate side effects. - Handle timeouts and retries safely.
- Log signature failures and malformed payloads for security visibility.

