Base URL
https://api.britishtext.com/v1Content-Type
application/jsonAuthentication
All API requests must be authenticated using HTTP Basic Auth with your Account SID as the username and your Auth Token as the password. You can find both in your account settings.
Authorization: Basic base64(account_sid:auth_token)
# Or Bearer token
Authorization: Bearer {auth_token}Account SID (public)
AC_bt_xxxxxxxxxxxxAuth Token (secret)
at_xxxxxxxxxxxxxxxxxxxxxxxxxxxxNever expose your Auth Token in client-side code or public repositories.
Quick start
Send your first SMS in under two minutes.
curl -X POST https://api.britishtext.com/v1/messages \
-u "AC_bt_xxxxxxxxxxxx:at_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "+447911283461",
"from": "+447700142857",
"body": "Your appointment is confirmed for 2pm tomorrow."
}'Replace the example Account SID, Auth Token, and phone numbers with your own values. Use AC_test_ credentials and +447700900000 to test in the sandbox.
Messages
/v1/messagesSend SMS
Send an SMS message to a UK or international phone number. The sender must be a number you have rented or an approved alphanumeric sender ID.
Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| to | string | Yes | Recipient phone number in E.164 format (e.g. +447911283461) |
| from | string | Yes | Rented UK number (E.164) or approved alphanumeric sender ID (max 11 chars) |
| body | string | Yes | Message text. Max 1,600 characters. Automatically segmented into multiple SMS parts. |
| webhook_url | string | No | Override the account-level delivery report webhook for this message only |
| reference | string | No | Your own reference ID for correlation (e.g. an order ID) |
| scheduled_at | string | No | ISO 8601 datetime to schedule future delivery (e.g. 2026-04-01T09:00:00Z) |
Request body
{
"to": "+447911283461",
"from": "+447700142857",
"body": "Your appointment is confirmed for 2pm tomorrow.",
"reference": "appt-reminder-12345"
}Response
{
"id": "msg_xxxxxxxxxxxx",
"to": "+447911283461",
"from": "+447700142857",
"body": "Your appointment is confirmed for 2pm tomorrow.",
"direction": "outbound",
"status": "queued",
"segments": 1,
"cost": 4,
"currency": "GBP",
"reference": "appt-reminder-12345",
"created_at": "2026-03-24T10:30:00Z",
"scheduled_at": null
}/v1/messages/bulkSend bulk SMS
Send up to 1,000 SMS messages in a single request. Returns a batch ID for tracking the overall dispatch.
Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| messages | array | Yes | Array of message objects, each with to, from, and body fields. Max 1,000 items. |
| webhook_url | string | No | Delivery report webhook applied to all messages in this batch |
Request body
{
"messages": [
{ "to": "+447911283461", "from": "+447700142857", "body": "Hi Sarah, your order is ready." },
{ "to": "+447523891024", "from": "+447700142857", "body": "Hi James, your order is ready." }
],
"webhook_url": "https://app.britishtext.com/webhooks/delivery"
}Response
{
"batch_id": "batch_xxxxxxxxxxxx",
"total": 2,
"queued": 2,
"failed": 0,
"messages": [
{ "id": "msg_xxxxxxxxxxxx", "to": "+447911283461", "status": "queued" },
{ "id": "msg_xxxxxxxxxxxx", "to": "+447523891024", "status": "queued" }
]
}/v1/messagesList messages
Retrieve a paginated list of sent and received messages. Supports filtering by direction, status, sender, recipient, date range, and your own reference ID.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| direction | string | No | inbound or outbound |
| status | string | No | queued, sent, delivered, failed, or undelivered |
| from | string | No | Filter by sender number (E.164) |
| to | string | No | Filter by recipient number (E.164) |
| since | string | No | ISO 8601 — return messages after this timestamp |
| until | string | No | ISO 8601 — return messages before this timestamp |
| reference | string | No | Filter by your client reference ID |
| limit | integer | No | Results per page. Default: 20. Max: 100. |
| offset | integer | No | Pagination offset |
Response
{
"messages": [
{
"id": "msg_xxxxxxxxxxxx",
"to": "+447911283461",
"from": "+447700142857",
"body": "Your appointment is confirmed for 2pm tomorrow.",
"direction": "outbound",
"status": "delivered",
"segments": 1,
"cost": 4,
"reference": "appt-reminder-12345",
"created_at": "2026-03-24T10:30:00Z",
"sent_at": "2026-03-24T10:30:01Z",
"delivered_at": "2026-03-24T10:30:03Z"
}
],
"total": 150,
"limit": 20,
"offset": 0
}/v1/messages/{id}Get message details
Retrieve the full details of a single message by its ID, including carrier error information if the message failed.
Response
{
"id": "msg_xxxxxxxxxxxx",
"to": "+447911283461",
"from": "+447700142857",
"body": "Your appointment is confirmed for 2pm tomorrow.",
"direction": "outbound",
"status": "failed",
"segments": 1,
"cost": 4,
"reference": "appt-reminder-12345",
"created_at": "2026-03-24T10:30:00Z",
"sent_at": "2026-03-24T10:30:01Z",
"delivered_at": null,
"error_code": "30006",
"error_message": "Landline or unreachable carrier"
}Numbers
/v1/numbers/availableBrowse available numbers
Search the pool of available UK mobile numbers you can rent. Filter by area code, type, or a digit pattern.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| area_code | string | No | Filter by UK area code (e.g. 020 for London, 0161 for Manchester) |
| type | string | No | mobile or geographic. Default: mobile |
| pattern | string | No | Digit pattern to search for (e.g. 7700) |
| limit | integer | No | Results per page. Default: 20. Max: 100. |
| offset | integer | No | Pagination offset |
Response
{
"numbers": [
{
"number": "+447700142857",
"number_formatted": "+44 7700 142 857",
"type": "mobile",
"area": "London",
"area_code": "020",
"monthly_cost": 300,
"currency": "GBP",
"capabilities": ["sms_inbound", "sms_outbound"]
}
],
"total": 1250,
"limit": 20,
"offset": 0
}/v1/numbers/rentRent a number
Provision a number from the available pool. The number is immediately usable for sending and receiving SMS after a successful response. Provisioning completes within 60 seconds.
Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| number | string | Yes | The E.164 number to rent, as returned by GET /v1/numbers/available |
| webhook_url | string | No | Webhook endpoint to receive inbound messages on this number |
| label | string | No | Friendly label for your own reference (e.g. Main business line) |
Request body
{
"number": "+447700142857",
"webhook_url": "https://app.britishtext.com/webhooks/inbound",
"label": "Main business line"
}Response
{
"id": "num_xxxxxxxxxxxx",
"number": "+447700142857",
"number_formatted": "+44 7700 142 857",
"type": "mobile",
"status": "active",
"label": "Main business line",
"webhook_url": "https://app.britishtext.com/webhooks/inbound",
"monthly_cost": 300,
"rented_at": "2026-03-24T10:30:00Z",
"renews_at": "2026-04-24T10:30:00Z"
}/v1/numbers/{id}Release a number
Return a rented number to the available pool. The number enters a 72-hour cooldown before it can be rented again by anyone. Billing for the current month is not refunded.
Response
{
"id": "num_xxxxxxxxxxxx",
"number": "+447700142857",
"status": "released",
"released_at": "2026-03-24T15:00:00Z"
}/v1/numbersList rented numbers
Retrieve all numbers currently rented on your account, including usage statistics and renewal dates.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No | active or suspended. Default: all |
| type | string | No | mobile or geographic |
| limit | integer | No | Results per page. Default: 20. |
| offset | integer | No | Pagination offset |
Response
{
"numbers": [
{
"id": "num_xxxxxxxxxxxx",
"number": "+447700142857",
"number_formatted": "+44 7700 142 857",
"type": "mobile",
"status": "active",
"label": "Main business line",
"webhook_url": "https://app.britishtext.com/webhooks/inbound",
"monthly_cost": 300,
"rented_at": "2026-03-24T10:30:00Z",
"renews_at": "2026-04-24T10:30:00Z",
"messages_sent": 247,
"messages_received": 89
}
],
"total": 2,
"limit": 20,
"offset": 0
}Webhooks
BritishText pushes real-time events to your HTTPS endpoint via HTTP POST. Webhooks are the recommended way to receive inbound messages and delivery reports without polling. Your endpoint must return a 2xx response within 10 seconds. Failed deliveries are retried 3 times with exponential backoff (10s, 60s, 300s).
/v1/webhooksRegister webhook
Subscribe to one or more event types. Supply a shared secret to enable HMAC signature verification on incoming payloads.
Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Your HTTPS endpoint to receive events. Must be publicly reachable. |
| events | array | Yes | Event types to subscribe to. See the event types table below. |
| secret | string | No | Shared secret used to compute the HMAC-SHA256 signature header |
Request body
{
"url": "https://app.example.com/webhooks/events",
"events": ["message.received", "message.delivered", "message.failed"],
"secret": "whsec_your_signing_secret"
}Response
{
"id": "wh_xxxxxxxxxxxx",
"url": "https://app.example.com/webhooks/events",
"events": ["message.received", "message.delivered", "message.failed"],
"status": "active",
"created_at": "2026-03-24T10:30:00Z"
}Event types
| Event | Trigger |
|---|---|
| message.received | Inbound SMS received on a rented number |
| message.delivered | Outbound SMS confirmed delivered by carrier |
| message.failed | Outbound SMS delivery failed |
| message.sent | Outbound SMS accepted by carrier |
| number.suspended | Rented number suspended due to spam/abuse |
| number.expiring | Rented number renewal due in 7 days |
Inbound message payload (message.received)
{
"event": "message.received",
"timestamp": "2026-03-24T10:30:00Z",
"data": {
"id": "msg_xxxxxxxxxxxx",
"from": "+447911283461",
"to": "+447700142857",
"body": "Yes, I'd like to book for 3pm.",
"direction": "inbound",
"received_at": "2026-03-24T10:30:00Z"
}
}Delivery report payload (message.delivered)
{
"event": "message.delivered",
"timestamp": "2026-03-24T10:30:03Z",
"data": {
"id": "msg_xxxxxxxxxxxx",
"status": "delivered",
"delivered_at": "2026-03-24T10:30:03Z",
"reference": "appt-reminder-12345"
}
}Signature verification
Every webhook delivery includes the following headers. Use X-BritishText-Signature to verify the payload was not tampered with.
X-BritishText-Event: message.received X-BritishText-Signature: sha256=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx X-BritishText-Timestamp: 1711270200 X-BritishText-Delivery-Id: del_xxxxxxxxxxxx
Verification algorithm
// Node.js example
const crypto = require("crypto");
function verifyWebhook(rawBody, timestamp, signature, secret) {
const signed = crypto
.createHmac("sha256", secret)
.update(timestamp + "." + rawBody)
.digest("hex");
return `sha256=${signed}` === signature;
}Reject payloads where the timestamp is more than 5 minutes old to prevent replay attacks.
Lookup
/v1/lookup/{number}HLR number lookup
Perform a real-time Home Location Register (HLR) lookup on any phone number. Returns carrier, line type, porting status, and reachability. Useful for validating contacts before a campaign.
Response
{
"number": "+447911283461",
"number_formatted": "+44 7911 283 461",
"valid": true,
"type": "mobile",
"carrier": {
"name": "EE",
"mcc": "234",
"mnc": "30"
},
"country": {
"code": "GB",
"name": "United Kingdom"
},
"ported": false,
"reachable": true,
"cost": 1
}Account
/v1/accountGet account info
Retrieve your account details including current balance, usage statistics for today and this month, and your plan's rate limits.
Response
{
"sid": "AC_bt_xxxxxxxxxxxx",
"name": "Acme Ltd",
"status": "active",
"balance": 50000,
"currency": "GBP",
"created_at": "2026-01-15T09:00:00Z",
"usage": {
"messages_sent_today": 1247,
"messages_sent_month": 28450,
"numbers_rented": 2,
"lookups_today": 34
},
"rate_limit": {
"messages_per_second": 50,
"messages_per_day": 100000
}
}/v1/account/api-keysCreate API key
Create a new API key with an optional label. The auth token is returned only once in this response — store it securely. You can create separate keys per environment (production, staging, development) and revoke them independently.
Request body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| label | string | No | Friendly label for this key (e.g. Production, Staging) |
Request body
{
"label": "Production"
}Response
{
"id": "key_xxxxxxxxxxxx",
"token": "at_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"label": "Production",
"created_at": "2026-03-24T10:30:00Z"
}Rate limits
Rate limits are applied per account. Every response includes the following headers:
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 97 X-RateLimit-Reset: 1711270260
When rate limited (429), the response includes a Retry-After header indicating how many seconds to wait.
| Resource | Limit |
|---|---|
| Send message (single) | 50/second, 100,000/day |
| Send message (bulk) | 10 requests/minute (each up to 1,000 messages) |
| API requests (general) | 100/second |
| Number lookup (HLR) | 10/second, 10,000/day |
Error codes
All errors follow a consistent JSON structure. The code field is machine-readable; use it in your error handling logic.
{
"error": {
"code": "invalid_parameter",
"message": "The 'to' field must be a valid E.164 phone number.",
"param": "to"
}
}| HTTP | Code | Description |
|---|---|---|
| 400 | invalid_parameter | A request parameter is missing or has an invalid value |
| 401 | authentication_failed | Invalid Account SID or Auth Token |
| 402 | insufficient_balance | Account balance too low to complete the operation |
| 404 | number_not_found | Number ID does not exist or does not belong to this account |
| 409 | number_unavailable | Requested number is no longer available for rent |
| 422 | message_rejected | Message rejected by carrier (spam filter, invalid content) |
| 429 | rate_limit_exceeded | Too many requests — retry after the Retry-After header value |
| 502 | carrier_error | Upstream carrier returned an error |
| 500 | internal_error | Unexpected server error — contact support if it persists |
Sandbox environment
Use sandbox credentials to test your integration without consuming credits or sending real messages. The sandbox simulates the full delivery lifecycle with a 2-second delay.
Sandbox Account SID prefix
AC_test_Sandbox Auth Token prefix
at_test_Magic test numbers
| Number | Behaviour |
|---|---|
| +447700900000 | Always delivers successfully |
| +447700900001 | Always fails delivery |
| +447700900002 | Delivers after a 30-second delay |
- Messages are simulated — not delivered to real devices
- Webhooks still fire with sandbox: true in the payload
- Zero credits consumed in sandbox mode
- Full delivery lifecycle simulated: queued → sent → delivered (2s delay)
Official SDKs
Official client libraries are in development. Each SDK will wrap the REST API with typed methods, automatic retry, webhook signature verification, and pagination helpers.
Node.js
@britishtext/sdkPython
britishtextPHP
britishtext/britishtext-phpGo
github.com/britishtext/britishtext-goIn the meantime, any HTTP client works with the REST API directly. See the quick start examples above.