SMS Messages

Send, retrieve, and track SMS messages through the Txtly API. SMS is delivered via AWS SNS and supports both transactional and promotional message types.

Send SMS

POST
/v1/sms

Send a single SMS message. Supports idempotency via the Idempotency-Key header.

Parameters

ParameterTypeDescription
torequiredstringRecipient phone number in E.164 format (e.g. +61412345678)
bodyrequiredstringMessage text (max 1600 characters). Long messages are automatically split into segments.
senderIdrequiredstring (UUID)ID of the SMS sender identity to send from. Must be active.
messageTypestringEither "transactional" or "promotional". Defaults to the sender's default message type.

Headers

ParameterTypeDescription
Idempotency-KeystringUnique key to prevent duplicate sends. If a message with the same key exists, the existing message is returned.

Example Request

curl -X POST https://api.txtly.com.au/v1/sms \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-key-123" \
  -d '{
    "to": "+61412345678",
    "body": "Your verification code is 123456",
    "senderId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
    "messageType": "transactional"
  }'

Example Response

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "to": "+61412345678",
  "body": "Your verification code is 123456",
  "messageType": "transactional",
  "status": "sent",
  "segments": 1,
  "providerMessageId": "sns-msg-abc123",
  "senderId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
  "senderName": "Acme Alerts",
  "createdAt": "2026-04-12T10:30:00Z",
  "sentAt": "2026-04-12T10:30:01Z"
}

Billing happens behind the scenes: each send is debited from your monthly SMS credit at the destination zone's retail rate, overflowing to metered overage billing once the credit is exhausted. See pricing zones.

Error: Unsupported destination

Sends to country codes outside the rate card return 422 Unprocessable Entity before any provider call:

{
  "error": "UNSUPPORTED_DESTINATION_COUNTRY",
  "message": "SMS sends to this destination country are not currently supported. Contact support to request coverage."
}

Country resolution is based on the E.164 prefix. See pricing zones for the supported list.

Send Batch SMS

POST
/v1/sms/batch

Send up to 100 SMS messages in a single request. The batch is validated up front; individual provider failures do not abort the batch.

Parameters

ParameterTypeDescription
messagesrequiredobject[]Array of SMS message objects (max 100, or your plan's batch limit). Each object has the same fields as the single send endpoint.

Example Request

curl -X POST https://api.txtly.com.au/v1/sms/batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "to": "+61412345678",
        "body": "Your order has shipped!",
        "senderId": "d290f1ee-6c54-4b01-90e6-d701748f0851"
      },
      {
        "to": "+61498765432",
        "body": "Your appointment is confirmed for tomorrow at 2pm",
        "senderId": "d290f1ee-6c54-4b01-90e6-d701748f0851"
      }
    ]
  }'

Example Response

{
  "data": [
    {
      "id": "a1b2c3d4-...",
      "to": "+61412345678",
      "status": "sent",
      "segments": 1,
      "senderName": "Acme Alerts"
    },
    {
      "id": "e5f6a7b8-...",
      "to": "+61498765432",
      "status": "sent",
      "segments": 1,
      "senderName": "Acme Alerts"
    }
  ]
}

Up-front Checks

Before anything is sent, the whole batch is checked and the request is rejected if any message fails (nothing is sent and nothing is billed):

  • Daily SMS message limit — 429 if the batch would exceed it.
  • Daily SMS spend cap — the batch's projected cost (the sum of each destination's retail rate) is pre-checked against your daily spend cap. If it would exceed the cap, the API returns 429 with {"error", "spent_today", "limit"}.
  • Unsupported destination country — 422 UNSUPPORTED_DESTINATION_COUNTRY with the offending index.
  • Opted-out recipient — 403 RECIPIENT_OPTED_OUT with the offending index.
  • Inactive or unknown senders, and message-type policy violations — 400.

After these checks pass, messages are sent individually. A message that fails at the provider is returned with status failed (and is not billed) while the rest of the batch continues.

List SMS Messages

GET
/v1/sms

Retrieve a paginated list of SMS messages with optional filtering.

Query Parameters

ParameterTypeDescription
limitintegerNumber of messages to return (default 20, max 100)
cursorstringCursor for pagination (from previous response)
statusstringFilter by status: accepted, queued, sent, delivered, failed, undelivered
senderIdstringFilter by sender identity ID
searchstringSearch by phone number or message body text

Get SMS Message

GET
/v1/sms/{id}

Retrieve a single SMS message by ID.

Path Parameters

ParameterTypeDescription
idrequiredstring (UUID)SMS message ID

Get SMS Events

GET
/v1/sms/{id}/events

Retrieve delivery events for a specific SMS message.

Path Parameters

ParameterTypeDescription
idrequiredstring (UUID)SMS message ID

Example Response

Returns an array of events. Each event includes a data object with the raw provider payload (e.g. the carrier status description for a failed message), or null when no provider payload is available.

[
  {
    "id": "e1f2a3b4-5678-90ab-cdef-1234567890ab",
    "type": "sent",
    "createdAt": "2026-04-12T10:30:01Z",
    "data": null
  },
  {
    "id": "f2a3b4c5-6789-01bc-def0-2345678901bc",
    "type": "delivered",
    "createdAt": "2026-04-12T10:30:05Z",
    "data": {
      "messageStatus": "DELIVERED",
      "messageStatusDescription": "Message has been accepted by phone"
    }
  }
]

Message Segments

Long SMS messages are split into segments. The number of segments depends on the character encoding:

EncodingChars per segmentWhen used
GSM-7160Standard ASCII characters only
UCS-270Messages containing Unicode characters (emoji, accented chars, etc.)

SMS Event Types

SMS messages generate events throughout their delivery lifecycle. Delivery-status events flow in automatically from the AWS delivery-event pipeline — no polling or configuration required. As receipts arrive, the message's status progresses acceptedsent delivered / failed / undelivered, and the carrier-reported price is recorded against the message. Each event carries a data payload with the carrier status description.

  • acceptedMessage accepted by the API
  • queuedMessage queued for delivery with the carrier
  • sentMessage sent to the carrier network
  • deliveredMessage delivered to the recipient's device
  • failedMessage failed to send (carrier rejection or network error)
  • undeliveredMessage was sent but could not be delivered to the recipient

SMS Rate Limits

SMS sending is subject to daily message limits and spend caps per team:

PlanDaily limitBatch sizeDaily spend cap
Free0 (not included)
Pro10,000100$100.00
Scale100,000100$1,000.00
Enterprise500,000100$10,000.00

When either limit is exceeded, the API returns a 429 response. Batch sends are pre-checked: the projected cost of the whole batch is charged against the daily spend cap before anything is sent, so a single batch cannot overshoot the cap. Per-team overrides can be configured by contacting support. Use GET /v1/rate-limits to inspect your current SMS usage and spend (see Rate Limits).

Pricing zones

Each supported country is assigned to one of three pricing zones. The zone determines the customer-facing retail rate per send. The destination country is resolved from the recipient's E.164 prefix when the send is accepted.

ZoneRetail (AUD)Countries
Zone 1$0.02United States, Canada
Zone 2$0.08United Kingdom, Australia, New Zealand, Ireland, Netherlands, Spain, Italy, Germany, France, Belgium, Austria, Switzerland, Denmark, Sweden, Norway, Finland, Japan, Singapore
Zone 3$0.25Mexico, South Africa, Malaysia, Philippines, Brazil

Sends to countries outside this list return 422 UNSUPPORTED_DESTINATION_COUNTRY. Each paid plan includes a monthly SMS credit (Pro $2.00, Scale $20.00, Enterprise $80.00) that's debited at the destination zone rate. Once the credit is exhausted, additional sends are billed via Stripe Billing Meters at the same per-zone rate.