Delivery receipts webhook
Strategic Mobile sends a webhook POST request to your callbackUrl when an outbound SMS message has a delivery status update. The request body follows the DeliveryReceiptWebhook schema and is sent as Content-Type: application/json. Your endpoint should return any 2xx response after it accepts the receipt.
Request
| Property | Value |
|---|---|
Method | POST |
Target | The per-message callbackUrl supplied on submission, or the default delivery receipt URL configured on your account. |
Content-Type | application/json |
Payload | DeliveryReceiptWebhook |
Expected response | Any 2xx status code. |
Idempotency key | msgId |
Webhook endpoint authentication is customer-controlled. If your endpoint requires authentication, configure the required headers or credentials for your account or callbackUrl. Strategic Mobile does not require a fixed API security scheme for this webhook receiver.
Payload fields
The public status field is a simplified five-value enum ( QUEUED, SENT, DELIVERED, FAILED, UNKNOWN). Required fields are msgId and status; treat the rest as optional.
| 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. Required. Use as your idempotency key. |
channel | string | null | 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. Required. Enum: QUEUED, SENT, DELIVERED, FAILED, UNKNOWN. |
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. |
Example payload
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"
}Recommended handling
Return a fast 2xx after minimal validation and durable acceptance, then process the receipt 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 (idempotent on msgId),
// then acknowledge with a 2xx response.
await upsertMessageStatus(dlr);
return res.status(200).json({ ok: true });
}HTTP/1.1 200 OK
Content-Type: application/json
{
"ok": true
}- Your endpoint should return a 2xx response after accepting the webhook. Use msgId as your idempotency key because webhook delivery can be retried.
- Acknowledge quickly after minimal validation and durable acceptance, then process the receipt asynchronously in the background.
- The webhook payload always includes msgId and status. Additional delivery metadata, such as accountId, servicePlanId, from, to, body, errorCode, errorMessage, segments, country, mccmnc, createdAt, sentAt, and updatedAt, may be included when available.
- Treat the public status as the simplified five-value enum (QUEUED, SENT, DELIVERED, FAILED, UNKNOWN); do not depend on internal carrier states.
- If you miss a webhook, query the current state on demand with GET /messages/sms/{msgId}; the response uses the same public status fields as DeliveryReceiptWebhook.
Related
- Delivery receipts — delivery state semantics and the on-demand
GET /messages/sms/{msgId}lookup.