Delivery receipts
Delivery Receipts, also known as DLRs, report the final or intermediate delivery state of an outbound SMS message. Developers can receive them through a webhook callback or query them through the API with GET /messages/sms/{msgId}. Requests are authenticated with a Bearer token and scoped by your servicePlanId.
Terminology
Delivery Receipt, Delivery Report, and DLR all refer to the same thing: the delivery status event for an outbound (MT) SMS message. Strategic Mobile exposes it two ways — pushed to your endpoint as a webhook callback, and pulled on demand from the API.
1. GET delivery receipt
GET /messages/sms/{msgId} · developer → Strategic Mobile API
Returns the current delivery record (the delivery receipt) for a message identified by its msgId. This is the confirmed way to query delivery state on demand. Only messages belonging to the authenticated caller are returned. Authenticate with a Bearer token and pass your servicePlanId as a query parameter to authorize and scope the lookup.
Parameters
| Field | Type | Description |
|---|---|---|
msgId | string (path) | Server-generated message identifier (UUID) returned by Send SMS. Required path parameter. |
servicePlanId | string (query) | Service plan that owns the message. Authorizes and scopes the lookup. Required query parameter. |
Responses
| Status | Description |
|---|---|
| 200 | The current per-message delivery record (the delivery receipt). |
| 400 | The message identifier is missing or malformed. |
| 401 | Missing Bearer token, malformed Authorization header, or invalid token. |
| 403 | Token is valid but not authorized for the requested account/servicePlan mapping. |
| 404 | No message with the given identifier exists for this caller. |
A 404 means no message with that identifier exists for the authenticated caller; it does not by itself indicate a delivery failure. Request samples (cURL, JavaScript, Python) are shown in the panel.
Newly accepted messages: a delivery receipt may not be queryable immediately after the message is accepted. If GET returns 404 right after a successful 202 response, retry after a short delay. The receipt becomes available once the message record is ready for status retrieval.
Delivery status semantics
The public status field is a simplified five-value enum. Internal carrier lifecycle states are mapped onto these values.
| Status | Meaning |
|---|---|
QUEUED | Message accepted and queued for delivery to the carrier; no delivery confirmation yet. Non-terminal. |
SENT | Message handed off toward the carrier; awaiting a final delivery receipt. Non-terminal. |
DELIVERED | Delivered. Carrier confirmed delivery to the handset. Final state. |
FAILED | Failed. Message could not be delivered. See errorCode / errorMessage for the reason. Final state. |
UNKNOWN | Unknown. A reliable carrier/provider delivery status is not available. Final or indeterminate state. |
2. Delivery receipt webhook
Strategic Mobile → your endpoint
When a message status changes, a delivery receipt is POSTed to your DLR callback URL — either the per-message callbackUrl supplied on submission or the default DLR URL configured on your account. This is a webhook receiver example: it shows the HTTP POST your endpoint receives, not a request you send. Your endpoint must accept an HTTPS POST with Content-Type: application/json and acknowledge with 200. Required fields are msgId and status.
POST https://client.example.com/sms/status
Content-Type: application/json
{
"accountId": "111111111111111111111111",
"servicePlanId": "222222222222222222222222",
"msgId": "019ee2da-e515-7322-805f-1ac6ce82f20f",
"channel": "SMS",
"direction": "MT",
"from": "+15550001111",
"to": "+15550002222",
"body": "Your verification code is 123456",
"status": "DELIVERED",
"errorCode": "000",
"errorMessage": "No error",
"segments": 1,
"price": 0.0007,
"ptf": 0.0065,
"currency": "USD",
"mccmnc": "313790",
"country": "PR",
"createdAt": "2026-06-20T04:20:40.982Z",
"sentAt": "2026-06-20T04:20:41.000Z",
"updatedAt": "2026-06-20T04:20:41.087Z"
}Delivery receipt payload fields
| Field | Type | Description |
|---|---|---|
accountId | string | null | Resolved account identifier (business partner) that owns the message. |
servicePlanId | string | null | Service plan that owns the message. |
msgId | string | Server-generated message identifier (UUID) the receipt refers to. |
channel | string | Message channel. Enum: SMS. |
direction | string | Message direction (for example MT). |
from | string | null | Sender address. |
to | string | null | Destination address. |
body | string | null | SMS message text. |
status | string | Simplified public delivery status. Enum: QUEUED, SENT, DELIVERED, FAILED, UNKNOWN. Internal carrier states are mapped onto this public status model. |
errorCode | string | 000 on success; otherwise the failure code. |
errorMessage | string | Human-readable status/error message. |
segments | integer | null | Number of SMS segments. |
price | number | null | Charged price, when available. |
ptf | number | null | Pass-through fee, when available. |
currency | string | null | Currency of price/ptf. |
mccmnc | string | null | Mobile country/network code. |
country | string | null | ISO 3166 country code. |
createdAt | string<date-time> | When the record was created. |
sentAt | string<date-time> | null | When the message was sent, when available. |
updatedAt | string<date-time> | When the record was last updated. |
Acknowledge each delivery receipt with a 2xx response after you accept it, and use msgId as your idempotency key because a receipt for the same message may be delivered more than once.
Recommended handling
For delivery receipts, return a fast 200 OK after minimal validation and durable acceptance, then process it asynchronously in the background.
export async function deliveryReceiptHandler(req, res) {
// Webhook endpoint authentication is customer-controlled. Validate the
// request using whatever scheme you configured for your own endpoint.
const dlr = req.body;
if (!dlr || !dlr.msgId || !dlr.status) {
return res.status(400).json({ ok: false, error: "invalid payload" });
}
// Persist or update your local status record, then acknowledge.
await upsertMessageStatus(dlr);
return res.status(200).json({ ok: true });
}HTTP/1.1 200 OK
Content-Type: application/json
{
"ok": true
}- Acknowledge the webhook quickly with 200 after basic validation, then process it asynchronously in the background.
- Use msgId as the idempotency key; treat delivery-receipt handling as idempotent, since the same msgId may be received more than once.
- Required fields on the webhook payload are msgId and status; treat other fields as optional.
- Use the GET delivery receipt endpoint to confirm the final state if you miss a webhook delivery.
- Treat the public status as the simplified five-value enum (QUEUED, SENT, DELIVERED, FAILED, UNKNOWN); do not depend on internal carrier states.
Operational notes
- Query the current delivery state on demand with GET /messages/sms/{msgId}; the response is the same DeliveryReceipt object delivered by the webhook.
- Make your webhook endpoint idempotent on msgId, since a delivery receipt for the same message may be delivered more than once.
- The public status is the simplified five-value enum (QUEUED, SENT, DELIVERED, FAILED, UNKNOWN); internal carrier states are mapped onto these values.
- price and ptf populate as billing data becomes available and may be null on earlier status updates.