Send an SMS

Goal: send a single SMS to one recipient with POST /messages and get back a messageId you can track.

Prerequisites:

  • An API key with the omni-api scope (Client portal → API Accesses).
  • Enough balance on your account.
  • Optional: your own Active SMS Sender ID — see Register a Sender ID. Without one, a generic default sender is used.

Steps

1. Run pre-flight checks

Confirm SMS is enabled for your account, pick a sender, and check your balance:

curl -sS -H "X-API-Key: <API_KEY>" "https://client.routeon.io/api/v1/channels"
curl -sS -H "X-API-Key: <API_KEY>" "https://client.routeon.io/api/v1/senders"
curl -sS -H "X-API-Key: <API_KEY>" "https://client.routeon.io/api/v1/balance"

From GET /senders, use a sender with channel: 1 and status: 2 (Active).

2. Send the message

The recipient phone number is digits with country code, without + (e.g. 905063565285). SMS is channel: 1.

curl -sS -X POST "https://client.routeon.io/api/v1/messages" 
  -H "X-API-Key: <API_KEY>" 
  -H "Content-Type: application/json" 
  -d '{
    "contact": "905063565285",
    "channel": 1,
    "senderId": "Routeon",
    "payload": { "text": "Hello from Routeon" },
    "webhook": "https://your.app/hooks/routeon",
    "clientInfo": "batchId=2026-07-14-001"
  }'

Request fields:

Field Required Description
contact Yes Recipient phone number, country code, no +
channel Yes 1 for SMS
senderId Yes An Active SMS sender from GET /senders
payload Yes* For SMS: { "text": "<message body>" }. Either payload or template must be present
template Yes* { "id": <content_template_id>, "params": { ... } } — send a stored content template instead of payload
webhook No URL for delivery-status events for this message; overrides the sender-level and API-key-level webhook
clientInfo No Free-form tag stored on the delivery record (EDR) — use it for batch IDs or correlation IDs
isOtp No true masks the message body in logs and reports — set it for one-time codes
successOn No sent (default) / delivered / seen — meaningful only inside multi-channel cascades (POST /omnimessages)
timeout No Default 1200 s, max 259200 s — meaningful only inside multi-channel cascades

3. Read the response

The API accepts the message and returns 202 Accepted immediately — delivery happens asynchronously:

{
  "error": false,
  "data": {
    "channel": 1,
    "transactionId": "95e3e0da-9684-4ba4-88a3-e9207ddeca05",
    "messageId": "6719b5f4-36af-4fba-b111-49ff07f4f96b"
  }
}

Store data.messageId — it is the key for webhooks and EDR lookups. Keep transactionId for support requests.

4. Mind message length and segmentation

Long messages are split into segments by the operator, and each segment is billed:

  • GSM 7-bit alphabet: 160 characters per single SMS.
  • Unicode (any character outside GSM-7, e.g. emoji or non-Latin scripts): 70 characters per single SMS.

A single non-GSM character switches the whole message to Unicode encoding. Keep bodies short, or budget for multi-segment cost — the actual charge appears as finalCost in the delivery record.

Verify

Track the message until a final state — see Track delivery for both options:

  • Webhook: your endpoint receives messageStatus events (sent, then delivered).
  • Pull:
curl -sS -H "X-API-Key: <API_KEY>" 
  "https://client.routeon.io/api/v1/edrs?messageIds=6719b5f4-36af-4fba-b111-49ff07f4f96b"

mdnStatus: "DELIVRD" and isDelivered: true mean the SMS reached the handset.

Common failures

Symptom Cause Fix
401 Unauthorized Missing or invalid X-API-Key, or the key lacks the omni-api scope Check the key value and its scopes in the portal (API Accesses); create a new key with omni-api if needed
400 Bad Request Unknown channel, malformed payload, or the sender is not registered for SMS Use channel: 1, payload.text, and a sender listed with channel: 1 in GET /senders
404 Not Found senderId not visible to your account Use a senderId exactly as returned by GET /senders
202 accepted but EDR shows mdnStatus: "REJECTD" Sender ID not approved/Active; destination closed for your account; API key missing the required scope or sender Check sender status (GET /senders, status: 2), the key scope, and confirm the destination operator is open with your manager
mdnStatus: "UNDELIV" Invalid number or unreachable subscriber Verify the phone number (country code, no +)
mdnStatus: "EXPIRED" Operator delivery window expired (phone off/out of coverage too long) No action on your side; consider resending later
Error mentioning insufficient funds Account balance too low Check GET /balance and top up
500 Internal Server Error Platform-side error Retry with exponential backoff; if it persists, contact support with data.requestId from the error body

Errors use the standard envelope:

{
  "error": true,
  "data": {
    "message": "something went wrong",
    "requestId": "af8c5065-5849-455b-ae1a-6a0b9f4b7209"
  }
}

Always log data.requestId — quote it when contacting support.