Message status update event (message.status.updated)
Dispatched via HTTP POST to the configured webhook URL whenever an outbound SMS message transitions delivery status (moving into the sent or notsent folder, or updating its delivery/view state).
POST <client_webhook_url>
Table of contents
Callback summary
| Property | Value |
| Event type | "message.status.updated" |
| HTTP method | POST |
| Target endpoint | Per-message webhook.url (if specified when sending) or the user account's registered webhook url (configured via POST /api/v2/webhook or PUT /api/v2/webhook) |
| Content-Type | application/json |
| Signature header | X-Hub-Signature-256: sha256=<hex_digest> (when an account webhook secret is configured) |
| Expected client response | Any 2xx HTTP status code (e.g., 200 OK, 204 No Content); no response payload is required |
Important: Webhook requests are always formatted as a JSON array of event envelopes (WebhookEventEnvelope[]), even when delivering a single event (an array of length 1, up to 100 events per delivery request). If the webhook endpoint receives multiple event types, a single batch array may contain both "message.received" and "message.status.updated" events. Receiving endpoints must deserialize the root payload as an array and inspect each item's type discriminator.
Request headers (sent to client)
| Header | Presence | Description |
Content-Type |
Always | application/json |
X-Hub-Signature-256 |
When secret is configured |
Lowercase hexadecimal HMAC-SHA256 signature of the raw UTF-8 request body, prefixed with sha256= (e.g., sha256=a4c8e1f2...), computed using the account webhook's shared secret (whsec_...). Omitted if dispatching to a per-message webhook.url when no account-level webhook secret is configured. |
Webhook payload schema
Root array (WebhookDeliveryPayload)
| Type | Description |
object[] (WebhookEventEnvelope[]) |
Non-empty JSON array of 1 to 100 event envelope objects. |
Event envelope object (WebhookEventEnvelope)
| Field | Type | Description |
eventId |
string (UUID) | Unique identifier for this webhook event. Retained across retry attempts; use this value for idempotency and deduplication. |
time |
string (ISO 8601) | Timestamp when the event was generated (with timezone offset). |
type |
string | Event type discriminator. Set to "message.status.updated" for delivery status update events. |
user |
string | Username of the account that dispatched the outbound message. |
payload |
object (MessageStatusUpdatedPayload) |
Event-specific data object containing the delivery status transition details. |
payload object (MessageStatusUpdatedPayload)
| Field | Type | Presence | Description |
messageId |
string (UUID) | Always | Unique identifier of the outbound message. |
folder |
string | Always | Target message folder after the status transition: "sent" or "notsent". |
status |
string | Always | Updated delivery status state ("rejected", "submitted", "partiallysubmitted", "submitfailed", "delivered", "partiallydelivered", "deliveryfailed", "viewed"). |
statusDescription |
string | Always | Human-readable explanation or provider status report message corresponding to status (e.g., "Delivered", "Submit failed"). |
updatedAt |
string (ISO 8601) | Always | Timestamp when the status transition occurred. |
Message status values & target folders
| Status | Target folder |
Description |
"rejected" |
"notsent" |
Message was rejected prior to submission due to validation or policy rules. |
"submitfailed" |
"notsent" |
Transmission attempt to the telecom provider/SMSC failed. |
"submitted" |
"sent" |
Message was successfully submitted to the telecom provider/SMSC. |
"partiallysubmitted" |
"sent" |
Multi-part message was partially submitted to the provider network. |
"delivered" |
"sent" |
Message was confirmed delivered to the recipient handset. |
"partiallydelivered" |
"sent" |
Multi-part message was partially confirmed delivered. |
"deliveryfailed" |
"sent" |
Handset delivery failed (e.g., unreachable, expired, or invalid subscriber). |
"viewed" |
"sent" |
Recipient read or viewed the message payload (where supported). |
Delivery, retry & signature verification
1. Batching & retry policy
- Batch size: Up to
100queued events destined for the same webhook URL are batched into a single HTTPPOSTrequest and delivered sequentially in FIFO order. - Retries & backoff: If the client endpoint does not return a
2xxHTTP status code (or if the connection times out), delivery is retried up to20times using quadratic backoff:delay = base + (base × retry2) seconds, wherebase = 60 seconds. - Idempotency: Retried deliveries reuse the original
eventId. Receiving servers should track processedeventIdvalues to avoid duplicate processing.
2. Verifying X-Hub-Signature-256
- Read the
X-Hub-Signature-256header and verify that it starts withsha256=. - Capture the raw UTF-8 request body bytes before parsing or re-serializing JSON.
- Compute the HMAC-SHA256 hash over the raw body bytes using the UTF-8 bytes of the shared
secret(whsec_...) returned when the account webhook was configured. - Format the hash as lowercase hexadecimal prefixed with
sha256=and compare it against the header value using a constant-time comparison function (e.g.,CryptographicOperations.FixedTimeEquals).
Examples
1. Message delivered status update (status: "delivered")
Single message.status.updated event delivered when an outbound SMS is confirmed delivered to the recipient handset.
Webhook request (sent to client):
POST /api/sms/inbound HTTP/1.1 Host: client.example.com Content-Type: application/json X-Hub-Signature-256: sha256=a4c8e1f2b3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c
[
{
"eventId": "91a27b9c-53e3-4d43-9831-29e1ebba5122",
"time": "2026-10-21T10:21:00+02:00",
"type": "message.status.updated",
"user": "http_user",
"payload": {
"messageId": "26385eed-6a6c-4deb-b549-dc055211a71d",
"folder": "sent",
"status": "delivered",
"statusDescription": "Delivered",
"updatedAt": "2026-10-21T10:21:00+02:00"
}
}
]
Expected client response (2xx):
HTTP/1.1 200 OK
(No response payload is required)
2. Message delivery failed status update (status: "deliveryfailed")
Single message.status.updated event delivered when handset delivery fails after submission.
Webhook request (sent to client):
POST /api/sms/inbound HTTP/1.1 Host: client.example.com Content-Type: application/json X-Hub-Signature-256: sha256=b5d9f2a3c4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d
[
{
"eventId": "4f5a6b7c-8d9e-4f0a-9b1c-2d3e4f5a6b7c",
"time": "2026-10-21T10:22:00+02:00",
"type": "message.status.updated",
"user": "http_user",
"payload": {
"messageId": "8f3b2a1c-9d4e-4f5a-b6c7-1a2b3c4d5e6f",
"folder": "sent",
"status": "deliveryfailed",
"statusDescription": "Delivery failed",
"updatedAt": "2026-10-21T10:22:00+02:00"
}
}
]
3. Batch delivery of multiple status updates
Multiple status transition events batched into a single HTTP POST request.
Webhook request (sent to client):
POST /api/sms/inbound HTTP/1.1 Host: client.example.com Content-Type: application/json X-Hub-Signature-256: sha256=c6e0a3b4d5f60718293a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e
[
{
"eventId": "91a27b9c-53e3-4d43-9831-29e1ebba5122",
"time": "2026-10-21T10:21:00+02:00",
"type": "message.status.updated",
"user": "http_user",
"payload": {
"messageId": "26385eed-6a6c-4deb-b549-dc055211a71d",
"folder": "sent",
"status": "delivered",
"statusDescription": "Delivered",
"updatedAt": "2026-10-21T10:21:00+02:00"
}
},
{
"eventId": "d34e6f8a-72b1-4c55-9012-34f5a6b7c8d9",
"time": "2026-10-21T10:21:02+02:00",
"type": "message.status.updated",
"user": "http_user",
"payload": {
"messageId": "9e4c3b2d-0e5f-4a6b-c7d8-2b3c4d5e6f7a",
"folder": "notsent",
"status": "submitfailed",
"statusDescription": "Submit failed",
"updatedAt": "2026-10-21T10:21:02+02:00"
}
}
]
Client response requirements
| Client HTTP status code | Dispatcher behavior |
2xx (200 OK – 299) |
Delivery is marked as acknowledged and removed from the dispatch queue. No response payload is required (any response body is ignored). |
3xx, 4xx, 5xx, or Network Timeout |
Delivery is treated as failed and scheduled for retry according to the quadratic backoff policy (up to 20 retries). |