TXTImpact can deliver a delivery report (DR) for every outbound SMS/MMS to your system over HTTPS. Reports are fired when the carrier returns a final status — delivered or failed — for a message you sent through the Omnihub platform.
When is a delivery-report webhook fired?
A delivery-report webhook fires once per message, when the underlying carrier reports a terminal status to TXTImpact:
- delivered — the message reached the recipient handset (status code
11) - failed — the carrier rejected delivery, or the message expired (status code
19)
Intermediate states (pending/queued/in-flight) and read-receipt sub-states are not sent. You get one webhook per message representing the final outcome.
Configuration
Configure each VAS user's delivery report destination in the TXTImpact dashboard under Integrations → Delivery Reports:
- Pick the user account that will own the report stream.
- Choose Web Hook URL and enter your endpoint (HTTPS recommended).
- Save. Delivery reports for messages sent by that user begin flowing immediately.
You can also choose Email as the destination instead of a webhook; in that case TXTImpact sends a human-readable email only for failed messages.
Authentication (X-W2A-Token header)
To prove the request originated from TXTImpact, set an opaque shared secret in your Account → VAS Attributes → Webhook Token. TXTImpact echoes it back on every request as:
X-W2A-Token: <your-secret>Your endpoint should reject any request whose X-W2A-Token header doesn''t match your stored copy. Empty/unset = no header sent (no verification).
Sample validator (Python/Flask):
incoming = request.headers.get("X-W2A-Token")
if incoming != STORED_W2A_TOKEN:
return Response(status=401)
HTTP request format
Method
POST
Content Type
application/json; charset=utf-8
Headers
| Header | Notes |
|---|---|
Content-Type | application/json |
X-W2A-Token | Optional shared-secret echo (see Authentication above) |
Sample JSON Body
{
"messageId": "af8d84df7e1d436d89e00a47815410c2",
"status": "delivered",
"statusCode": "11",
"statusMessage": "Message delivered",
"from": "15551234567",
"mobileNumber": "15557654321",
"timestamp": "2026-05-14T15:40:08Z"
}Field Definitions
| JSON Field | Type | Notes |
|---|---|---|
messageId | string | V2 message identifier returned when you submitted the original send. Use this to correlate the DR back to the original message in your system. |
status | string | Human-readable label: delivered, failed, or unknown (lowercase). |
statusCode | string | TXTImpact internal status code (e.g. 11 = delivered, 19 = failed). When the underlying carrier error has a mapping, this is the mapped TXTImpact code; otherwise it is the raw provider code. |
statusMessage | string | Friendly description of the status. Empty string when no description is available. |
from | string | Originating phone number / shortcode (E.164 digits, no +). |
mobileNumber | string | Recipient phone number (E.164 digits, no +). |
timestamp | string | When the carrier-reported status was received, ISO-8601 UTC (YYYY-MM-DDTHH:MM:SSZ). |
Notes:
- All string fields default to
""when no value is available — nevernull. Your parser does not need to handle JSON nulls. The original message body is intentionally not included in the DR payload — you already have it from when you submitted the send. Correlate using
messageId.
Expected Response & Retry Policy
Your endpoint must respond with a 2xx HTTP status within 30 seconds. Any non-2xx, timeout, or transport-level failure triggers an automatic retry on the schedule below:
| Attempt | Wait before next try |
|---|---|
| 1 (initial) | 30 seconds |
| 2 | 2 minutes |
| 3 | 10 minutes |
| 4 | 30 minutes |
| 5 | 1 hour |
| 6 | 3 hours |
| 7 | terminal — no further retries |
Total retry window is ~5 hours over 7 attempts. After that the DR is marked permanently failed and.
Recommended Implementation Notes
- Treat all fields as strings and parse defensively.
- Use
messageIdas your idempotency key — TXTImpact may retry the same DR if your endpoint returned a transient error. Persist the first successful processing and ignore duplicates. - Validate the
X-W2A-Tokenheader on every request; reject unauthorized payloads with401. Respond with
200 OKas quickly as possible. Defer any heavy processing to a background worker so you don't hit the 30-second timeout.