Test an iMessage API integration before the first live send
Run a practical sandbox acceptance plan for sends, replies, signatures, rate limits, timeouts, CRM matching, and human handoff before moving to a real number.
Test the decisions your application makes
A successful simulated send is a useful first check. A release-ready integration also needs to handle a reply, duplicate event, missing contact match, blocked destination, rate limit, timeout, and human takeover. Each case should name the expected business action and the evidence that proves it occurred once.
Use invented contacts and a dedicated test project. This article is an acceptance plan built on the Miss Blue sandbox, not a benchmark of live iMessage delivery. The simulator exercises application behavior without contacting recipients; a later live trial verifies the device and channel boundaries.
Confirm that the credential selects simulation
Miss Blue uses the same API hostname for live traffic and simulation. Only an mb_sandbox_ key selects the sandbox. Legacy mb_test_ keys can reach live traffic, so a variable named TEST_KEY is not evidence of isolation. The response includes X-Miss-Blue-Sandbox: true; verify it in your test client.
Store a sandbox key server-side as MISS_BLUE_SANDBOX_KEY, then obtain a virtual number ID from the numbers endpoint. The following shell guard rejects a credential with the wrong prefix before making the request. Prefix checking is a local safeguard; the server still validates the key. Never put a real key in a blog snippet, browser bundle, or test log.
case "$MISS_BLUE_SANDBOX_KEY" in
mb_sandbox_*) ;;
*) echo "Set a sandbox key before running this example" >&2; exit 1 ;;
esac
curl --fail-with-body --max-time 15 \
https://api.missblue.dev/v1/numbers \
-H "Authorization: Bearer $MISS_BLUE_SANDBOX_KEY"Make one reply complete the full workflow
Replace VIRTUAL_NUMBER_ID below with the sandbox number ID returned by the first request. The recipient is a fixture label. The reply scenario creates simulated delivery and an incoming message; inspect the resulting message ID and the sandbox event list. Register your sandbox project webhook first if you want your own endpoint to receive the event.
case "$MISS_BLUE_SANDBOX_KEY" in
mb_sandbox_*) ;;
*) echo "Set a sandbox key before running this example" >&2; exit 1 ;;
esac
curl --fail-with-body --max-time 15 \
-X POST https://api.missblue.dev/v1/messages \
-H "Authorization: Bearer $MISS_BLUE_SANDBOX_KEY" \
-H "Content-Type: application/json" \
-H "X-Miss-Blue-Sandbox-Scenario: reply" \
-d '{"number_id":"VIRTUAL_NUMBER_ID","recipient":"+12025550101","text":"Example appointment question"}' Keep an outcome table with explicit pass conditions
The documented scenarios complete immediately, including the synthetic timeout. Use them to test response handling, not elapsed network latency. To advance a pending or sent fixture later, use the playground or the documented simulate endpoint with the existing message ID.
Swipe across the table to see every column.
| Scenario | Simulator result | Your application should prove |
|---|---|---|
| delivered | 201 with a delivered fixture | Store the provider ID; do not invent a customer reply |
| read | 201 with delivery and read receipts | Read evidence stays separate from an incoming message |
| reply | 201 plus an incoming message | One correctly mapped follow-up action |
| failed | 400, failed fixture, message.failed event | Failure is visible and does not become successful outreach |
| offline | 201, pending, queued: true | Queued work remains distinguishable from delivered work |
| rate_limited | 429, Retry-After: 60, no message created | Delay work according to policy; do not hammer the endpoint |
| timeout | 504 with a pending fixture | Unknown outcome is retained; no blind second send |
| sent | 201, sent, awaiting a receipt | A later fixture transition updates the same message |
Miss Blue sandbox scenario behavior, checked against the public contract on September 13, 2026.
Test your webhook boundary, not only the event JSON
Sandbox project webhooks make real HTTP requests with simulated payloads. Use a public HTTPS endpoint, its returned signing secret, and the documented Miss-Blue-Signature format. Verify the timestamp and raw-body HMAC before trusting the parsed event. Sandbox endpoints and secrets are separate from live ones.
Capture a synthetic signed request in an access-controlled test harness. Change one byte and confirm rejection. Repeat a valid event within your signature tolerance and confirm that durable event deduplication produces no second action. Make event storage fail temporarily and confirm the endpoint does not return success for work it lost. Keep signing secrets and payloads out of ordinary logs.
The simulator can emit another reply, but another reply creates a new incoming message. That is not a duplicate-delivery test. Replay the same signed event in your controlled receiver harness, or exercise a documented delivery retry, to test the same envelope ID twice.
Treat an unknown send as its own state
For the timeout scenario, inspect the pending fixture in the playground and verify the integration preserves uncertainty. In a real network timeout, the client may not receive a provider ID at all. Your local logical action ID is still necessary, but it does not by itself deduplicate a second request at the provider.
Miss Blue’s message send contract does not document an Idempotency-Key header. Keep dispatch ownership in your adapter and reconcile using documented provider evidence or human review before issuing a replacement send. Do not mark an unknown attempt as definitely failed merely because the HTTP client stopped waiting.
Test a takeover while that attempt is unknown. The teammate should see the uncertainty and pending reconciliation. An old worker should not send again after ownership changed just to clear its queue.
Know what the sandbox cannot prove
The sandbox does not test real recipient reachability, channel delivery speed, carrier behavior, or whether a person answers a call. Caller-supplied call event fixtures test a parser, not a phone network. Attachment uploads store metadata and a checksum while discarding file content, so media-download behavior needs a separate test.
Fixtures are limited and expire after seven days; the latest 200 events are retained, and older pending deliveries are discarded with their events. This is a bounded test environment, not an event archive or a throughput benchmark. A reset affects the shared project sandbox and its webhook endpoints, so coordinate it with teammates.
Finish with a controlled live acceptance run
After the synthetic cases pass, use a Live key, a real granted number, and a separately registered live webhook. Remove the scenario header and use IDs returned by the live environment. Sandbox records are not promoted into production. Start with internal contacts who expect the test.
Verify actual delivery, an inbound reply, the staff inbox, the CRM association, and the documented status evidence. Record the release, environment, test case, expected action, and observed result. This gives the team a reproducible acceptance record without turning real customers into test fixtures.
Quick answers
Is the sandbox a different API URL?+
No. Use the normal API URL with an mb_sandbox_ credential. Check the sandbox response marker and use virtual resource IDs.
Does a sandbox timeout wait for a real network timeout?+
No. It is an immediate simulated 504 with a pending fixture. Add separate controlled network tests if you need to verify your client’s timeout timing.
Does running the reply scenario twice test duplicate webhooks?+
No. Repeated replies create additional incoming messages. Test duplicate handling with the same event envelope ID through a controlled replay or delivery retry.
Can a passing sandbox test guarantee live delivery?+
No. It validates supported simulated behavior. Real numbers, recipients, attachments, channel behavior, and operational readiness need a separate live acceptance run.