How it works
Follow a message from setup to delivery.
Choose a channel and sender, test the request, get a price, and follow its outcome. Use the dashboard to send manually or the API to send from your own system.
The path of a live message
Start with the message you want to send.
Choose SMS, WhatsApp, or email, then select an eligible sender. Use a scoped API key on your server or prepare the message in the workspace.
Your first send, step by step
Set up your workspace
Select the channels you need. Request approved senders or connect your WhatsApp Business number as required.
Simulate your first request
Create a sandbox key or use the workspace sandbox to try success, provider rejection, and an insufficient balance. Simulations do not use wallet credit.
Quote and fund
Get a live quote for the destination and channel. Fund the workspace using the configured checkout before creating a live key and sending.
Send from the API or workspace
Use a scoped live key on your server, or use the workspace for manual and bulk sends. Confirm permission to contact recipients.
Follow delivery
Save the returned message ID. Check Delivery logs or configure signed webhooks to follow the status.
Reconcile usage
Review available and reserved wallet credit. An eligible live send reserves its quote before queueing, then the worker settles the charge as it dispatches.
Your wallet
Where the money goes, step by step.
Every live send follows the same five steps. Your balance only moves when a message does.
- 01
Quote
The price is worked out for the channel, destination and SMS segments. providerMicros and serviceFeeMicros are shown separately.
- 02
Reserve
The quoted amount is held from your available balance and the message joins the queue. A short balance returns 402 and nothing is held.
- 03
Dispatch
The worker checks consent, suppression, the channel connection and your balance again, then charges the attempt as it is claimed.
- 04
Settle
The provider’s answer settles the attempt. Delivery receipts and read events update the message as they arrive.
- 05
Refund
A confirmed rejection before the provider accepts the message is refunded once. Accepted attempts can remain billable after a delivery failure.
Fallback
A second chance on another channel.
For saved contacts with fallback on, an unavailable channel or a confirmed failure moves the message to the next channel the contact agreed to.
route: WhatsApp → SMS → EmailWhat the response means
| Response | Meaning | What to do |
|---|---|---|
| 202 Accepted | A live request was queued and the quote reserved. | Save the message ID and monitor it. |
| 200 OK (replay) | The same Idempotency-Key and payload were sent again. | Nothing new was sent. Use the original message ID. |
| 402 Insufficient funds | The available balance cannot cover the live quote. | Fund the wallet and retry the intended request. |
| 403 Forbidden | The key or workspace does not have access to the requested operation or channel. | Review channel settings and key scope. |
| 409 Conflict | A send reused an idempotency key with different input, or the price exceeded your cap. | Check the original request and refresh the quote if needed. |
| Sent / accepted | The provider accepted the handoff. | Wait for a delivery receipt where the route supports one. |
| Delivered / read | The provider reported delivery or a read event. | Check Delivery logs or consume the signed event. |
Delivery receipts depend on the provider and route. The sandbox simulates status without contacting a provider.
Delivery logs
See every attempt, not just the result.
Each message lists its attempts, the channel used, the provider’s answer and the exact charge.
| Time | Channel | Recipient | Status | Charge |
|---|---|---|---|---|
| 10:42:07 | +256 770 ••• 456 | delivered | $0.0280 | |
| 10:42:05 | SMS | +256 701 ••• 210 | delivered | $0.0392 |
| 10:41:58 | amina@••••.com | sent | $0.0011 | |
| 10:41:51 | SMS | +254 712 ••• 091 | failed | $0.0000 |
| 10:41:44 | +256 752 ••• 113 | read | $0.0280 | |
| 10:41:30 | SMS | +256 772 ••• 630 | sending | $0.0392 |
- Quoted · $0.0280 reserved
- Handed to WhatsApp
- Delivered 10:42:07
- Read 10:42:31
{ "type": "message.status",
"status": "read",
"channel": "WhatsApp" }Built to be safe
The guarantees behind every send.
Idempotent sends
Every send needs an Idempotency-Key. Retrying with the same key and payload returns the original message, so a network blip never double-sends.
Consent on every send
API sends require consent: true. Fallback and campaigns only use channels each contact agreed to, and opt-outs are respected automatically.
Signed webhooks
Each event is signed with HMAC-SHA256 over the timestamp and body. Verify the X-Savara-Signature header before trusting an event.
Scoped keys, stored as hashes
Keys are limited to the channels and actions you choose. Only a hash is stored, so a key is shown once when you create it.
Rate limits
Each workspace can make 60 API requests per minute, which protects you and the shared routes from runaway loops.
No guessing on uncertain sends
If a provider’s answer is ambiguous, the attempt is held for review rather than resent automatically.
Webhooks
Know the moment status changes.
Register an HTTPS callback and SavaraFlow posts each status event to it. Check the timestamp is fresh, deduplicate by the X-Savara-Event ID and reply with a 2xx. Events can arrive out of order, so fetch the current status when it matters.
Test a signature in the browserverify-webhook.jsNode.js1import crypto from "node:crypto";
2
3// raw: the request body exactly as received
4export function verify(raw, headers, secret) {
5 const timestamp = headers["x-savara-timestamp"];
6 const signature = (headers["x-savara-signature"] ?? "").replace("sha256=", "");
7 // reject anything older than five minutes
8 if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
9 const expected = crypto.createHmac("sha256", secret)
10 .update(`${timestamp}.${raw}`).digest("hex");
11 return signature.length === expected.length &&
12 crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
13}
See what your sends might cost.
The calculator gives an estimate. Your live quote uses your workspace’s configured rate.