Short answer
To receive delivery reports with a webhook, expose an HTTPS endpoint that accepts POST requests and register its URL with your messaging provider. Store each event, answer with a 2xx status quickly and process it asynchronously. Match events to your records by message ID, deduplicate them, and never let a late status overwrite a final one.
Set up the endpoint
During development, a tunnel gives a local server a public HTTPS URL.
- Expose an HTTPS endpoint that accepts POST requests with a JSON body.
- Give the URL to your provider so it is configured on the right account or sub-account.
- Send a test message to your own number and log the raw event body.
- Map the fields you need from real events, not from assumed field names.
- Keep sample payloads as test fixtures for each channel you use.
Which statuses will you receive?
Providers translate operator and channel receipts into their own status names. This normalised set is the one OmniChannels.ai uses across SMS, WhatsApp and other channels.
| Status | Meaning | Final? |
|---|---|---|
| SUBMITTED | Accepted and queued | No |
| SENT | Handed to the operator or channel | No |
| DELIVERED | Confirmed on the device | Usually; READ may follow |
| READ | Opened, on channels that report reads | Yes |
| FAILED | Not delivered, with a reason where available | Yes |
| REJECTED | Refused, for example an unregistered sender ID | Yes |
Make the handler reliable
- Store the raw body, then return 2xx. If storage fails, return 5xx so the event can be retried.
- Do the real work in a queue or worker, not before you respond.
- Deduplicate by message ID; the same status can arrive twice.
- Rank statuses so a late SENT never overwrites DELIVERED.
- Accept status values you do not recognise instead of failing.
- Never resend a message from inside the webhook handler.
Secure it and recover missed reports
Accept HTTPS only, use a long unguessable path, validate the body shape, and confirm state with a status call before irreversible actions such as refunds. If your endpoint was down, ask the provider to re-push receipts for the period or read statuses from its reporting endpoints.
Over SMPP, receipts arrive as deliver_sm on the bind instead of a webhook. Some operators return receipts late or not at all, so treat them as strong evidence, not proof of reading.
