Savaraflow
Open console

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.

Offline simulation
POST/api/v1/messages
Response
Press “Send request” to see the response.
Same request with cURL
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 sandbox

Channels 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

MethodPathPurpose
GET/api/v1/sendersList approved managed sender IDs available to this live key.
POST/api/v1/messagesSend SMS, WhatsApp, or email directly to a phone number or email address.
POST/api/v1/bulk/quotesCheck eligibility and price a batch of up to 250 addresses.
POST/api/v1/bulk/messagesQueue an eligible batch and reserve its total cost atomically.
POST/api/v1/quotesEstimate a direct send without creating a contact or holding wallet funds.
GET/api/v1/messagesList 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/walletRead total, reserved, and available balance in USD millionths.
GET/api/v1/templatesList synced approved WhatsApp templates and their parameter counts.
GET/api/v1/contactsList contacts. Pass ?after=<nextCursor> to paginate.
POST/api/v1/flows/triggerStart 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.

1 · Signed string1731141727.{"id":"3f9a2c7e-41d2-4b8e-9c15-07ad1e6b52f0","type":"message.sta…
2 · HMAC-SHA256 (hex)sha256=…
Paste a received signature to compare.

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.

Choose what SavaraFlow can save on this device. You can change your choice here at any time.