Webhooks
Nopexo calls your server when something happens, so you don’t have to poll.
Set up an endpoint
In the dashboard open More → Developers → Webhooks, add your HTTPS URL and choose events (or all). Copy the signing secret (whsec_…) — it’s shown once. Use Send test event to try it.
Events
| Type | Sent when | data |
|---|---|---|
message.submitted | The network accepted a message (not yet delivered). | Message |
message.delivered | The network confirmed delivery to the phone. | Message |
message.failed | A message ended undelivered, expired, rejected or not sent. | Message (with `error`) |
otp.verified | A code was verified. | OtpRequest |
otp.failed | A verification failed after too many wrong codes. | OtpRequest + `reason` |
bulk.completed | All messages of a bulk batch have been handed to the network (or skipped). | Batch |
webhook.test | You pressed “Send test event” in the dashboard. | { message } |
The request
A POST with a JSON body and these headers:
| Header | Value |
|---|---|
Nopexo-Signature | t=1762596900,v1=5f2b… |
Nopexo-Event-Id | Same across retries — use it to ignore duplicates. |
Nopexo-Event-Type | e.g. message.delivered |
Nopexo-Delivery-Id | This endpoint’s delivery (shown in the delivery log). |
{
"id": "wev_4k8m2n6p9q3r7t1v",
"type": "message.delivered",
"createdAt": "2026-11-08T10:15:04.000Z",
"environment": "production",
"projectId": "prj_7kd2qm4x9t3c8f1v",
"data": {
"id": "msg_7kd2qm4x9t3c8f1v",
"to": "+233241234567",
"status": "delivered",
"final": true,
"batchId": null,
"…": "…"
}
}Verify the signature
v1 is the hex HMAC-SHA256 of t + "." + raw request body, keyed with your secret. Compute it over the raw bytes (not re-encoded JSON), compare in constant time, and reject timestamps older than 5 minutes.
# Compute the expected signature for a saved request body (body.json) and timestamp t:
printf '%s.%s' "$T" "$(cat body.json)" | openssl dgst -sha256 -hmac "$NOPEXO_WEBHOOK_SECRET"Responding, retries and the delivery log
- Answer with any 2xx within 10 seconds. Do slow work after responding.
- Anything else — an error status, a timeout, a refused connection — is retried up to 10 times with doubling delays (1 minute, 2, 4 … about 8½ hours in total).
- Events can arrive more than once and out of order. Deduplicate on the event
id, and use the status indatarather than the order events arrive. - Each attempt — status code, error, timing and your response — is in the dashboard’s delivery log, where you can also send an event again.
- An endpoint that fails 20 events in a row is disabled; re-enable it in the dashboard once it’s fixed.
Separate from provider webhooks
These are Nopexo’s webhooks to you. Nopexo’s own connections to SMS networks use separate endpoints and secrets that customers never see.
Rotating the secret
Choose New secret on the endpoint. Events are signed with the new secret immediately, so deploy it first (or accept both secrets briefly).