Skip to main content

SMS API

The SMS API sends messages from your sender lines, reads what was sent and received, and manages templates, blocked numbers and the webhook. The arianet sms CLI commands are built on it. The wallet, top-ups, tickets and team members belong to the Arianet account and are not part of this API.

The previous SMS API

The previous sms-api.ariaservice.net/api/v1 addresses and their keys keep working during the migration window; their answers carry Deprecation: true and a link to this page. Use this API and your Arianet account token for new work.

Quick start​

  1. In the panel, at cloud.ariaservice.net/users/api-tokens, create a token with the scopes you need (table below).
  2. List your sender lines and send a message from one of them (from is the line's number or its id):
export ARIANET_TOKEN="paste-your-token-here"
BASE=https://api.ariaservice.net/sms/v1
AUTH="Authorization: Bearer $ARIANET_TOKEN"

curl -s $BASE/lines -H "$AUTH"
curl -s -X POST $BASE/messages -H "$AUTH" -H "Content-Type: application/json" \
-d '{"from":"300074360","recipients":["09121234567","09351234567"],"body":"Your code is 1234","client_reference":"order-981"}'

The answer is 202 Accepted:

{"batch_id":"01JC8Z...","status":"processing","total_recipients":2,"total_cost_rial":3000,"filtered_blocked_count":0}

A send creates a batch and charges the account's wallet; follow it with GET /batches/{id} and GET /messages?batch_id=.... Numbers on your blocked list are skipped (filtered_blocked_count). The price per message, and whether it is charged per message or per SMS part, is in GET /prices.

Sending​

POST /messages takes:

FieldType
recipientsarray of strings, 1-100mobile numbers (required)
fromdigit stringthe sender line's number or id from GET /lines (required)
bodystring, at most 2000 charactersthe text; required unless template_id is given
template_idintegersend one of your templates instead of body
scheduled_atISO 8601 time, in the futuresend later instead of now
client_referencestringyour reference for this send; when absent the Idempotency-Key header is used. A send repeated with the same reference is not sent twice

With scheduled_at nothing is sent or charged yet: the answer is {"scheduled_id":12,"status":"pending","scheduled_at":"..."}, the send is listed in GET /scheduled and can be cancelled with DELETE /scheduled/{id} while it is pending.

Lists and pages​

Every list takes page and per_page (at most 200) and answers:

{"data":[...],"meta":{"total":134,"current_page":1,"per_page":50}}

GET /messages also filters by batch_id, status and to; GET /blocked-recipients by phone.

Authentication and scopes​

ScopeAllows
sms:messages:readsent and received messages, batches, scheduled sends, prices and the report
sms:messages:writesending and cancelling scheduled sends, blocked numbers and the webhook
sms:lines:readsender lines
sms:templates:readtemplates
sms:templates:writecreating, changing and deleting templates

A token without any scope is unrestricted; asking for a dedicated line (POST /lines) needs such a token. SMS runs in region iran-1 only and needs no Arianet-Region header.

Resources​

Method and pathPurpose
POST /messagessend now or at scheduled_at (see Sending)
GET /messages, GET /messages/{id}sent messages and their delivery
GET /batches, GET /batches/{id}send batches
GET /scheduled, DELETE /scheduled/{id}scheduled sends; cancel a pending one
GET /inboundmessages received on your lines
GET /lines, POST /linessender lines; ask for a dedicated line (number, note)
GET /templates, POST /templates, GET/PUT/DELETE /templates/{id}templates (name, body)
GET /blocked-recipients, POST /blocked-recipients, DELETE /blocked-recipients/{id}blocked numbers; input phone and note, output mobile and reason
GET /webhook, PUT /webhook, POST /webhook/secretthe webhook (url, enabled, events) and a new signing secret; see Webhooks
GET /reports/summarysent, delivered, failed and spend, with a daily breakdown (from, to)
GET /pricesprice per message and its volume tiers

The request and answer of every endpoint are in the interactive reference.

Statuses​

Objectstatus values
Message (GET /messages)queued, sent, delivered, failed, undelivered, rejected, expired
Batch (GET /batches)pending, processing, partial (some failed), completed, failed
Scheduled (GET /scheduled)scheduled, processing, sent, failed, cancelled

The status filter on GET /messages and GET /batches takes these values.

Message detail and timeline​

Every message in GET /messages also has:

FieldMeaning
operatorRecipient operator from the number prefix: mci, mtn, rightel, other (best effort for ported numbers)
encodingunicode or gsm
queued_atWhen the message was queued
provider.nameSending gateway, e.g. magfa
provider.message_idThe operator's (Magfa) tracking id
provider.status_codeLast operator status code: 0 no status yet, 8 reached the operator, 1 delivered to the handset, 2 not delivered to the handset, 16 never reached the operator, -1 expired
provider.error_codeMagfa's error code when it refused the message
delivery_checksNumber of delivery-status checks
last_checked_atTime of the last check

GET /messages/{id} also returns timeline: the steps in time order, each with type, at, status_code and error_code.

typeMeaning
queuedQueued
sent_to_providerAccepted by Magfa, tracking id assigned
provider_refusedRefused by Magfa (refunded)
status_checkedStatus check with no final result yet
delivered_to_operatorReached the operator's network, waiting for the handset
deliveredDelivered to the handset
not_deliveredNot delivered to the handset
not_sent_to_operatorNever reached the operator's network
expiredNo delivery report within 24 hours
rejectedRejected

Every batch in GET /batches also has channel (panel, api or system), sender_ip (masked, like 85.133.133.x) and completed_at.

Webhooks​

Set the URL, whether it is on, and the events you want with PUT /webhook:

curl -s -X PUT $BASE/webhook -H "$AUTH" -H "Content-Type: application/json" \
-d '{"url":"https://example.com/sms-hook","enabled":true,"events":["message.delivered","message.failed","inbound.received"]}'
EventWhen
batch.completeda send finished; always sent while the webhook is on
message.delivereda message reached the recipient
message.faileda message will never be delivered (failed, undelivered, rejected, expired)
inbound.receivedan SMS arrived on one of your dedicated lines

The last three are sent only when listed in events; they are off by default.

Each event is a POST of {"event","occurred_at","data"}, where data has the same shape the API returns for that object:

{"event":"message.delivered","occurred_at":"2026-11-12T17:20:00+00:00","data":{"id":"01m78c...","batch_id":"01m78c...","to":"09121234567","from":"300074360","body":"Your code is 1234","parts":1,"status":"delivered","cost_rial":1750,"error":null,"sent_at":"...","delivered_at":"...","created_at":"..."}}

The X-Signature header is the HMAC-SHA256 of the raw body keyed with your webhook signing secret; verify it. A non-2xx answer is retried up to 5 times with growing delays.

Errors​

Errors are JSON: {"code": "...", "message": "..."}.

HTTPMeaning
401no token, or an invalid one
402insufficient_balance
403insufficient_scope, forbidden, identity_needed, account_suspended, or sender_line_not_usable (the line is not yours to send from)
404not_found (for example a template of another account)
422validation_failed, all_recipients_blocked, already_blocked, not_cancellable
429rate_limit_exceeded / rate_limited
503sms_unavailable, region_unavailable
504gateway_timeout: no answer in time; the send may still have happened, check GET /messages before retrying (that is what client_reference is for)