FORMAT: 1A
HOST: https://api.texto.com.au

# Texto SMS API

Australia's simplest SMS API — send, receive, and track SMS for 3¢ per message.
Full human-readable docs: https://texto.com.au/api
Webhook reference: https://texto.com.au/webhooks (delivery receipts) and https://texto.com.au/inbound-webhooks (inbound messages)




## Authentication

All requests require an `Authorization: Bearer YOUR_API_KEY` header.
Generate keys at https://app.texto.com.au → Developer → API Keys.

## Rate Limits

- 50 requests/second per API key
- 25 concurrent requests per API key
- Higher limits available on request
- Max 1,000 recipients per `/send-batch` call

# Group Sending

## Send single SMS [/send]

### Send a message [POST]

+ Request (application/json)

    + Headers

            Authorization: Bearer YOUR_API_KEY

    + Body

            {
              "to": "+61412345678",
              "message": "Hello from Texto!",
              "sender": "+61400000000"
            }

+ Response 200 (application/json)

            {
              "message_id": "8c1f9b2e-1a4c-4f87-9bd2-2d2f6f6f6f6f",
              "credits_used": 1,
              "credits_remaining": 99
            }

+ Response 400 (application/json)
+ Response 401 (application/json)
+ Response 402 (application/json)

## Batch send [/send-batch]

### Send to up to 1,000 recipients [POST]

Supports `{{merge}}` placeholders resolved per recipient.

+ Request (application/json)

    + Headers

            Authorization: Bearer YOUR_API_KEY

    + Body

            {
              "recipients": [
                { "phone": "+61412345678", "merge_data": { "name": "Alice" } },
                { "phone": "0412345679" }
              ],
              "message": "Hi {{name}}, welcome!",
              "sender": "+61400000000"
            }

+ Response 200 (application/json)

            {
              "campaign_id": "uuid",
              "recipients_queued": 2,
              "invalid_numbers": [],
              "opted_out_count": 0,
              "credits_used": 2,
              "credits_remaining": 97
            }

# Group Tracking

## Message [/message/{id}]

+ Parameters
    + id: `8c1f9b2e-1a4c-4f87-9bd2-2d2f6f6f6f6f` (string, required) - Message UUID

### Get a message with delivery receipt [GET]

+ Response 200 (application/json)

            {
              "message": {
                "id": "uuid",
                "recipient": "+61412345678",
                "body": "Hello!",
                "status": "delivered",
                "sent_at": "2025-01-01T00:00:00Z"
              },
              "delivery_receipt": {
                "status": "delivered",
                "received_at": "2025-01-01T00:00:05Z"
              }
            }

+ Response 404 (application/json)

## Campaign [/campaign/{id}]

+ Parameters
    + id: `uuid` (string, required)

### Get campaign stats and messages [GET]

+ Response 200 (application/json)

            {
              "campaign": {
                "id": "uuid",
                "status": "completed",
                "recipient_count": 50,
                "created_at": "2025-01-01T00:00:00Z"
              },
              "messages": []
            }

# Group Inbound

## Inbox [/inbox{?limit,offset,from,date_from,date_to}]

+ Parameters
    + limit: 25 (integer, optional) - 1-100
    + offset: 0 (integer, optional)
    + from: `+614` (string, optional) - Partial sender match
    + date_from: `2025-01-01T00:00:00Z` (string, optional) - ISO 8601
    + date_to: `2025-01-31T23:59:59Z` (string, optional) - ISO 8601

### List inbound messages [GET]

+ Response 200 (application/json)

            {
              "messages": [],
              "total": 42,
              "limit": 25,
              "offset": 0
            }

# Group Opt-outs

## Opt-out list [/optouts]

### List opted-out numbers [GET]

+ Response 200 (application/json)

            {
              "optouts": [
                {
                  "number": "+61412345678",
                  "created_at": "2025-01-01T00:00:00Z"
                }
              ]
            }

# Group Account

## Balance [/balance]

### Check credit balance [GET]

+ Response 200 (application/json)

            { "credits": 100 }

# Group Webhooks

## Delivery receipt [/your-endpoint-url]

Configured in the Texto dashboard. Texto POSTs a JSON event for every delivery receipt.

Headers:

- `Content-Type: application/json`
- `X-Texto-Event: message.dlr`
- `X-Texto-Delivery: <UUID>` — unique per delivery, same UUID on retries (dedupe on this)
- `X-Texto-Signature: sha256=<hex>` — HMAC-SHA256 of the raw body using your signing secret

Retry schedule: up to 3 attempts (immediate, ~1 min, ~5 min). Reply with any 2xx within 15 seconds.

### Delivery receipt event [POST]

+ Request (application/json)

            {
              "message": {
                "id": "8c1f9b2e-1a4c-4f87-9bd2-2d2f6f6f6f6f",
                "recipient": "+61412345678",
                "body": "Hi Sam, your appointment is confirmed for Tue 9am.",
                "status": "delivered",
                "sent_at": "2026-05-06T03:14:22.000Z"
              },
              "delivery_receipt": {
                "status": "delivered",
                "received_at": "2026-05-06T03:14:25.421Z"
              }
            }

+ Response 200

## Inbound message [/your-endpoint-url]

Configured in the Texto dashboard. Texto POSTs a JSON event for every inbound message (MO) received on any of your numbers, including opt-out replies (STOP, UNSUBSCRIBE, etc. — `is_optout` is true and the opt-out is already recorded on your account).

Headers:

- `Content-Type: application/json`
- `X-Texto-Event: message.inbound`
- `X-Texto-Delivery: <UUID>` — unique per delivery attempt, same UUID on retries
- `X-Texto-Signature: sha256=<hex>` — HMAC-SHA256 of the raw body using your inbound signing secret

Dedupe on `message_id` in the body (stable per inbound message across retries).
Retry schedule: up to 3 attempts (immediate, ~1 min, ~5 min). Reply with any 2xx within 15 seconds.

### Inbound message event [POST]

+ Request (application/json)

            {
              "event": "message.inbound",
              "message_id": "8c1f9b2e-1a4c-4f87-9bd2-2d2f6f6f6f6f",
              "from": "+61412345678",
              "to": "+61480123456",
              "body": "STOP",
              "received_at": "2026-05-06T03:14:25.421Z",
              "in_reply_to": "1d4e9b2e-1a4c-4f87-9bd2-2d2f6f6f6f6f",
              "is_optout": true
            }

+ Response 200

# Group Account Management

Multi-account endpoints for SaaS platforms, agencies and resellers. Create and manage a
sub-account per customer, allocate credits, provision API keys, invite users, assign
dedicated numbers, configure webhooks and pull per-account or whole-of-group reporting.
Requires account hierarchy to be enabled on the parent account — ask us to switch it on.

## Accounts [/accounts]

### List accounts [GET]

+ Response 200 (application/json)

            {
              "accounts": [
                {
                  "id": "uuid",
                  "business_name": "Client A Pty Ltd",
                  "parent_account_id": "uuid",
                  "created_at": "2026-01-01T00:00:00Z"
                }
              ]
            }

### Create a sub-account [POST]

+ Request (application/json)

            {
              "business_name": "Client A Pty Ltd",
              "email": "ops@clienta.com.au",
              "daily_limit": 1000,
              "managed_by_parent": false,
              "team_access_from_parent": false,
              "optout_exempt": false,
              "seed_credits": 500
            }

+ Response 201 (application/json)

            { "account_id": "uuid", "id": "uuid", "email": "ops@clienta.com.au" }

+ Response 403 (application/json)
+ Response 409 (application/json)

## Account [/account/{id}]

+ Parameters
    + id: `uuid` (string, required) - Account UUID

### Get account detail [GET]

+ Response 200 (application/json)

            {
              "account": {
                "id": "uuid",
                "business_name": "Client A Pty Ltd",
                "credits": 480,
                "account_status": "active",
                "daily_send_limit": 1000,
                "managed_by_parent": false,
                "team_access_from_parent": true,
                "user_count": 3,
                "number_count": 1
              }
            }

### Update a sub-account [PATCH]

+ Request (application/json)

            { "business_name": "Client A Group", "daily_limit": 5000, "team_access_from_parent": true }

+ Response 200 (application/json)

### Delete a sub-account [DELETE]

+ Response 200 (application/json)

## Account balance [/account/{id}/balance]

+ Parameters
    + id: `uuid` (string, required)

### Get sub-account balance [GET]

+ Response 200 (application/json)

            { "credits": 480 }

## Allocate credits [/account/{id}/credits/allocate]

+ Parameters
    + id: `uuid` (string, required)

### Move credits from parent to sub-account [POST]

+ Request (application/json)

            { "amount": 500 }

+ Response 200 (application/json)

## Recall credits [/account/{id}/credits/recall]

+ Parameters
    + id: `uuid` (string, required)

### Move credits back to the parent [POST]

+ Request (application/json)

            { "amount": 200 }

+ Response 200 (application/json)

## API keys [/account/{id}/keys]

+ Parameters
    + id: `uuid` (string, required)

### List API keys [GET]

+ Response 200 (application/json)

            {
              "keys": [
                { "id": "uuid", "name": "Client A production", "prefix": "txt_ab12cd34", "revoked_at": null }
              ]
            }

## Provision API key [/account/{id}/key]

+ Parameters
    + id: `uuid` (string, required)

### Create an API key (returned once) [POST]

+ Request (application/json)

            { "name": "Client A production" }

+ Response 201 (application/json)

            { "id": "uuid", "key": "txt_...", "prefix": "txt_ab12cd34" }

## Revoke API key [/account/{id}/key/{keyId}]

+ Parameters
    + id: `uuid` (string, required)
    + keyId: `uuid` (string, required)

### Revoke [DELETE]

+ Response 200 (application/json)

## Account users [/account/{id}/users]

+ Parameters
    + id: `uuid` (string, required)

### List users [GET]

+ Response 200 (application/json)

            {
              "users": [
                { "id": "uuid", "email": "ops@clienta.com.au", "can_send": true, "can_billing": false, "status": "active", "source": "direct" }
              ]
            }

### Invite a user [POST]

+ Request (application/json)

            { "email": "newuser@clienta.com.au", "name": "Sam", "can_send": true, "can_billing": false }

+ Response 201 (application/json)
+ Response 409 (application/json)

## Remove user [/account/{id}/users/{memberId}]

+ Parameters
    + id: `uuid` (string, required)
    + memberId: `uuid` (string, required)

### Remove [DELETE]

+ Response 200 (application/json)

## Account access [/account/{id}/access]

+ Parameters
    + id: `uuid` (string, required)

### Read access settings [GET]

+ Response 200 (application/json)

            {
              "managed_by_parent": false,
              "team_access_from_parent": true,
              "direct_team": [],
              "eligible_parent_team": []
            }

### Update access settings [PUT]

+ Request (application/json)

            { "managed_by_parent": true, "team_access_from_parent": true }

+ Response 200 (application/json)

## Parent team [/team]

### List users on your own account [GET]

+ Response 200 (application/json)

            { "users": [] }

## Numbers [/numbers]

### List your dedicated numbers [GET]

+ Response 200 (application/json)

            { "numbers": [ { "id": "uuid", "number": "+61480123456", "price_monthly": 15 } ] }

## Group numbers [/numbers/group]

### List numbers across the hierarchy [GET]

+ Response 200 (application/json)

            { "numbers": [] }

## Available numbers [/numbers/available{?country,limit,offset}]

+ Parameters
    + country: `AU` (string, optional) - AU or NZ
    + limit: 20 (integer, optional) - Max 100
    + offset: 0 (integer, optional)

### Browse numbers you can take up [GET]

+ Response 200 (application/json)

            { "numbers": [], "limit": 20, "offset": 0 }

## Sub-account numbers [/account/{id}/numbers]

+ Parameters
    + id: `uuid` (string, required)

### List numbers on a sub-account [GET]

+ Response 200 (application/json)

            { "numbers": [] }

## Assign numbers [/account/{id}/numbers/assign]

+ Parameters
    + id: `uuid` (string, required)

### Assign numbers to a sub-account [POST]

+ Request (application/json)

            { "numbers": ["+61480123456"] }

+ Response 200 (application/json)

## Recall numbers [/account/{id}/numbers/recall]

+ Parameters
    + id: `uuid` (string, required)

### Take numbers back [POST]

+ Request (application/json)

            { "numbers": ["+61480123456"] }

+ Response 200 (application/json)

## Webhook configuration [/webhooks]

### Read webhook endpoints [GET]

+ Response 200 (application/json)

            {
              "delivery_receipt": { "url": "https://yourapp.com/hooks/dlr", "enabled": true, "signing_enabled": true },
              "inbound": null
            }

## Delivery receipt webhook [/webhooks/delivery]

### Create or update the endpoint [PUT]

+ Request (application/json)

            { "url": "https://yourapp.com/hooks/dlr", "enabled": true }

+ Response 200 (application/json)

### Rotate the signing secret [POST]

+ Response 200 (application/json)

            { "secret": "shown once" }

## Inbound webhook [/webhooks/inbound]

### Create or update the endpoint [PUT]

+ Request (application/json)

            { "url": "https://yourapp.com/hooks/inbound", "enabled": true }

+ Response 200 (application/json)

### Rotate the signing secret [POST]

+ Response 200 (application/json)

            { "secret": "shown once" }

# Group Reporting

## Account report [/report{?from,to,direction,status,country,campaign_id,keyword,number}]

+ Parameters
    + from: `2026-01-01` (string, optional) - YYYY-MM-DD, defaults to 30 days ago
    + to: `2026-01-31` (string, optional) - YYYY-MM-DD, defaults to today
    + direction: `all` (string, optional) - all, outbound or inbound
    + status: `delivered` (string, optional)
    + country: `AU` (string, optional) - AU or NZ
    + campaign_id: `uuid` (string, optional)
    + keyword: `JOIN` (string, optional)
    + number: `+61480123456` (string, optional)

### Usage summary for your account [GET]

+ Response 200 (application/json)

            {
              "account_id": "uuid",
              "from": "2026-01-01",
              "to": "2026-01-31",
              "summary": {
                "messages": { "total": 12480, "parts": 13102, "sent": 12400, "delivered": 12190, "failed": 210 },
                "credits_consumed": 13102,
                "inbound": 80,
                "optouts": 14
              }
            }

## Sub-account report [/account/{id}/report{?from,to}]

+ Parameters
    + id: `uuid` (string, required)
    + from: `2026-01-01` (string, optional)
    + to: `2026-01-31` (string, optional)

### Usage summary for one sub-account [GET]

+ Response 200 (application/json)

## Group report [/report/group{?from,to}]

+ Parameters
    + from: `2026-01-01` (string, optional)
    + to: `2026-01-31` (string, optional)

### Usage across every account in your hierarchy [GET]

+ Response 200 (application/json)

            {
              "from": "2026-01-01",
              "to": "2026-01-31",
              "accounts": [
                { "account_id": "uuid", "business_name": "Client A Pty Ltd", "credits_consumed": 8600, "delivery_rate": 0.9797 }
              ],
              "grand_total": { "credits_consumed": 21702, "inbound": 122, "optouts": 23 }
            }
