Playground
Use the CastBrick SMS Playground to test your integration with sandbox API keys and intercepted messages.
SMS Playground
The CastBrick Playground is a zero-cost, zero-risk environment for testing your SMS integration. Think of it as Mailpit for SMS — messages are intercepted, never sent to a real phone, and no credits are deducted.
Access the Playground in your dashboard at Settings → Playground or directly at
/playground.
Why use the Playground?
| Scenario | Without Playground | With Playground |
|---|---|---|
| Sender ID pending approval | Requests blocked | Any sender accepted |
| Testing your integration | Real SMS sent, costs money | Intercepted, 0 cost |
| CI/CD automated tests | Dangerous in production | Safe |
| Onboarding / demos | Burns credits | Free |
| Debugging webhooks | Real charges | No deduction |
Your Sandbox API Key
Every CastBrick account has two API keys created automatically:
| Key | Prefix | Behaviour |
|---|---|---|
| Live | cb_live_... | Real SMS sent, credits deducted |
| Sandbox | cb_test_... | SMS intercepted, 0 credits, any sender |
Find your keys at API Keys in the dashboard. Each key shows a Live or Test badge.
You can also create a new playground key from API Keys → Create Key by selecting Playground Access. That generates a cb_test_... sandbox key and it can be used directly in the Playground inbox.
Sending a test SMS
Use your cb_test_ key exactly like your live key — same endpoint, same request body.
curl -X POST https://api.castbrick.co/v1/sms/send \
-H "Authorization: Bearer cb_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"recipients": ["+244923456789"],
"content": "Hello from Sandbox! Your OTP is 123456.",
"senderId": "MyBrand"
}'Response (identical to live):
{
"messageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "queued",
"recipientCount": 1,
"timestamp": "2025-03-30T14:23:01Z"
}The message appears in the Playground inbox within seconds.
What the Playground shows
Once a message is sent with your sandbox key, open the Playground in the dashboard:
┌──────────────────────────────────────────────────────────────┐
│ Playground │
│ Test your SMS integration without sending real messages │
│ │
│ ┌─ Sandbox banner ──────────────────────────────────────┐ │
│ │ SMS are intercepted · not sent · no credits deducted │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌── Inbox (1) ──────────┐ ┌── Message detail ───────────┐ │
│ │ │ │ │ │
│ │ +244 923 456 789 │ │ To +244 923 456 789 │ │
│ │ Hello from Sandbox! │ │ From MyBrand │ │
│ │ 2s ago ● Delivered │ │ Status ● Delivered │ │
│ │ │ │ Time 14:23:01 │ │
│ │ │ │ │ │
│ │ │ │ Hello from Sandbox! │ │
│ │ │ │ Your OTP is 123456. │ │
│ └───────────────────────┘ └─────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘Messages arrive in real-time via Server-Sent Events — no need to refresh.
Simulated delivery
The Playground simulates realistic delivery behaviour:
- 95% of messages are marked
delivered - 5% are randomly marked
failedwith errorSANDBOX_SIMULATED_FAILURE
This lets you test your error-handling logic without special setup.
Sender ID in sandbox
Sandbox mode skips the sender ID approval check. You can use any sender string:
{ "senderId": "MyBrand" } // ✅ accepted even if not approved
{ "senderId": "TEST-OTP" } // ✅ accepted
{ "senderId": "AnyNameHere" } // ✅ acceptedThis is especially useful when your sender ID is pending approval and you don't want to wait before testing your integration.
REST API
The Playground exposes three endpoints — all require authentication (JWT or API key).
List intercepted messages
GET /v1/playground/messagesQuery parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
pageNumber | int | 1 | Page number |
pageSize | int | 50 | Results per page (max 100) |
Response:
{
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"phoneNumber": "+244923456789",
"message": "Hello from Sandbox!",
"senderId": "MyBrand",
"status": "delivered",
"createdAt": "2025-03-30T14:23:01Z"
}
],
"totalCount": 1
}Clear the inbox
DELETE /v1/playground/messagesRemoves all intercepted messages for your account. Returns 204 No Content.
Real-time stream (SSE)
GET /v1/playground/streamServer-Sent Events stream. Each new sandbox message is pushed as a JSON event the moment it is processed:
data: {"id":"3fa85f64...","phoneNumber":"+244923456789","message":"Hello!","senderId":"MyBrand","status":"delivered","createdAt":"2025-03-30T14:23:01Z"}Connect with any SSE client:
const es = new EventSource('https://api.castbrick.co/v1/playground/stream', {
headers: { Authorization: `Bearer ${token}` }
});
es.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log('Sandbox message received:', msg);
};Using the Playground in automated tests
// jest / vitest example
const TEST_KEY = process.env.CASTBRICK_TEST_KEY; // cb_test_xxx
it('sends an OTP and receives it in the playground inbox', async () => {
// 1. Send via API
const send = await fetch('https://api.castbrick.co/v1/sms/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${TEST_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipients: ['+244923456789'],
content: 'Your code is 999888',
senderId: 'TestOTP',
}),
});
const { messageId } = await send.json();
// 2. Poll playground to confirm it was intercepted
await new Promise(r => setTimeout(r, 500)); // allow worker to process
const inbox = await fetch('https://api.castbrick.co/v1/playground/messages', {
headers: { Authorization: `Bearer ${TEST_KEY}` },
});
const { items } = await inbox.json();
const match = items.find(m => m.id === messageId);
expect(match).toBeDefined();
expect(match.status).toBe('delivered');
expect(match.message).toContain('999888');
});Switching between sandbox and live
The only change needed is the API key. No code changes required.
# Sandbox
Authorization: Bearer cb_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Live
Authorization: Bearer cb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDifferences from live mode
| Feature | Live (cb_live_) | Sandbox (cb_test_) |
|---|---|---|
| SMS actually sent | Yes | No — intercepted |
| Credits deducted | Yes | No |
| Sender ID required to be approved | Yes | No — any string accepted |
| Delivery simulation | Real (carrier-dependent) | 95% delivered / 5% failed |
| Retries on failure | Yes (up to 3x) | No |
| Webhooks fired | Yes | No |
| Messages visible in SMS logs | Yes | No (only in Playground) |
| Credits deducted | Yes | No |