Webhooks
Get real-time inbound email events pushed to your endpoint. Register webhooks per sender ID, verify HMAC signatures, inspect deliveries, and handle retries.
Webhooks push the moment email arrives — no polling required. You register one or more webhook URLs per sender ID; MailAfrica delivers events with HMAC-SHA256 signatures you can verify.
Register a webhook
curl -X POST https://api.mailafrica.online/api/webhook/webhooks \
-H "X-API-Key: MAIL_<your_api_key_here>" \
-H "Content-Type: application/json" \
-d '{
"address_id": 42,
"url": "https://your-app.example.com/hooks/mailafrica",
"secret": "your-own-secret" // optional
}'Fields: address_id (required, must be an address you own), url (required, http/https), secret (optional — if omitted, a whsec_ + 32 hex secret is generated and returned once).
secret securely — you need it to verify every delivery. It is only returned at creation time.Event payloads
Real inbound events look like this (delivered with Content-Type: application/json and User-Agent: MailAfrica-Webhook/1.0):
{
"event": "inbound.message_received",
"webhook_id": 12,
"address_id": 42,
"message_id": 9001,
"timestamp": "2026-08-15T09:30:00Z"
}Fetch the full message with GET /api/inbound/messages/{message_id}. Test events use event: "webhook.test" and include a friendly message field.
Verify the signature
Every delivery carries the same HMAC-SHA256 hex signature in both X-Signature and X-Webhook-Signature headers. Compute it over the raw request body with your secret and compare (constant-time).
import crypto from "node:crypto";
const secret = "whsec_..."; // from webhook creation
export function verifySignature(req, secret) {
const signature = req.headers["x-webhook-signature"];
const body = await readRawBody(req); // raw string, before JSON.parse
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex"),
);
}Retries & deliveries
MailAfrica retries failed deliveries up to 3 attempts with 1s / 3s / 9s backoff and a 10s timeout. Inspect delivery history per webhook:
curl https://api.mailafrica.online/api/webhook/webhooks/12/deliveries \
-H "X-API-Key: MAIL_<your_api_key_here>"[
{
"id": 501,
"webhook_id": 12,
"message_id": 9001,
"status_code": 200,
"attempt": 1,
"delivered_at": "2026-08-15T09:30:01Z",
"status": "delivered",
"next_retry_at": null,
"last_error": null,
"created_at": "2026-08-15T09:30:00Z"
}
]Status is one of pending | retrying | delivered | failed. Return 2xx quickly from your handler so we stop at one attempt.
Test & manual trigger
curl -X POST https://api.mailafrica.online/api/webhook/webhooks/12/test \
-H "X-API-Key: MAIL_<your_api_key_here>"Manage webhooks
# List webhooks for an address (address_id required)
curl "https://api.mailafrica.online/api/webhook/webhooks?address_id=42" \
-H "X-API-Key: MAIL_<your_api_key_here>"
# Delete a webhook
curl -X DELETE https://api.mailafrica.online/api/webhook/webhooks/12 \
-H "X-API-Key: MAIL_<your_api_key_here>"