Delivery Reports
A delivery report (DLR) is the asynchronous, per-message acknowledgement that tells you the final outcome of an SMS — whether it was delivered to the handset, rejected by the operator, expired, or failed. Without a DLR, all you know is that the platform accepted the message for sending.
How to Receive DLRs
DLRs are pushed to an HTTPS webhook you control as soon as the operator reports the final state. Enable them by setting enableDelivery: true on your send request and configuring the destination URL in the wholesale portal under Settings → Webhooks.
Typical end-to-end latency from submission to a DELIVERED callback is a few seconds.
Webhook Payload
DLRs are delivered via HTTP POST to the URL you configure. The payload identifies the message and gives the new status.
{
"id": "msg_abc123",
"globalId": "req_1234567890",
"destination": "+393351234567",
"status": "DELIVERED",
"statusCode": 0,
"submitDate": "2026-05-14T10:23:11.000+0200",
"doneDate": "2026-05-14T10:23:14.221+0200",
"operator": "TIM",
"parts": 1
}
Field reference
| Field | Meaning |
|---|---|
id | Per-message identifier returned by the send response |
globalId | Correlation ID for the original submission |
destination | The recipient phone number |
status | Final state (see table below) |
statusCode | Numeric code matching the status |
submitDate | When the platform accepted the message |
doneDate | When the final state was reached |
operator | Receiving mobile operator (best effort) |
parts | Number of SMS parts billed |
DLR Status Codes
| Status | Code | Meaning | Billable? |
|---|---|---|---|
DELIVERED | 0 | The handset confirmed reception | Yes |
BUFFERED | 1 | Stored on the operator, not yet delivered | Yes |
EXPIRED | 2 | Validity period elapsed before delivery | Yes |
REJECTED | 3 | The operator rejected the message | No |
UNDELIVERABLE | 4 | Wrong number, blacklisted, opted-out | No |
UNKNOWN | 5 | No final state received within the validity window | Depends |
FAILED | 6 | Internal failure before submission to operator | No |
Billing rules can vary by operator; the wholesale portal is the source of truth for your account.
Webhook Requirements
- HTTPS only — Plain HTTP is not accepted.
- Respond with
200 OKwithin 10 seconds — Otherwise the DLR is retried. - Idempotency — The same DLR may be retried on transient errors. Use
idas the dedup key. - Order is not guaranteed — A
BUFFEREDevent may arrive after aDELIVERED. UsedoneDateto reconcile. - Retry policy — Failed deliveries are retried with exponential back-off for up to 24 hours, then dropped.
Example Webhook Handler
- Python (Flask)
- Node.js (Express)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.post("/webhook/dlr")
def dlr():
payload = request.get_json(force=True)
msg_id = payload["id"]
status = payload["status"]
# Persist; downstream processing should be async.
save_dlr(msg_id, status, payload)
return jsonify({"ok": True}), 200
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/dlr', (req, res) => {
const { id, status } = req.body;
saveDlr(id, status, req.body); // your queue / DB
res.status(200).json({ ok: true });
});
app.listen(8080);
Next Steps
- Sending SMS — Make sure
enableDelivery: trueon your sends. - Inbound SMS — Receive replies on rented numbers.
- Error Handling — Map status codes to user-facing actions.