Zoril SMS Documentation

API reference, Android gateway operation, message status, retries, webhooks, plans and consent requirements.

1. Before you start

Use https://sms.zoril.app as the production base URL. You need a verified account, an organisation with API access enabled on its assigned plan, and a live API key. Pair an Android gateway with an active SMS SIM for real delivery.

For a first integration, use a test key. A test response is simulated; it does not send an SMS or prove that your SIM can deliver. Test requests still pass through API access and request-limit checks.

  • Create keys in API Keys. Store them only on your server, never in browser scripts, public repositories or mobile app bundles.
  • Use international recipient numbers, for example +256784777801. Send a real test only to a number you own or have permission to contact.
  • A platform plan or credit purchase does not replace the SIM's airtime, SMS bundle or network permission.

2. Authentication

Pass your organisation's API key as a bearer token on every API request. Keys are stored as hashes and the full key is shown when created. Revoked keys no longer authenticate. To rotate, create a replacement, update your integration, verify it works, then revoke the old key.

Workspace sign-in credentials and Android device credentials are not interchangeable with a public SMS API key. Keep each secret private and restrict who can manage keys.

Authorization: Bearer YOUR_ZORIL_API_KEY
Content-Type: application/json

3. Send an SMS

POST /api/public/v1/sms/send accepts JSON with to (required phone number), message (required, 1–1,600 characters) and device_id (optional UUID belonging to your organisation). The message field is named message, not body. The endpoint does not accept a custom sender name, scheduling time or callback_url.

Omit device_id to use the organisation's configured gateway selection policy. Supplying it pins the message to that device. A new accepted request returns HTTP 202 with id, status, to and created_at. Keep the returned ID for reconciliation.

This example is a live send. Set ZORIL_API_KEY in your server environment; use a test key first. Replace the recipient with an authorised test number.

curl -X POST https://sms.zoril.app/api/public/v1/sms/send \
  -H "Authorization: Bearer $ZORIL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-123-confirmation-v1" \
  -d '{"to":"+256784777801","message":"Zoril: Your order has been confirmed."}'

{
  "id": "returned-message-uuid",
  "status": "queued",
  "to": "+256784777801",
  "created_at": "ISO-8601 timestamp"
}

4. Track the result

GET /api/public/v1/messages/{id} retrieves a message using an API key for the same organisation. Check status and the available timestamps/error details. Review API Logs in the workspace when a request fails.

  • queued: accepted for processing; no delivery claim.
  • sent: the gateway reported the send. This is not proof of delivery to the recipient.
  • delivered: a delivery report was recorded. Mobile networks may not return a report for every message.
  • failed: inspect the error and attempt history before retrying.
  • received: an inbound message from the paired phone.
  • Test keys and simulator devices can report delivered without contacting the mobile network.
curl https://sms.zoril.app/api/public/v1/messages/MESSAGE_UUID \
  -H "Authorization: Bearer $ZORIL_API_KEY"

5. Safe retries

Use one stable Idempotency-Key per logical send, and reuse that same key and payload after a timeout. A matched existing message returns HTTP 200 with idempotent: true. Do not generate a new key each time you retry the same operation.

Do not assume exactly-once delivery. A request timeout can happen after acceptance; reconcile the original message before resending. Use bounded exponential backoff with jitter, and retain your own business-operation-to-message-ID mapping. Do not reuse an idempotency key for different recipients or different content.

Authentication and quota checks occur before the existing-message lookup, so even a retry can be rejected when access or limits have changed. Idempotency is tied to retained message records, not a promise of permanent deduplication.

6. Replies and webhooks

Paired Android gateways forward incoming SMS to the workspace when permissions and connectivity allow. Configure enabled Webhooks for inbound events and supported status updates. Select event names in the Webhooks screen rather than assuming every message change has an event.

Deliveries include x-zoril-event and x-zoril-signature: sha256=<hex>. Verify HMAC-SHA256 over the exact raw request body with your webhook secret, before parsing or acting on it. Acknowledge with a 2xx response quickly, then process through your own queue.

The current delivery format does not provide a signed timestamp header. Deduplicate using the event and message ID, and prevent repeated side effects on your own system. Webhook deliveries may be retried; inspect delivery history for attempts and errors.

// Node.js receiver example (your server, not browser code)
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, signature, secret) {
  const expected = "sha256=" + createHmac("sha256", secret)
    .update(rawBody).digest("hex");
  const actual = Buffer.from(signature ?? "");
  const wanted = Buffer.from(expected);
  return actual.length === wanted.length && timingSafeEqual(actual, wanted);
}

// Example inbound payload:
{ "event": "message.inbound", "data": { "id": "message-uuid",
  "from_number": "+256784777801", "body": "STOP" } }

7. Plans, limits and costs

Plans, trial duration, feature access, API allowances, campaign limits and messaging rates are controlled by the administrator. Use Pricing for available offers and Billing & Usage in your workspace for your assigned plan, trial/renewal dates and current usage; do not build fixed commercial allowances into your integration.

The send endpoint applies per-address and per-key abuse limits, the plan's API allowance, subscription checks, SMS allowance/credits, and organisation/device daily SMS limits. Daily SMS limits can include timezone, next_reset_at and retry_after_seconds in a 429 response. API daily allowances use a rolling 24-hour window; not every 429 is a midnight reset.

Long messages and Unicode can require multiple SMS parts and additional carrier charges. One GSM-7 SMS typically holds 160 septets; multipart capacity is 153 per part. UCS-2 typically allows 70 code units for a single part and 67 per multipart segment. Some characters count as more than one unit. Treat cost previews as estimates unless your actual billing records confirm the charge.

8. Error handling

Read the HTTP status and JSON error field together. Additional fields vary by error. Do not blindly retry validation, permission, suspension or opt-out failures.

  • 400 invalid_input: correct the JSON fields and recipient.
  • 401 missing_api_key / invalid_api_key: check the bearer key, environment and revocation state.
  • 403 api_not_in_plan: the assigned plan does not allow API access.
  • 402 subscription_* / no_plan: ask your organisation administrator to review account status and renewal.
  • 404 device_not_found / not_found: verify the ID and organisation ownership.
  • 409 no_online_device: reconnect a gateway; recipient_suppressed: honour the opt-out, do not bypass it.
  • 429 rate_limited, daily-limit or quota errors: inspect usage and any reset/retry fields; back off or review your plan and credits.
  • 5xx: reconcile uncertain acceptance, then retry with the same idempotency key and a bounded backoff.

9. Gateway and campaign operation

Keep gateway phones connected, powered and able to send SMS through the selected SIM. Grant SMS/phone permissions and allow background activity. A device's identity persists across ordinary reconnections; do not delete and recreate it simply because the phone is temporarily offline.

Use the workspace Campaigns and group-send tools to schedule large or personalised sends. Campaign processing uses bounded batches, gateway throughput controls, retries and attempt records. Gateway Status shows queue age and progress. Pause or cancel stops work not yet handed off; an SMS already submitted to a phone cannot be recalled.

A SIM gateway normally sends from its phone number. A branded alphanumeric sender ID needs an approved network/provider route; entering a name in a template or sender setting alone cannot change the SIM's originating number.

10. Consent and privacy

Identify the sender, use an appropriate lawful basis and record marketing consent with evidence. Do not send promotions to suppressed or opted-out recipients. Keep numbers normalised so opt-out matching is consistent. Never disguise a promotion as a transactional notification to bypass consent.

The public send endpoint is a single-message transport and does not accept a campaign category or automatically add a marketing sender/STOP footer. Your integration must provide compliant content and check the required consent; the campaign workflow applies its own marketing safeguards. Destination-country and carrier rules still need to be checked by the sender.

Do not send passwords, identity documents, full account details or sensitive balances by SMS. SMS is not end-to-end encrypted. Review the Privacy Policy and Terms, and contact the team at https://zoril.app/#contact for support.