Test-mode keys (rb_test_*) route every send to a simulator that:
- Does not contact real carriers.
- Does not debit your wallet.
- Does fire webhooks with realistic delivery receipts.
- Does return deterministic outcomes based on magic numbers.
You build your whole integration — including DLR handling and error branching — before a single real kobo changes hands.
Creating a test key
In the dashboard: API keys → Create → check “test mode”. The returned key is prefixed rb_test_*.
Keep live and test keys in separate env vars. Convention: ROBASE_API_KEY for prod, ROBASE_TEST_KEY for dev/CI.
Magic numbers
Send to specific numbers to simulate outcomes:
| Number ending | Behavior |
|---|
…00000000001 (e.g. +2340000000001) | Delivers in ~100 ms (sms.delivered webhook) |
…00000000002 | Rejected synchronously with phone_invalid |
…00000000003 | Delivers after 30 s (slow-carrier simulation) |
…00000000004 | Rejected with dnd_blocked |
Any other E.164 number defaults to the …0001 behavior (fast delivery).
Example: exercise every branch
import { Robase } from '@robase/node';
const pm = new Robase(process.env.ROBASE_TEST_KEY!);
// Happy path
await pm.sms.send({ to: '+2340000000001', from: 'TESTCO', body: 'ok' });
// Permanent failure — your code should NOT retry this.
try {
await pm.sms.send({ to: '+2340000000002', from: 'TESTCO', body: 'invalid' });
} catch (e) { console.log(e.type); } // 'phone_invalid'
// DND block — your code should exclude this recipient from future sends.
try {
await pm.sms.send({ to: '+2340000000004', from: 'TESTCO', body: 'dnd' });
} catch (e) { console.log(e.type); } // 'dnd_blocked'
// Slow delivery — your webhook handler must handle delayed DLRs.
await pm.sms.send({ to: '+2340000000003', from: 'TESTCO', body: 'slow' });
// `sms.delivered` fires ~30 s later.
Webhooks in test mode
Test-mode sends fire webhooks exactly the same way live sends do. Register a test webhook at a tunnel URL (ngrok, cloudflared) while developing, then swap for your production URL when you ship.
The event payload includes test_mode: true in the data object so you can branch or log differently:
{
"type": "sms.delivered",
"data": {
"sms_id": "...",
"test_mode": true,
...
}
}
Test mode in CI
A common CI pattern:
# .github/workflows/ci.yml
env:
ROBASE_API_KEY: ${{ secrets.ROBASE_TEST_KEY }}
Every integration test hits real Robase with a test key. You get genuine end-to-end confidence without cost, and you catch API contract regressions immediately.
What test mode doesn’t simulate
Carrier-specific rejections. Some MNOs filter certain keywords (gambling, loan promos) that our simulator passes through. Test mode catches schema / wallet / compliance issues — it doesn’t replace a smoke test on real numbers before launch.