API reference/phone
Receive delivery status webhooks from an SMS provider
POST/api/v1/phone/feedback/{provider}/{feedbackKey}
- authentication
- Feedback key in the URL
- billing
- Free.
The webhook target for an SMS provider's delivery status callbacks, the phone counterpart of the email bounce webhook. Providers cannot send bearer tokens, so the account's feedback key rides in the URL (the same key as for email, generated or rotated on the dashboard); the provider name picks the payload adapter. Twilio and MessageBird post form fields, Vonage and Sinch JSON; both are read. Only definitive statuses become outcomes; queued, sent, buffered, accepted and unknown are ignored.
| Provider | Path | Mapping |
|---|---|---|
| twilio | /api/v1/phone/feedback/twilio/{key} |
Message status callback: delivered → delivered · undelivered and failed → undelivered. To is the number; the callback carries no timestamp, so the outcome is dated on receipt. |
| vonage | /api/v1/phone/feedback/vonage/{key} |
SMS API receipts (msisdn, status, message-timestamp) and Messages API status webhooks (to, status, timestamp): delivered → delivered · expired, failed, rejected and undeliverable → undelivered. |
| messagebird | /api/v1/phone/feedback/messagebird/{key} |
Status reports (recipient, status, statusDatetime), configured as POST: delivered → delivered · delivery_failed and expired → undelivered. |
| sinch | /api/v1/phone/feedback/sinch/{key} |
Per-recipient reports (recipient, status, at) and batch reports (statuses[].recipients): Delivered → delivered · Failed, Expired, Rejected and Aborted → undelivered. |
Throttled at 120 requests per minute per IP. Outcomes recorded here behave exactly like ones reported through POST /api/v1/phone/feedback: they feed the account's own later lookups and its suppression list, and are never billed.
Parameters
| name | in | type | description |
|---|---|---|---|
| providerrequired | path | string | Which provider's payload format to expect. One of: twilio, vonage, messagebird, sinch. |
| feedbackKeyrequired | path | string | The account's feedback key, which starts with `fb_`. |
Request body
The provider's native callback payload, as JSON or form fields.
Example request
curl -X POST https://spaw.co/api/v1/phone/feedback/twilio/fb_… \
-H "Content-Type: application/json" \
-d '{
"MessageSid": "SM0f2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d",
"To": "+447911012345",
"MessageStatus": "undelivered",
"ErrorCode": "30003"
}'Responses
200The outcomes the payload carried were recorded.
{
"success": true,
"data": {
"recorded": 1,
"skipped": 0
}
}401The feedback key does not exist or was rotated.
{
"success": false,
"error": {
"code": "INVALID_FEEDBACK_KEY",
"message": "That feedback key does not exist or was rotated.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}404The provider path is not one of twilio, vonage, messagebird, or sinch.
{
"success": false,
"error": {
"code": "UNKNOWN_FEEDBACK_PROVIDER",
"message": "Supported providers: twilio, vonage, messagebird, sinch.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}429Over 5 requests per second for the key. Retry after the limit resets.
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after the limit resets.",
"request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
}
}Error codes
Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.