Test keys and the sandbox
What test API keys do, which countries they cover, and the simulated responses they return.
A test key (esms_test_...) lets you build and test your whole
integration without sending a real SMS or spending balance. Create one in the
dashboard under API keys, choosing Environment: Test. It works on the
same endpoints as a live key - only the key changes.
What a test key does
- Nothing reaches a real phone network and nothing is charged. Your balance is never touched.
- Every African country works - all 54, including countries still marked Coming soon for live sends. You do not need to enable a country first.
- Numbers are validated exactly like live sends, so format errors show up in testing, not in production.
- Any sender ID is accepted, including one you have not registered yet, so
you can test with the name you plan to use. The single-send response tells
you whether that sender would pass live:
live.sender_approvedisfalseandlive.sender_noteexplains what to do (request it under Sender IDs, or leavesender_idout to use the default). A live key still refuses an unapproved sender withsender_not_approved, so check that flag before you switch keys. - Responses are realistic: real segment and encoding calculation, the price a
live send would cost, a
test_...message id and"livemode": false. - The delivery lifecycle is simulated (
queued->submitted->delivered) and your webhook fires with"livemode": false. - Test messages only appear in your test logs. A test key never sees live messages and a live key never sees test ones.
Example
curl https://sms.esmsafrica.io/api/messages/send \
-H "Authorization: Bearer esms_test_xxx" \
-H "Content-Type: application/json" \
-d '{"to":"+237677123456","text":"Your code is 482913"}'{
"id": "test_43105d4651d54e3e9d762b200f64508e",
"status": "queued",
"environment": "test",
"livemode": false,
"segments": 1,
"encoding": "GSM7",
"cost": 30.0,
"cost_currency": "XAF",
"route": "ESMS_CM",
"balance_after": null,
"note": "Sandbox message - simulated, not sent to a real network and not billed.",
"live": {
"country": "CM",
"availability": "live",
"country_enabled": false,
"note": "Enable this country for live sends at https://sms.esmsafrica.io/countries.",
"sender_approved": true
}
}The live object tells you what will happen when you switch to a live key:
| Field | Meaning |
|---|---|
availability | live - we deliver there today. coming_soon - live sends are refused (and not charged) until it opens. |
country_enabled | Whether live sends to this country are already enabled on your account. If false, enable it before going live. |
sender_approved | Whether the sender_id you sent (or the default, if you left it out) is approved for this country. If false, a live send with this sender is refused with sender_not_approved. |
sender_note | Only when sender_approved is false: which senders are approved for the country and how to request yours. |
With an unregistered sender the same request still returns 200, flagged:
"live": {
"country": "CM",
"availability": "live",
"country_enabled": false,
"note": "Enable this country for live sends at https://sms.esmsafrica.io/countries.",
"sender_approved": false,
"sender_note": "Sender ID 'MyBrand' is not approved for CM yet, so a live send would be refused (sender_not_approved). Request it under Sender IDs in the dashboard, or omit sender_id to use the default. Approved for CM: eSMS, eSMSAfrica."
}Simulated outcomes
Pick the outcome with the last 4 digits of the number. Any other number is delivered.
| Number ends in | Simulated result |
|---|---|
| any other digits | delivered |
...0000 | undelivered |
...0002 | sent (no final delivery report) |
...0003 | failed (rejected by the network) |
...0404 | failed (unknown subscriber) |
...0009 | HTTP 402 insufficient balance |
Verify (OTP) in test mode
POST /api/verify/start with a test key does not send an SMS. The code is
always 000000, so you can test the full start and check flow:
curl https://sms.esmsafrica.io/api/verify/start \
-H "Authorization: Bearer esms_test_xxx" \
-H "Content-Type: application/json" \
-d '{"to":"+237677123456"}'Bulk sends
POST /api/messages/send-bulk with a test key accepts recipients in any mix
of African countries. Each recipient gets a simulated outcome as above.
Going live
- Create a live key (
esms_live_...). - Enable each country you send to.
- Make sure every sender you use shows
live.sender_approved: truein test (request any that do not under Sender IDs). - Top up your balance.
- Swap the key. No other code change is needed.
If a live send is refused, the error says why and nothing is charged:
code | What to do |
|---|---|
country_not_enabled | Enable the country in the dashboard. |
destination_unavailable | The country is Coming soon for live sends. |
sender_not_approved | Request the sender ID for that country, or leave sender_id out. |
insufficient_balance | Top up. |