textly.
Documentation menu

API reference

The core Textly endpoints for authentication, messaging, campaigns, and workspace management.

Base URL

The Textly API will be available at https://api.textly.co.ke. All examples below show paths relative to that origin.

Authentication

Protected requests use Authorization: Bearer <token>. Account setup routes use a customer session. Sending and read endpoints accept either a customer session or an active API key for that organization.

MethodEndpointPurpose
POST/auth/registerCreate an organization and owner session
POST/auth/loginCreate a seven-day session
GET/auth/meInspect the current identity
POST/auth/logoutRevoke a session
GET / POST/api-keysList or create API keys
POST/api-keys/:id/revokeRevoke an API key

Send messages

POST /messages

Provide an approved senderId, a messages array, and an Idempotency-Key header. Optional campaignName labels the batch. Optional scheduledAt is an ISO 8601 timestamp with timezone, up to one year ahead.

{
  "senderId": "approved-sender-uuid",
  "campaignName": "Customer update",
  "messages": [
    { "recipient": "254712345678", "text": "Hello Amina" },
    { "recipient": "0712345679", "text": "Hello Musa" }
  ]
}

Textly normalizes Kenyan mobile numbers to 254XXXXXXXXX, rejects duplicate recipients in one request, checks suppressions, and reserves the full charge in one database transaction. Requests support up to 1000 recipients; each text is limited to ten calculated SMS segments.

To send to a saved group, provide groupId and either text or templateId instead of the messages array. Group sends support up to 10,000 contacts. Opted-out and suppressed contacts are excluded. A name placeholder in the text or template personalizes each recipient's message.

POST /messages/quote

Pass the same send body without an idempotency key to review recipients, rendered message samples, segments, estimated charge, and wallet sufficiency before sending. The final send recalculates the charge and reserves funds.

The same idempotency key with the same payload returns the original response. Reusing the key for changed content returns 409 Conflict.

GET /messages

Returns your messages, newest first. Use limit (1–500), offset, status, and recipient query parameters to page and filter delivery history.

GET /messages/:id

Returns one message, including its current status, segment count, charge, and timestamps.

Message states

StatusMeaning
queuedAccepted by Textly and waiting to dispatch
submittingWorker has claimed the message
acceptedOnfon returned a provider message ID
deliveredProvider receipt reports delivery
failedProvider receipt reports delivery failure
rejectedProvider explicitly rejected the submission
unknownSubmission outcome needs reconciliation
cancelledQueued message was cancelled before dispatch

Campaigns

Every send request has a request ID and appears in campaign views. GET /campaigns lists recent batches, GET /campaigns/:id shows the summary and first 100 messages, and GET /campaigns/:id/messages?limit=100&offset=0 pages through the full audience. POST /campaigns/:id/cancel cancels messages still queued. Already submitted messages remain in their current states.

Workspace endpoints

ResourceEndpoints
Wallet and rateGET /wallet, GET /wallet/entries, GET /pricing
ReportsGET /reports/overview?days=30 for status totals, charges, and daily counts
Sender IDsGET /senders, POST /senders with optional business and network details
ContactsGET /contacts, POST /contacts, POST /contacts/import, PUT /contacts/:id, DELETE /contacts/:id
GroupsGET /contact-groups, POST /contact-groups, GET /contact-groups/:id/contacts, PUT /contact-groups/:id, DELETE /contact-groups/:id
TemplatesGET /templates, POST /templates, PUT /templates/:id, DELETE /templates/:id
SuppressionsGET /suppressions, POST /suppressions
TeamGET /team, POST /team/invitations, POST /auth/accept-invite

Amounts use integer KES cents, represented as decimal strings. Segment counts and charges are calculated by Textly; confirm your pricing and billing agreement before sending at scale.

Errors and retries

Expect standard HTTP status codes: 400 for invalid input or insufficient wallet balance, 401 for missing or invalid credentials, 403 for a role restriction, and 409 for an idempotency conflict. Retry a send with the same key and payload after a client-side timeout.