Messages

One endpoint to send, two to read.

MethodPathScope
POST/v1/messagesmessages:send
GET/v1/messagesmessages:read
GET/v1/messages/{id}messages:read

Sending

FieldRequiredNotes
toyesE.164, e.g. +447400123456. A UK number given as 07… is assumed to be UK; anything else needs its country code.
bodyyesUp to 1600 characters. See segments for what that costs.
fromnoA sender you have. Omit it and we pick one that is valid for the destination — which is usually the right answer, because the rules differ by country.
classnootp, transactional (default) or marketing. This is not cosmetic — see below.
metadatanoYour own key/values, returned on the message and on every webhook about it. Put your order id here rather than trying to parse it back out of the body.

Class changes behaviour, not just reporting

ClassConsentQuiet hoursRoute
otpNot requiredIgnoredPremium — deliverability matters more than price
transactionalNot requiredIgnoredPremium
marketingRequiredRespectedCost-optimised

Marketing is the only class that needs recorded prior opt-in, because under PECR it is the only one that does. Sending marketing to a contact with no recorded opt-in is refused withOPTED_OUT, and quiet hours will hold it rather than deliver it at 6am.

Do not label marketing as transactional to get past the consent check. It will send, and it will be unlawful, and the complaint arrives at your door rather than ours. The class is a declaration about the message, not a routing hint.

The response

{
  "id": "01j8xk2p7n4qc3m9v0wz5bt6ay",
  "object": "message",
  "status": "queued",
  "direction": "outbound",
  "to": "+447400123456",
  "to_country": "GB",
  "from": "TEXTMAST",
  "body": "Your code is 123456",
  "encoding": "gsm7",
  "segments": 1,
  "class": "otp",
  "price": { "amount": "0.0350", "currency": "GBP" },
  "failure": null,
  "scheduled_at": null,
  "submitted_at": null,
  "delivered_at": null,
  "metadata": {},
  "created_at": "2026-09-17T09:14:22+00:00"
}

Fields worth a second look

Listing

GET /v1/messages?limit=50&status=failed

Cursor paginated — see pagination. Not offsets: the underlying table is being written to constantly, and page=2 of a moving list silently skips rows.