Testing with test keys & magic numbers

Use a test key and reserved magic numbers to exercise the whole messaging API — sends, status callbacks, and replies — without a live number.

You don't need a provisioned, registered phone number to start integrating. Issue a test key, send to a set of reserved magic numbers, and Beep fully simulates the message lifecycle: nothing reaches a carrier, nothing is billed, but your status-callback and reply-destination integrations fire for real with genuine signatures. This mirrors the test-card model you may know from payment platforms.

Test keys vs. live keys

API keys carry a visible, unambiguous prefix:

PrefixModeBehavior
beep_live_…LiveSends are delivered to real recipients through a carrier and are billed.
beep_test_…TestEvery send is simulated — never delivered, never billed.

Every endpoint accepts either key; only the send behavior diverges. Create a test key from the dashboard (choose the Test key option) the same way you create a live key.

A test key:

  • Routes every send to the simulator instead of a carrier. Single sends and batch sends behave identically.
  • Needs no provisioned sender. If your workspace has no number yet (or you don't specify one), Beep uses a reserved synthetic sender, so a brand-new workspace can integrate before number approval completes.
  • Is never billed and never counts toward usage or campaign analytics. Simulated traffic is tagged and excluded from all reporting.
  • Is rate-limited like any other key. The per-API-key request rate limit applies normally to test keys; test sends skip only the provider-level send-throughput limiting and cost reservation (there is no real send).

Magic numbers only mean something under a test key. A live key sending to a magic number gets ordinary live treatment (and fails naturally, since the reserved numbers are non-routable). Test behavior can never leak into production, and a test key can never send a real SMS.

Magic numbers

Send to one of these reserved numbers (drawn from the fictional 555-01XX line range under non-geographic area code 500) to select a deterministic outcome:

NumberOutcomeSimulated lifecycle
+15005550101Successqueued → sent → delivered
+15005550102Delivery failurequeued → sent → failed (carrier undelivered; error_code set)
+15005550103Hard reject at sendimmediate failed (invalid recipient; error_code set)
+15005550104Opt-outdelivered, then a simulated STOP → opt_out callback + suppression
+15005550105Landlinefailed, error_code = LANDLINE
+15005550106Success + inbound replydelivered, then a simulated inbound reply routed to your reply destination
+15005550107Success + inbound MMSdelivered, then an MMS webhook with a stored sample PNG
+15005550108Success + unavailable MMSdelivered, then a reply with attachments_unavailable: true
  • Any other well-formed E.164 number on a test key defaults to the success path (delivered), so you can use arbitrary numbers for happy-path flows.
  • Malformed numbers still return the normal validation error, so that path is testable too.

+15005550100 is the reserved synthetic sender and is never a valid recipient outcome.

How callbacks fire for simulated sends

Simulated sends drive the real status-callback machinery. If you've configured a status-callback URL (per send via status_callback, or as the workspace default), Beep POSTs a signed message.<event> for each transition in the outcome's lifecycle, with the same HMAC signing, payload shape, retry schedule, and idempotency as a live send. So a single magic number lets you verify your webhook end-to-end:

  • +15005550101 delivers message.queued, message.sent, message.delivered.
  • +15005550102 delivers message.queued, message.sent, message.failed (with an error_code).
  • +15005550103 and +15005550105 deliver a single message.failed.
  • +15005550104 delivers message.queued, message.sent, message.delivered, then message.opt_out.

See Notification Settings for how to configure the callback URL and verify signatures.

Exercising reply destinations

Two magic numbers simulate inbound traffic so your reply destinations (webhook, zendesk, or beep_inbox) are exercised for real:

  • +15005550106 (success + reply) — after delivery, Beep creates a simulated inbound reply and routes it through the same path a real reply takes. If the send (or workspace) has a webhook or Zendesk reply destination, your endpoint receives a signed message.received payload.
  • +15005550104 (opt-out) — after delivery, Beep simulates an inbound STOP. This applies suppression to the simulated recipient and emits a message.opt_out status callback. Consistent with live behavior, a STOP is never forwarded to a webhook or Zendesk destination — it suppresses and reports, but does not deliver a reply.

Because opt-out suppression is applied only to the simulated test recipient, it never affects your live recipients.

Try it

This send simulates a successful delivery. Swap in your own test key and workspace id; change to to any magic number above to drive a different outcome.

curl -X POST https://app.beepmessaging.com/api/v1/messages \
  -H "Authorization: Bearer beep_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Workspace-Id: 11111111-1111-1111-1111-111111111111" \
  -H "Content-Type: application/json" \
  -d '{
    "status_callback": "https://app.example.com/hooks/beep-status",
    "messages": [
      { "to": "+15005550101", "body": "Hello from test mode." }
    ]
  }'
{
  "data": {
    "id": "a1b2c3d4-0000-0000-0000-000000000001",
    "to": "+15005550101",
    "status": "queued",
    "client_reference": null,
    "person_id": "b2c3d4e5-0000-0000-0000-000000000002",
    "phone_number_id": "+15005550100",
    "mode": "test"
  }
}

Every send acknowledgment, batch item, and GET /api/v1/messages/{id} response includes a mode field ("test" or "live") — à la Stripe's livemode — so you can tell a simulated send from a real one. A beep_test_ key always reports "test"; a beep_live_ key reports "live".

The send acknowledgment reports a non-terminal queued status just like a live send. Poll the simulated terminal status with GET /api/v1/messages/{id}:

curl https://app.beepmessaging.com/api/v1/messages/a1b2c3d4-0000-0000-0000-000000000001 \
  -H "Authorization: Bearer beep_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Workspace-Id: 11111111-1111-1111-1111-111111111111"

For +15005550101 the message resolves to delivered; for the failure numbers it resolves to failed with the matching error_code.

When you're ready for production

Switch the Authorization header from your beep_test_ key to a beep_live_ key and send to real recipients once your sending number is provisioned and registered. No other code changes are required — the request and response shapes are identical.


Did this page help you?