Webhooks
Receive real-time delivery receipts at your own URL instead of polling.
Setup
Register a URL and choose which events to receive.
Add an endpoint from Settings → API Keys & Webhooks (or via the webhooks API). Choose which events to receive, or * for all. Each endpoint has its own signing secret.
Delivery payload
What we send when a message reaches a final state.
We POST a JSON body to your URL when a message reaches a final state:
{
"event": "message.delivered",
"mobile_number": "+233241234567",
"provider_message_id": "abc123",
"status": "delivered",
"timestamp": "2026-06-30T10:24:01+00:00"
}
Verifying the signature
Confirm a delivery really came from Hellio.
Every delivery is signed so you can confirm it came from Hellio. Two headers are sent:
X-Hellio-Signature— HMAC-SHA256 of the raw request body, keyed with your endpoint secret.X-Hellio-Timestamp+X-Hellio-Signature-V2— HMAC-SHA256 of"{timestamp}.{body}", for replay protection. Reject deliveries whose timestamp is too old.
Verify in your handler (PHP example):
$payload = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_HELLIO_TIMESTAMP'] ?? '';
$expected = hash_hmac('sha256', $timestamp.'.'.$payload, $secret);
if (! hash_equals($expected, $_SERVER['HTTP_X_HELLIO_SIGNATURE_V2'] ?? '')) {
http_response_code(401);
exit;
}
// ...process the event
Retries & failures
What happens when your endpoint is down.
Respond with a 2xx within 10 seconds to acknowledge. Non-2xx responses are retried with exponential backoff. An endpoint that keeps failing is automatically disabled, and the last error is recorded so you can diagnose and re-enable it.
Best practices
Build a reliable receiver.
- Always verify the signature before trusting a payload.
- Make your handler idempotent, keyed on
provider_message_id. - Return quickly and process heavy work asynchronously.
