# Send SMS

> Send SMS to up to 100 recipients with one POST /sms/send request. Automatic GSM-7/UCS-2 encoding, per-recipient results, atomic batch behavior and cost summary. Examples in 8 languages.
> Source: https://docs.23telecom.co.uk/sms/send/

Instructions for LLMs: This is one page of the 23 Telecom messaging API docs
(SMS today; more channels planned). Base URL: https://restlink23telecom.com/api/v1,
auth via the X-API-Key header. Match errors on the error_code field, never on
description text. Full docs: https://docs.23telecom.co.uk/llms-full.txt · Schemas: https://docs.23telecom.co.uk/openapi.yaml

Send an SMS to one or more recipients.

`POST /api/v1/sms/send` (permission: `sms.send`)

## Request

```
POST https://restlink23telecom.com/api/v1/sms/send
Header: X-API-Key: <your key>
```
*(The web page shows this example in cURL, Node.js, Python, PHP, Ruby, Java, Go and .NET.)*

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `to` | string[] | Yes | 1 to 100 phone numbers in E.164 format |
| `message` | string | Yes | Message text — encoding is detected automatically |
| `sender_id` | string | Yes | Alphanumeric sender ID (max 11 chars) or phone number |

## Response

```json title="200 OK"
{
  "status": true,
  "messages": [
    {"dnis": "+14155551234", "message_id": "api_42_1743667200123456789_a3f8b2c1d9e45f67", "segment_num": 1},
    {"dnis": "+447911123456", "message_id": "api_42_1743667200123456789_b7c4e8f1a2d3690b", "segment_num": 1}
  ],
  "results": [
    {"dnis": "+14155551234", "message_id": "api_42_1743667200123456789_a3f8b2c1d9e45f67", "segments": 1, "status": "accepted"},
    {"dnis": "+447911123456", "message_id": "api_42_1743667200123456789_b7c4e8f1a2d3690b", "segments": 1, "status": "accepted"}
  ],
  "summary": {
    "total_recipients": 2,
    "total_segments": 2,
    "total_cost": 0.02,
    "encoding": "GSM-7",
    "accepted_count": 2,
    "blocked_count": 0,
    "queue_error_count": 0,
    "config_error_count": 0,
    "db_error_count": 0
  }
}
```

| Field | Description |
| --- | --- |
| `messages` | Accepted messages only (backward-compatible array) |
| `results` | **All** recipients with their individual status |
| `summary` | Totals: accepted, blocked, errors, cost, detected encoding |

### Per-recipient statuses

| `results[].status` | Meaning |
| --- | --- |
| `accepted` | Queued for delivery |
| `blocked_country` | Recipient's country is in your blocked list |
| `queue_error` | Internal queue failure |
| `config_error` | SMS sending not configured on the account |

  A multi-recipient request is **all-or-nothing** at the persistence layer:
  either every recipient is queued or none is. If the database is briefly
  unavailable you get `500 DB_ERROR` and nothing was sent — simply retry the
  entire request. You will never get a silent partial send.

## Errors

| HTTP | Code | Description |
| --- | --- | --- |
| 400 | `INVALID_BODY` | Cannot parse request body |
| 400 | `INVALID_TO` | Missing or empty `to` array |
| 400 | `INVALID_MESSAGE` | Missing or empty `message` |
| 400 | `INVALID_SENDER` | Missing or empty `sender_id` |
| 400 | `TOO_MANY_RECIPIENTS` | Over 100 recipients |
| 402 | `INSUFFICIENT_BALANCE` | Balance too low — response includes `balance`, `estimated_cost`, `currency`, `total_recipients`, `billable_recipients`, `segments` |
| 403 | `NO_SMS_ACCESS` | SMS not enabled on your account |
| 403 | `CONFIG_ERROR` | SMS credentials incomplete — contact support |
| 403 | `WORKSPACE_NOT_AVAILABLE` | Target workspace was deleted — use a live workspace |
| 500 | `DB_ERROR` | Could not queue (atomic rollback) — retry the whole request |

## Delivery tracking

The send response confirms **acceptance**, not delivery. To learn whether the
message reached the handset:

- **Webhooks (recommended):** receive a [delivery report](/webhooks/delivery)
  on your server the moment the carrier reports it.
- **Polling:** call [GET /sms/status/:message_id](/sms/status) until the
  status is final.