Hellio Messaging
Docs /Webhooks

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.
Was this page helpful? Thanks for the feedback! Still stuck? Talk to our team