CastBricks Docs

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?

ScenarioWithout PlaygroundWith Playground
Sender ID pending approvalRequests blockedAny sender accepted
Testing your integrationReal SMS sent, costs moneyIntercepted, 0 cost
CI/CD automated testsDangerous in productionSafe
Onboarding / demosBurns creditsFree
Debugging webhooksReal chargesNo deduction

Your Sandbox API Key

Every CastBrick account has two API keys created automatically:

KeyPrefixBehaviour
Livecb_live_...Real SMS sent, credits deducted
Sandboxcb_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 failed with error SANDBOX_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" }   // ✅ accepted

This 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/messages

Query parameters:

ParameterTypeDefaultDescription
pageNumberint1Page number
pageSizeint50Results 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/messages

Removes all intercepted messages for your account. Returns 204 No Content.

Real-time stream (SSE)

GET /v1/playground/stream

Server-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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Differences from live mode

FeatureLive (cb_live_)Sandbox (cb_test_)
SMS actually sentYesNo — intercepted
Credits deductedYesNo
Sender ID required to be approvedYesNo — any string accepted
Delivery simulationReal (carrier-dependent)95% delivered / 5% failed
Retries on failureYes (up to 3x)No
Webhooks firedYesNo
Messages visible in SMS logsYesNo (only in Playground)
Credits deductedYesNo