Handle Failures
When a message can't be sent or delivered, the API returns a structured error object. Here's what each error means and what to do about it.
Error structure
All errors follow the same shape:
{
"error": {
"type": "opted_out",
"message": "This contact has opted out of messages."
}
}Some errors include a detail object with field-level information:
{
"error": {
"type": "validation_error",
"message": "Could not perform the request with the provided parameters.",
"detail": {
"body": ["can't be blank"]
}
}
}Errors at send time
These errors come back synchronously when you call POST /accounts/:account_id/messages.
opted_out (422)
The recipient has previously replied STOP. Carriers require you to honour opt-outs. Do not attempt to send to this contact until they explicitly re-opt-in by texting START.
Fix: Check contact opt-out state before sending. Subscribe to contact.opted_out webhook events to update your database in real time.
demo_message_limit (403)
You've sent 25 messages from the demo number. The demo is for exploration — it's not for production use.
Fix: Register your business and purchase a phone number.
link_size_limit (422)
The message body contains more URLs than the campaign's registered includes allows, or the total URL content exceeds carrier limits.
Fix: Reduce the number of links in the message, or enable link shortening on your account.
no_phone_number (422)
No from number was specified and the account has no default phone number configured.
Fix: Either set a default phone number on the account in the dashboard, or always pass a from value.
phone_number_not_found (422)
The phone number ID passed as from doesn't exist or doesn't belong to this account.
Fix: Confirm the phone number ID is correct and belongs to the account you're sending from.
Errors at delivery (via webhooks)
These appear in message.failed webhook events after a message was initially accepted. The failure_reason field on the event contains the slug.
unreachable_destination
The recipient's number is disconnected, out of service, or unreachable on the carrier network.
What to do: Flag the contact as undeliverable in your system. Continued attempts to an unreachable number don't help and count against your volume.
Permanent undeliverability. When Surge receives
unreachable_destination,invalid_mobile_number, orunreachable_carrierfor a number, that number is permanently flagged as undeliverable in Surge's carrier database. All future sends to it — from any account — are cancelled immediately without hitting the carrier. If a number becomes reachable again (e.g., the subscriber reactivates their SIM), the flag clears when a successful delivery is recorded. This is why it's important to stop sending to failed numbers: it has no effect and your campaign volume quota is still consumed.
invalid_content_type
An MMS attachment's content type isn't supported by the recipient's carrier or handset.
What to do: Retry with a different format (prefer image/jpeg or image/png) or send the content as a link instead of an attachment.
message_filtered
The carrier's spam filter blocked the message. This can happen when message content matches spam patterns, when a campaign isn't fully approved, or when a number isn't attached to an active campaign.
What to do:
- Confirm the sending number is attached to an active, approved campaign
- Check that message content matches your registered campaign description and sample messages
- Avoid link shorteners that aren't trusted by carriers (use Surge's built-in link shortening instead)
Complete failure reason reference
All possible values for failure_reason in message.failed events:
| Reason | Meaning | Retryable? |
|---|---|---|
account_archived | The sending account has been archived | No |
account_locked | The platform has been locked due to a policy violation | No — contact support |
attachment_retrieval_error | Surge couldn't fetch the attachment URL when building the MMS | Yes — fix the URL or hosting |
blocked | The number has been explicitly blocked at the carrier level | No |
carrier_error | A transient error from the carrier network | Yes — Surge retries automatically |
configuration_error | The account's carrier credentials are misconfigured | No — contact support |
daily_message_cap_reached | The campaign's daily T-Mobile volume cap was hit | Yes — automatically next day |
geo_not_enabled | The destination country isn't in the platform's allowed regions | No — contact support to enable the region |
invalid_attachment_url | The attachment URL is malformed or not publicly accessible | Yes — fix the URL |
invalid_content_type | MMS attachment type unsupported by the recipient's carrier | Yes — use a supported format |
invalid_mobile_number | The number is not a valid mobile number (e.g., a landline) | No — permanently undeliverable |
matching_sender_and_receiver | The from and to numbers are the same | No — sending to yourself isn't supported |
message_filtered | Blocked by carrier spam filters or Surge's content policy | Investigate content before retrying |
message_too_big | The message body exceeds the 1,600-character limit | Yes — shorten the message |
mms_not_supported | The recipient's carrier or handset doesn't support MMS | Yes — Surge falls back to SMS automatically |
opted_out | The contact has opted out of this conversation | No — wait for explicit opt-in |
premium_rate_number | The destination is a premium-rate number | No |
suspected_fraud | Surge's fraud detection flagged the send | No — contact support |
unknown_destination | The number doesn't exist on any carrier network | No — permanently undeliverable |
unknown_error | An error occurred that Surge couldn't categorise | Investigate with support |
unreachable_carrier | The destination carrier network is unreachable | No — permanently undeliverable |
unreachable_destination | The number is disconnected or out of service | No |
unregistered_number | The sending number isn't registered with the carrier for this message type | No — contact support |
unsupported_route | No valid carrier route exists for this number pair | No — contact support |
Validation errors
validation_error (422)
A request field is missing or invalid. The detail map contains per-field errors.
{
"error": {
"type": "validation_error",
"message": "Could not perform the request with the provided parameters.",
"detail": {
"to": ["is not a valid E.164 phone number"],
"body": ["can't be blank"]
}
}
}Fix: Correct the flagged fields. Phone numbers must include the country code with a + prefix (e.g. +18015551234, not 8015551234).
Full error reference
See the Error Reference for the complete list of error types, HTTP status codes, and additional context.