Skip to content

Test mode and magic numbers

Keys prefixed blu_test_ are sandbox keys. Sends made with them never reach WhatsApp or any provider, never bill, and are flagged as test data. The sandbox simulates the full delivery lifecycle so your status handling and webhook code run against realistic events.

The simulated lifecycle

On a test key a message moves queued → sent → delivered, about one step every 5 seconds, and on to read for WhatsApp; SMS and email stop at delivered. The webhooks message.sent, message.delivered and message.failed fire as it moves; the simulated read fires none.

Each test webhook is signed with your endpoint secret, as live traffic is, and carries "test": true in its payload.

Message statuses

  • queued: accepted and waiting to send. Every send, live or test, starts here.
  • sending: handed to the provider, whose answer is not back yet. A send still sending after 30 minutes is failed.
  • sent: the provider accepted it.
  • delivered: it reached the phone or the mailbox.
  • read: the recipient read it, on a channel that reports reads (WhatsApp).
  • failed: it could not be sent or delivered; the message says why.
  • parked: held because the prepaid wallet ran out of credit; it is queued again after a top-up. Never on a test key.

Magic numbers

Deterministic recipients let you exercise every path your integration must handle:

NumberBehaviour
+27800000001Always delivers (any other number behaves the same way).
+27800000002Always fails at the send step with recipient_unreachable and fires message.failed.
+27800000003Delivers, then fires a simulated message.received reply so you can test inbound handling.

Testing the failure path

failure.ts
// A new sandbox has no templates yet: send free-form text, and create the contact in the same call.
const msg = await bt.messages.send({
  channel: "whatsapp",
  to: { phone: "+27800000002", create: true, first_name: "Always Fails" },
  body: "This one is meant to fail.",
});

// ~5 seconds later:
const failed = await bt.messages.get(msg.message_id);
console.log(failed.status); // "failed"
// and your webhook receives message.failed with "test": true

Validate-only requests

Independently of the key type, you can pass test_mode: true in a send body to validate auth, contact lookup, template variables, and opt-outs without creating anything at all — the safest very first call:

validate.ts
const check = await bt.messages.send({
  test_mode: true,
  channel: "whatsapp",
  to: { phone: "+27821234567" },
  template_id: "<template id>",
  template_variables: { "1": "Sam" },
});
console.log(check.status); // "validated"
console.log(check.rendered_body_preview);

Differences from live

  • No provider calls, no Meta quality/tier effects, no billing.
  • Timing is fixed (~5s per step); live WhatsApp delivery timing varies.
  • Test rows are flagged test_mode in your workspace data.