message. The status is the machine-readable part.
The retry table
Why a 500 is safe to retry
500 Cannot send message means the gateway accepted your request and could not queue it. Any
charge is reversed.
That is a deliberate property of the ingress rather than an accident: credit is taken before queueing
and given back when queueing fails. So a retry costs one message, not two — which is exactly the
guarantee that makes an automatic retry defensible here and not on a 402.
Build for the receipt, not the response
The single most common integration mistake is treating a200 as delivery.
The submit response is sent before routing runs, so acceptance and delivery are unrelated
statements. A message can be accepted and then refused for an unapproved sender, dropped for want of
a route, or rejected by the carrier — and none of that can appear on the original call.
1
Store the messageId
It is the only thing that correlates your record with everything that follows.
2
Treat the send as 'submitted', not 'sent'
A separate state in your own model, so the difference stays visible.
3
Move to a final state only on a level 2 receipt
ACCEPTD and BUFFRED arrive as level 1 and another receipt follows.4
Time out on your own clock
A message that never gets a final receipt should expire in your system too, or it sits
“submitted” forever.
Errors you will meet in production, not in testing
403 — not an approved sender ID
403 — not an approved sender ID
Your account has sender-ID approval enforced. The
from you used has not been approved, or you
omitted it and your login name is not approved either.Not retryable, and not fixable in code — it needs an approval from your provider.403 — content does not match any approved template
403 — content does not match any approved template
Your account sends only approved wording. A message that works in staging and fails in production is
usually this.Whitespace is folded before matching, so extra spaces and newlines are not the cause. A changed
word, an added full stop, or a variable in a different position is.
402 with money still on the account
402 with money still on the account
Credit is reserved in blocks, not per message — around twenty messages’ worth at a time — and refills
are asynchronous. A message arriving while the next reservation is in flight is refused even though
credit remains.That one is transient: retry it. A
402 that persists with a healthy balance is not, and is worth
raising.400 — Unknown argument: custom-tlvs
400 — Unknown argument: custom-tlvs
Every other field treats
_ and - as the same character. custom_tlvs is the one exception and
keeps its underscore.Related
Every status
The full catalogue with what provokes each.
Delivery receipts
The half of the story the response cannot tell you.