Test Mode (Simulated Sends)
The scariest moment in any messaging tool is the second after you click Send. Simulated sends remove it: the send runs through the entire real delivery pipeline — audience resolution, consent, frequency caps, the fatigue budget, quiet hours, smart channel arbitration, personalization rendering — but at the final step every delivery goes to a simulator instead of APNs, FCM, your email provider, or your SMS gateway.
Nothing reaches a real subscriber. Nothing counts against your quota. The run still appears in History so you can read exactly what would have happened.
Simulated sends are available on every plan.
What a simulation is (and is not)
A simulation is an honest rehearsal, not a mock:
- Everything upstream of delivery is real. If the fatigue budget would have held back 40 recipients, the run's detail page shows "40 held back". If smart channel would have chosen email for a subscriber, the arbitration readout says so. If your Liquid template fails to render for a recipient, the leg is recorded as failed.
- Nothing downstream of delivery happens. No provider is contacted, no credentials are read, no quota or usage is metered, no webhooks fire, and simulated activity never pollutes analytics, engagement history, frequency counters, or smart-channel rankings.
Two honest limitations: a simulator cannot tell you whether a push token is actually still valid (only a real delivery attempt can), and simulated jobs always deliver in one immediate pass — send-time optimization windows are noted but not waited for.
Simulating a test send
Compose → Send test to myself. The dialog defaults to Simulate — running it costs nothing and touches nobody. Flip "Send for real to this recipient" only when you want an actual delivery to your own device (that path consumes quota, like any real send).
Via the API:
POST /api/v1/notifications/test
{
"channel_ids": ["<channel-uuid>"],
"device_id": "<your-device-uuid>",
"content": { "title": "Hello", "body": "Testing safely" },
"simulate": true
}
The response carries "delivery_mode": "simulated".
Simulating a full campaign
Compose → Simulate send (beside the real Send button) runs your exact draft — audience, channels, filters, smart channel, holdout, category — as a simulation. It is deliberately a separate button, not a mode toggle: there is no state to forget, and each click's outcome is unambiguous.
Via the API, add delivery_mode to a normal compose request:
POST /api/v1/notifications
{
"channel_ids": ["..."],
"target": { "type": "segment", "segment_id": "..." },
"content": { "title": "...", "body": "..." },
"delivery_mode": "simulated"
}
Notes:
- A tenant at its quota cap can still simulate — rehearsal is exactly what you want when you're at cap.
- Simulated runs never create a campaign container and are hidden from campaign lists by default.
Reading the results
Open the run from History (use "Show test & simulated runs" to reveal simulated runs in the list, or follow the link the Simulate button gives you). The detail page shows:
- A "Simulated" badge next to the status — the run completed without a single real delivery.
- The usual per-channel outcome counts, where
simulatedstands in fordelivered. - Every guardrail readout as if the send were real: suppressions by consent, frequency caps, the fatigue budget, and the smart channel arbitration summary. Rehearse an arbitration strategy or check how hard a budget would bite — without sending anything.
How it composes
| Surface | Live behavior | Simulated behavior |
|---|---|---|
| Providers (push/email/SMS/WhatsApp) | Real delivery | Never contacted |
| Quota & usage metering | Consumed | Untouched |
| Guardrails (consent, caps, budget) | Enforced | Evaluated & reported, meters not consumed |
| Analytics, engagement, rankings | Counted | Excluded by construction |
| Webhooks & events | Fired | Suppressed |
| History | Listed | Badged, behind the include toggle |
Simulate vs sandbox — which one?
Simulate is a rehearsal of one send inside your live workspace. A sandbox workspace is a full shadow copy where only the simulator can ever run — seeded with synthetic subscribers and history. Rule of thumb: rehearsing one send → Simulate; experimenting over days with journeys, segments, or API integration → sandbox.