Developers
The SavaraFlow messaging API.
Send to a phone number or email address, follow delivery, manage prepaid credit and trigger automations from your own application.
Try it in the playground
Send a request and see the exact response. It runs offline by default and calls the real sandbox when you paste a test key.
/api/v1/messagesPress “Send request” to see the response.curl -X POST "$SAVARA_HOST/api/v1/messages" \
-H "Authorization: Bearer $SAVARA_TEST_KEY" \
-H "Idempotency-Key: " \
-H "Content-Type: application/json" \
-d '{ "channel": "SMS", "to": "+256770123456", "body": "Order 1842 is ready for pickup.", "scenario": "delivered" }'Without a key the playground runs offline with the same validation as the API. Paste a sandbox key (sv_test_…) from the console to call the real sandbox. Sandbox calls never touch your wallet.
Get a live API key
Sign in to the console, create a workspace, and complete a wallet top-up. Create a key in Developers → Overview. Copy it once and send it as Authorization: Bearer <key> from your server.
Use your live Node deployment origin. The hosted preview simulates funding, keys, and sends. Each workspace can make 60 API requests per minute per environment.
Test with a sandbox key
Create an sv_test_ key in Developers → Sandbox. POST a direct message to /api/v1/messages with a test key and an Idempotency-Key header. Test keys only access simulated message sends and results.
Set scenario to delivered for HTTP 200, rejected for HTTP 422, or insufficient_funds for HTTP 402. Simulation responses include an ID, environment: "test", and zero cost. Read simulated history with GET /api/v1/messages using the same environment.
{
"channel": "SMS",
"to": "+256770123456",
"body": "Your booking is confirmed.",
"scenario": "insufficient_funds"
}Open the sandboxChannels and sender identities
Enable channels in Settings → Senders. When creating a key, choose which of those channels it may send on. A disallowed send or quote returns 403. Changing workspace channels does not add permissions to a key.
Use GET /api/v1/senders to find approved managed sender IDs. Set sender_id to that ID on SMS or email quotes and sends, including bulk requests. The default identity is used when no ID is supplied. WhatsApp uses the workspace’s connected Meta number.
Live quotes expose providerMicros, serviceFeeMicros, and costMicros. Pass maxCostMicros when sending to cap the reservation. Pricing uses USD millionths and bills SMS by segment.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/senders | List approved managed sender IDs available to this live key. |
| POST | /api/v1/messages | Send SMS, WhatsApp, or email directly to a phone number or email address. |
| POST | /api/v1/bulk/quotes | Check eligibility and price a batch of up to 250 addresses. |
| POST | /api/v1/bulk/messages | Queue an eligible batch and reserve its total cost atomically. |
| POST | /api/v1/quotes | Estimate a direct send without creating a contact or holding wallet funds. |
| GET | /api/v1/messages | List outgoing messages. Filter by channel and paginate with the returned cursor. |
| GET | /api/v1/messages/{id} | Read the current delivery status of a message. |
| GET | /api/v1/wallet | Read total, reserved, and available balance in USD millionths. |
| GET | /api/v1/templates | List synced approved WhatsApp templates and their parameter counts. |
| GET | /api/v1/contacts | List contacts. Pass ?after=<nextCursor> to paginate. |
| POST | /api/v1/flows/trigger | Start an active external workflow for a known contact. |
| GET | /api/v1/flow-runs/{id} | Read the current workflow run state. |
Send directly
Set to to an international phone number or email address and confirm permission with consent: true. This records consent for the request without subscribing the recipient to campaigns. Direct requests use the selected channel. Saved-contact requests with contactId still support eligible fallback routes.
curl https://YOUR_SAVARA_HOST/api/v1/messages \
-H "Authorization: Bearer $SAVARA_API_KEY" \
-H "Idempotency-Key: order-1842-sms" \
-H "Content-Type: application/json" \
-d '{"channel":"SMS","to":"+256770123456","body":"Order 1842 is ready.","consent":true}'Email body
Use Email setup to prepare a sender and simulate a request. SavaraMail acceptance indicates handoff to the mail service.
{
"channel": "Email",
"to": "customer@example.com",
"subject": "Your order is ready",
"body": "You can collect order 1842 today.",
"consent": true
}WhatsApp template body
Connect the workspace sender and sync its approved templates. Outside an open reply window, supply an approved template and its parameters in order.
{
"channel": "WhatsApp",
"to": "+256770123456",
"template": {
"name": "order_update",
"language": "en",
"parameters": ["Amina", "1842"]
},
"consent": true
}Quotes, charges, and retries
Submit the same direct payload to POST /api/v1/quotes for an estimate. Quotes do not need an idempotency key. Sends and workflow starts require Idempotency-Key.
Direct sends reserve the quoted charge before entering the queue. A new request returns 202. An exact retry returns 200 with the same ID. Reusing a key for different input returns 409. Insufficient available credit returns 402.
{
"id": "MESSAGE_UUID",
"status": "queued",
"costMicros": 22000,
"currency": "USD",
"units": 1,
"replayed": false
}costMicros is quoted in USD millionths. Follow the message status endpoint or signed webhooks for the final outcome. An SMS may span more than one billed segment.
Send a batch
Send one payload to up to 250 unique recipients. Quote with POST /api/v1/bulk/quotes and review rows, rejected, and costMicros. Supply only eligible addresses to POST /api/v1/bulk/messages with an idempotency key and consent for every recipient. Set maxCostMicros to cap the total at your quoted price.
The send reserves the full cost in one transaction. If any recipient becomes ineligible, pricing exceeds the cap, or credit falls short, the batch is rejected. A successful response includes each message ID.
{
"channel": "Email",
"recipients": ["amina@example.com", "john@example.com"],
"subject": "Your order is ready",
"body": "Your order is ready to collect.",
"consent": true,
"maxCostMicros": 10000
}Start an external flow
Set the trigger to External API request, activate the flow, and POST this body to /api/v1/flows/trigger.
{"flowId":"FLOW_UUID","contactId":"CONTACT_UUID"}Verify delivery webhooks
Configure an enabled HTTPS origin and save the signing secret. Calculate HMAC-SHA256 over the timestamp, a period, and the raw request body. Compare the hex digest to X-Savara-Signature in constant time, after removing sha256=.
Use X-Savara-Timestamp for a five-minute freshness check. Deduplicate by X-Savara-Event, acknowledge with a 2xx, and fetch current status if events arrive out of order.
Test a webhook signature
Check your verification code against a known-good signature. Paste the secret from your workspace and a request you received.
1731141727.{"id":"3f9a2c7e-41d2-4b8e-9c15-07ad1e6b52f0","type":"message.sta…sha256=…This timestamp is more than five minutes old. Reject it in production to block replays.
Everything runs in your browser. Your secret is never sent anywhere.