Hi, I'm William. I work in CPaaS / A2P messaging, mostly on international SMS delivery for verification, notifications, and alerts.
The most common bug report I hear from developers is:
"The API returned success, but the user never got the code."
That success usually only means the message was accepted. It says almost nothing about whether it was delivered — or delivered in time to matter. For OTP, a code that arrives after 60 seconds is effectively a failed code.
Here are the 4 checks I run first.
1. Route: Is there a direct route to that carrier?
A cheap route is not a cheap message if it never lands.
For international OTP, ask:
- Is this a direct carrier connection, or is it hopping through aggregators?
- Is the sender ID allowed in the destination country? Many markets reject or rewrite unregistered sender IDs.
- Is this route built for OTP traffic, or for bulk marketing traffic? They fail in different ways.
Developer check: Don't test with one number in one country and call it done. Test your top 3 user countries separately. Delivery behaviour is per-country, per-carrier — not global.
2. Speed: Measure delivery time, not send time
Log two timestamps, not one:
t0 = when your system requested the send
t1 = when the delivery receipt (DLR) says "delivered"
delivery_time = t1 - t0
If you only log your API call, you're measuring your own server — not the user's experience.
A practical alert I like:
# Pseudocode: flag slow OTP delivery for investigation
if delivery_time_seconds > 30:
log_warning("slow_otp_delivery", country=country, carrier=carrier)
if delivery_status != "DELIVERED":
trigger_fallback() # voice OTP, another route, or a retry with a new code
3. Receipts: Treat the delivery receipt as part of your product
No DLR handling means you're flying blind. At minimum, handle:
| Receipt state | What it usually means | What to do |
|---|---|---|
DELIVERED |
Handset received it | Stop retrying, mark verification as sent |
UNDELIVERED / FAILED
|
Carrier or handset rejected it | Check number format, sender ID, and route |
EXPIRED |
Sat too long in transit | Your route is too slow for OTP — change it |
| No receipt at all | Provider gives you no visibility | That's a provider problem, not a user problem |
Wire DLR webhooks into the same dashboard where you watch signup conversion. A sudden drop in delivered rate in one country almost always shows up there before users start complaining.
4. Support: Can a human look at one message ID?
When a single important user can't log in, "check our status page" is not support.
Before you commit to a provider, test this in the trial — not after you sign:
- Send a real message.
- Give support that one message ID.
- See if they can tell you the carrier, the delivery state, and why it failed.
If they can't trace one message, they can't help you debug a country-wide drop either.
A quick pre-launch checklist
- [ ] Phone numbers normalised to E.164 (
++ country code) before sending - [ ] Delivery time (not just send time) logged per country
- [ ] DLR webhook handled, including
EXPIREDandUNDELIVERED - [ ] Fallback ready: second route, voice OTP, or WhatsApp where it makes sense
- [ ] One-message trace tested with support using a real message ID
Disclosure: I work at Chuanglan (a CPaaS provider). If you want a sandbox to run the checks above, new accounts currently get 100 free SMS messages + a small API credit to test with: https://www.chuanglan.com/control/regist?exclusive_code=LKFHZWBU — no card needed for the trial. Either way, run the 4 checks on whatever provider you use now. Most "mystery" OTP failures turn out to be check #1 or #3.
What does your OTP failure dashboard look like today — do you track delivery time per country, or just send success rate? I'd genuinely like to compare notes in the comments.
Top comments (0)