HTTP REST API Reference
The SBS TELECOM HTTP API lets you send SMS, query delivery status, poll inbound messages and check your balance programmatically.
Base URL
https://portal.sbstelecom.net/api/v1
The API is served by the customer portal, on the same origin you sign in to. There is no separate api. hostname.
Authentication
Every request needs an API key and secret — a pair, not a single token. Create them in the portal under Developer → API keys. Three forms are accepted:
# 1. HTTP Basic (recommended)
curl -u KEY:SECRET https://portal.sbstelecom.net/api/v1/balance
# 2. Dedicated headers
X-API-Key: KEY
X-API-Secret: SECRET
# 3. Bearer, colon-separated
Authorization: Bearer KEY:SECRET
Missing or incorrect credentials return 401. If an IP allow-list is set on the API key or on the connection, the source IP must satisfy both, otherwise the request returns 403 source IP not allowed.
Send a Message — POST /api/v1/sms
Send a single SMS. to and text are required; from defaults to the sender ID configured on your connection.
POST /api/v1/sms
Content-Type: application/json
Idempotency-Key: order-4471-sms
{
"to": "+447911123456",
"from": "SBSTEL",
"text": "Your verification code is 847291"
}
Response 202 Accepted:
{
"message_id": "5f3c1b9a2e44...",
"status": "queued",
"to": "+447911123456",
"from": "SBSTEL",
"charged": true,
"cost": 0.0175,
"balance": 499.7025
}
Safe Retries — Idempotency-Key
Send the same Idempotency-Key again and nothing is sent and nothing is charged; you get the original message back with 200 OK:
{
"message_id": "5f3c1b9a2e44...",
"status": "duplicate",
"duplicate": true,
"charged": false,
"cost": 0
}
Tell a real send from a replay by the status code (202 vs 200) or the duplicate flag — never by message_id, which is identical in both. The key may also be given in the body as idempotency_key; the header wins.
Message Status — GET /api/v1/sms/{id}
{
"message_id": "5f3c1b9a2e44...",
"source": "SBSTEL",
"dest": "447911123456",
"status": "delivered",
"dlr": "DELIVRD",
"direction": "MT",
"country": "United Kingdom",
"operator": "EE"
}
An id outside your account returns 404.
Message Status Codes
queued— Accepted and charged, waiting for an egresssubmitted— Handed to the carrier, no receipt yetenroute— The carrier has it and is attempting deliverydelivered— Confirmed delivered to the handsetexpired— Validity period elapsed before deliveryundeliverable— The carrier rejected it or gave up. Not retriedrejected— Refused before it reached a carrier
Inbound Messages — GET /api/v1/inbound
Poll for received (MO) messages. limit defaults to 100, maximum 500; results ascend by id.
GET /api/v1/inbound?since=0&limit=100
{ "messages": [ ... ], "next_since": 88213, "count": 12 }
Pass the returned next_since as since on your next poll to page forward with no gaps or repeats.
Account Balance — GET /api/v1/balance
{
"balance": 499.70,
"currency": "USD",
"credit_limit": 100.00,
"credit_used": 0.00,
"available": 599.70,
"payment_terms": "prepaid",
"postpaid": false
}
Poll available, not balance. On a postpaid account sending stops when available reaches zero — which is at minus your credit limit, not at a zero balance.
Errors
Every error returns {"error":"message"}.
400— Invalid JSON, orto/textmissing401— Missing or invalid API credentials402— Insufficient balance403— Sender ID not approved or blocked; recipient opted out (replied STOP); source IP not allowed; identity verification required; message held for review404— Message not found422— No price or route for the destination429— Rate limit exceeded (seeRetry-After), or refused by a traffic control on your account500— Submit failed
Rate Limits
The default allowance is 10 requests per second, applied as a token bucket per connection — all API keys on one connection share it, exactly as an SMPP bind is throttled. Exceeding it returns 429 with Retry-After: 1. Higher limits are available; contact support.