Webhooks
🔔 Outbound Webhooks
Outbound Webhooks allow MessengerOS to notify your application in real time whenever something happens to a notification you sent — delivery confirmations, opens, clicks, bounces, unsubscribes, and more.
Instead of polling our API for status updates, you register a URL and we'll push an HTTP POST to it every time an event occurs.
⚙️ Step 1: Create a Webhook Configuration
Head over to Settings → Webhooks → Add Webhook in your MessengerOS account.
You'll need to provide:
- URL — The HTTPS endpoint on your server that will receive the webhook POST requests.
- Channels — Which channels to receive events for:
email,sms, and/orwhatsapp.
A secret key is automatically generated for you. Keep it safe — you'll use it to verify that incoming requests are genuinely from MessengerOS.
🔐 Step 2: Verify the Signature
Every webhook request we send includes an X-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook secret:
X-Signature: sha256=<hmac_sha256_hex_of_request_body>
📋 Copy
To verify the signature on your end, compute the HMAC-SHA256 of the raw request body using your secret and compare it to the value in the header (after stripping the sha256= prefix). Here's how you'd do it in PHP:
$rawBody = file_get_contents('php://input');
$secret = 'your_webhook_secret_here';
$header = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
// Strip the "sha256=" prefix
$provided = str_replace('sha256=', '', $header);
// Compute expected signature
$expected = hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($expected, $provided)) {
http_response_code(401);
exit('Invalid signature');
}
// Signature is valid — process the payload
$payload = json_decode($rawBody, true);
📋 Copy
📦 Step 3: Understand the Payload
All webhook events share the same JSON structure:
{
"status": "accepted", // event type (see table below)
"notification_origin_id": "your-unique-id", // the ID you sent when creating the notification
"recipient": "user@example.com", // email address or phone number
"error": null // present only on failure events, null otherwise
}
📋 Copy
📋 Step 4: Handle the Events
The status field tells you what happened. Here are all possible values:
Email events
| Status | Description | Error field |
|---|---|---|
| accepted | The email was accepted to be sent to recipient and is queued for delivery. | null |
| failed | The email could not be delivered. Check the error field for details. |
Reason for failure (e.g. "MX record not found", "Delivery failed") |
| open | The recipient opened the email. | null |
| click | The recipient clicked a link in the email. | null |
| unsubscribed | The recipient unsubscribed from all notifications. | null |
| complaint | The recipient marked the email as spam. | null |
SMS events
| Status | Description | Error field |
|---|---|---|
| accepted | The SMS was accepted to be sent to recipient and is queued for delivery. | null |
| failed | The SMS could not be sent. Check the error field for details. |
Reason for failure |
| Delivered | The SMS was successfully delivered to the recipient's handset. | null |
| Undelivered | The SMS could not be delivered to the recipient. | null |
✅ Step 5: Respond to the Webhook
Your endpoint must return a 2xx HTTP status code (e.g. 200 OK) within 10 seconds to acknowledge that you received the event. If your endpoint does not respond with a 2xx status, the delivery will be marked as failed.
HTTP/1.1 200 OK
💡 Tips
- Always use
notification_origin_idwhen sending notifications — without it, no webhook will be dispatched for that notification. - Multiple webhook configs are supported. You can register several endpoints for the same project — each one will receive all matching events.
- Per-channel filtering is supported. Configure each webhook to receive events only for the channels you care about (email, sms, whatsapp).