# Agile Telecom SMS API — Complete Documentation (LLM-ready) > Full technical documentation for the Agile Telecom wholesale SMS API. > Generated from the docs at https://agiletelecom.com/documentation/ > Live reference and console: https://wholesale.agiletelecom.com/services/sms > Wholesale portal: https://wholesale.agiletelecom.com/ > Last updated: 2026-05-14 This file is intended to be loaded as context for an AI assistant. It covers SMS sending (REST and SMPP), inbound SMS, delivery reports, network intelligence (credit check, MNP) and operational best practices. --- ## 1. Product overview Agile Telecom provides wholesale A2P SMS over two transports: - **REST API** over HTTPS — recommended for application integrations. - **SMPP** binary protocol over TLS — recommended for high-throughput senders. Inbound SMS is offered on rented numbers, delivered via HTTP webhook or email. Network services: - **Credit check** — query the current account balance. - **MNP lookup** — query the operator and porting status of a mobile number. This product is SMS-only. ## 2. Endpoints REST base URLs (choose by account type): - Legacy customers: `https://wholesale.agiletelecom.com/services/` - New customers: `https://wholesale.agiletelecom.com/services/` SMS send: ``` POST /sms/send Content-Type: application/json ``` Credit check: ``` GET /sms/credit ``` MNP lookup: ``` GET /mnp/number-lookup?number=+393351234567 ``` SMPP: - Host: `smpp.agiletelecom.com` - Port: `2776` (TLS) - Modes: TX (send only), RX (receive DLR only), TRX (both) - Default cap: 4 active binds per account ## 3. Authentication Three layers, used in combination as required: 1. **API Key** — header `X-Api-Key: YOUR_KEY`. Recommended. 2. **Basic Auth** — header `Authorization: Basic base64(user:pass)`. Available on all endpoints. 3. **IP whitelist** — optional, enabled per account from the wholesale portal. Security notes: - HTTPS only. From 2026-01-15 plain HTTP is rejected. - Only `POST` requests with `Content-Type: application/json` are accepted on `/sms/send`. GET and form-encoded POST are removed. - Rotate API keys every 90 days in high-security environments. Error codes: - `401` — invalid credentials. - `403` — IP not whitelisted. - `429` — rate limit exceeded; apply exponential back-off with jitter. ## 4. SMS REST API ### Request ```json { "globalId": "req_2026_05_14_001", "maxIdLen": 64, "enableConcatenated": true, "enableUnicode": true, "enableDelivery": true, "simulation": false, "messages": [ { "destinations": ["+393351234567"], "ids": ["order-5571"], "sender": "Agile", "body": "Your order has shipped.", "scheduling": "2026-05-20 08:30:00.000+0200", "hexBody": false, "udhData": null } ] } ``` Top-level fields: | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `globalId` | string | no | – | Correlation ID for the whole request | | `maxIdLen` | int | no | 64 | Maximum length of per-message IDs | | `enableConcatenated` | bool | no | true | Split long bodies into multiple SMS parts | | `enableUnicode` | bool | no | true | Allow non-GSM characters (forces UCS-2) | | `enableDelivery` | bool | no | true | Receive delivery reports via webhook | | `simulation` | bool | no | false | Dry run; no send, no billing | | `messages` | array | yes | – | One or more messages | Per-message fields: | Field | Type | Required | Description | |-------|------|----------|-------------| | `destinations` | array | yes | International phone numbers, e.g. `+393351234567` | | `ids` | array | no | Stable per-destination tracking IDs | | `sender` | string | yes | Sender ID (numeric or alphanumeric ≤ 11 chars) | | `body` | string | yes | Message text | | `scheduling` | string | no | ISO-like timestamp with tz offset | | `hexBody` | bool | no | If true, `body` is interpreted as hex | | `udhData` | string | no | Custom UDH for advanced concatenation | ### Response ```json { "globalId": "req_2026_05_14_001", "accepted": true, "results": [ { "destination": "+393351234567", "id": "msg_abc123", "accepted": true, "statusCode": 200 } ] } ``` A `200` HTTP response does not guarantee every destination was accepted. Inspect `results[].statusCode`. ### Per-message status codes | Code | Meaning | Action | |------|---------|--------| | 200 | Accepted | Await DLR | | 400 | Invalid destination or body | Fix and resend | | 401 | Sender ID not authorised | Use different sender or pre-register | | 402 | Insufficient credit | Top up via portal | | 403 | Destination blacklisted / opted-out | Remove from list | | 429 | Per-route throttle | Slow down | | 500 | Internal error | Retry with back-off | ### Encoding | Encoding | 1 part | per part (concat) | |----------|--------|-------------------| | GSM-7 | 160 | 153 | | UCS-2 | 70 | 67 | A single non-GSM character (emoji, accented char outside extended set) flips the whole message to UCS-2. ### Sender IDs - Numeric: digits only; short codes or long numbers. - Alphanumeric: ≤ 11 characters; not replyable. Some countries (Italy, India, France, UAE…) require pre-registration. ## 5. SMPP protocol Use SMPP when sustained TPS is needed (thousands per second per bind). Implementation notes: - TLS only, port 2776. - Default 4 binds per account (mix TX/RX/TRX as needed). - Send `ENQUIRE_LINK` at least every 30 seconds. - If no response within 5 minutes, the connection is closed. Example (Python `smpplib`): ```python import smpplib import smpplib.gsm import smpplib.consts client = smpplib.client.Client( "smpp.agiletelecom.com", 2776, allow_unknown_opt_params=True ) client.connect() client.bind( smpplib.consts.SMPP_BIND_TX, system_id="YOUR_USERNAME", password="YOUR_PASSWORD", ) parts, encoding_flag, msg_type_flag = smpplib.gsm.make_parts("Hello World!") for part in parts: client.send_message( source_addr_ton=smpplib.consts.SMPP_TON_INTL, source_addr="12345", dest_addr_ton=smpplib.consts.SMPP_TON_INTL, destination_addr="+391234567890", short_message=part, ) ``` ## 6. Delivery reports (DLR) Enable with `enableDelivery: true`. Configure a webhook URL in the portal (Settings → Webhooks). Webhook payload: ```json { "id": "msg_abc123", "globalId": "req_2026_05_14_001", "destination": "+393351234567", "status": "DELIVERED", "statusCode": 0, "submitDate": "2026-05-14T10:23:11.000+0200", "doneDate": "2026-05-14T10:23:14.221+0200", "operator": "TIM", "parts": 1 } ``` Status codes: | Status | Code | Meaning | Billable | |--------|------|---------|----------| | DELIVERED | 0 | Handset confirmed | yes | | BUFFERED | 1 | Stored on operator | yes | | EXPIRED | 2 | Validity elapsed | yes | | REJECTED | 3 | Operator rejected | no | | UNDELIVERABLE | 4 | Wrong number / blacklisted / opt-out | no | | UNKNOWN | 5 | No final state in validity window | depends | | FAILED | 6 | Internal failure | no | Webhook contract: - HTTPS only. - Respond `200 OK` within 10s. - Retries with exponential back-off up to 24h. - Use `id` for dedup; order is not guaranteed. ## 7. Inbound SMS Rent a number from the wholesale portal. Choose webhook or email delivery in **Numbers → Inbound**. ### Webhook Form-encoded POST to your URL. Parameters: | Param | Description | |-------|-------------| | `originator` | Sender phone number | | `destination` | Your rented SIM | | `date_time` | YYYYMMDDHHMMSS | | `text` | SMS body | Mandatory response body: exactly `+OK`. Otherwise the message is retried up to 3 times, 15 minutes apart. After that it is discarded. ### Email Messages arrive from `smsin@agiletelecom.com` with subject `Agile Telecom -- SMS received`. ## 8. Network services ### Credit check ``` GET /sms/credit ``` Returns the current account balance. Use before large campaigns. ### MNP lookup ``` GET /mnp/number-lookup?number=+393351234567 ``` Returns operator, country, portability and validity. Use to drop invalid numbers before billing. ## 9. HTTP-level errors | HTTP | Meaning | Action | |------|---------|--------| | 200 | Accepted (check per-message statusCode) | Continue | | 400 | Bad request | Fix payload | | 401 | Invalid credentials | Re-check API key / Basic Auth | | 403 | IP not whitelisted | Add IP to whitelist | | 404 | Wrong endpoint / account-type URL | Use correct base URL | | 408 | Timeout | Retry with back-off | | 413 | Payload too large | Split batch | | 415 | Wrong Content-Type | Set application/json | | 429 | Rate limit exceeded | Exponential back-off, honour Retry-After | | 5xx | Server error | Retry with back-off, alert if persistent | ## 10. Best practices - **Idempotency**: stable `globalId` and `ids` enable safe retries. - **Retries**: `408`, `429`, `5xx` are retryable; cap at 3 attempts with back-off. - **Opt-out**: implement `STOP` / `UNSUBSCRIBE` handling on inbound, persist per phone number, consult before every marketing send. - **Quiet hours**: respect local rules (typically 21:00 → 09:00 local). - **Number hygiene**: MNP lookup before large campaigns, drop invalid numbers. - **Encoding**: prefer GSM-7, strip stray non-GSM characters to halve cost on transactional SMS. - **Sender ID**: pre-register alphanumerics where regulators require it (Italy, India, France, UAE). - **Observability**: log every submission with `globalId`, `id`, destination and timestamp; reconcile with DLRs. - **Security**: keys only in env vars; rotate every 90 days; HTTPS only. ## 11. Common pitfalls - "All 401 after midnight" → cron uses stale key. Reload from env on restart. - "Some destinations silently dropped" → sender ID not authorised for the country; check `REJECTED` DLRs. - "Late delivery" → handset off, `BUFFERED → DELIVERED`. Lower validity for time-sensitive sends. - "Random 429s" → per-route limits. Slow down or use SMPP. - "Webhook keeps being retried" → server didn't return `200` (or `+OK` on inbound) within the SLA. Queue work async. ## 12. Support - Email: help@agiletelecom.com - Wholesale portal: https://wholesale.agiletelecom.com/ - SMS API console: https://wholesale.agiletelecom.com/services/sms - Documentation site: https://agiletelecom.com/documentation/