ReadySMS

API Documentation

Complete REST API reference — authentication, SMS, contacts, billing, and the Zapier integration surface used by our published Zapier app.

Don't want to read this whole thing?
The Integration Assistant can do the setup for you — issue API keys, subscribe webhooks, even send a test SMS — based on what you describe.
Open Assistant →

Contents

Overview

ReadySMS exposes a REST API over HTTPS. Every response is JSON with a top-level success boolean; success responses include a data field, error responses include an error string.

Production Base URL

https://api.readysms.io

All endpoints below are relative to this base URL. The API is owned and operated by ReadySMS — the same team behind readysms.io and app.readysms.io. There is no sandbox or staging environment; all documented endpoints are production.

Authentication

Two authentication methods are supported. Use OAuth 2.0 for third-party integrations (Zapier, partner apps). Use API keys for direct server-to-server calls owned by the account holder.

OAuth 2.0 (Authorization Code Flow)

Used by the published Zapier integration. See the Zapier OAuth section for the full flow. Access tokens returned by /zapier/oauth/token are JWTs; pass them in Authorization: Bearer <token> on every subsequent request.

API Keys

Personal API keys are generated from Settings → API Keys in the dashboard. Pass either as a bearer token or an X-API-Key header:

Authorization: Bearer rsms_your_api_key_here
X-API-Key: rsms_your_api_key_here

Keys prefixed rsms_ are general API keys; keys prefixed zap_ are Zapier-scoped keys generated via POST /zapier/api-keys.

SMS

Send messages, query message history, and generate AI-written copy.

POST /sms/send Send an SMS message

Body

tostringRecipient phone number (E.164) required
messagestringMessage content (max 1600 chars) required
consent_attestedbooleanMust be true — certifies the recipient gave prior express written consent under TCPA. required
from_phone_number_idintegerID of the sending phone number

Example

{
  "to": "+14155551234",
  "message": "Hello from ReadySMS!",
  "consent_attested": true,
  "from_phone_number_id": 1
}

Success response

{
  "success": true,
  "data": {
    "message_id": "abc123",
    "segments": 1,
    "status": "pending",
    "credits_remaining": 2499
  }
}

Missing consent (HTTP 403)

{
  "success": false,
  "error_code": "api_consent_attested_required",
  "error": "API sends require `consent_attested: true` in the request body...",
  "docs": "https://app.readysms.io/api-docs#consent"
}
GET /sms/logs Message history

Query

limitintegerMax results (default 50)
offsetintegerPagination offset
statusstringFilter: pending, delivered, failed
directionstringFilter: outbound, inbound
POST /sms/generate AI-generate SMS copy (costs 1 credit)

Body

descriptionstringWhat the message should say required

Example

// Request
{ "description": "appointment reminder for tomorrow 3pm" }

// Response
{
  "success": true,
  "data": [
    "Hi {first_name}! Reminder: your appointment is tomorrow at 3 PM. Reply STOP to opt out.",
    "Hey {first_name}, don't forget your appointment tomorrow at 3:00 PM! Reply STOP to cancel.",
    "{first_name}, your appt is confirmed for tomorrow at 3 PM. See you then! Reply STOP to opt out."
  ]
}

Contacts

Create, read, update, delete contacts.

GET /contacts List contacts with filters

Query

searchstringSearch by name, phone, email
stagestringFilter by pipeline stage
statusstringFilter: active, opted_out
sortstringname_asc, name_desc, score_high, date_added
POST /contacts Create a contact

Body

phonestringPhone number required
first_namestringFirst name
last_namestringLast name
emailstringEmail address
tagsarrayArray of tag strings
pipeline_stagestringnew_lead, contacted, qualified, proposal, won, lost
PUT /contacts/:id Update a contact

Same fields as POST. Only include fields you want to update.

DELETE /contacts/:id Delete a contact

Permanently deletes the contact and all associated messages and conversations.

Billing

Query credit balance and transaction history.

GET /billing/balance Credit balance and usage

Response

{
  "success": true,
  "data": {
    "balance": 2500,
    "rate": 0.0084,
    "account_type": "standard",
    "daily_avg_send": 428,
    "estimated_days_left": 5
  }
}
GET /billing/transactions Transaction history

Query

limitintegerMax results (default 20)

Outbound Webhooks

Events pushed from ReadySMS to subscriber URLs (powers Zapier triggers and custom integrations).

Available Events

new_message new_contact new_conversation new_opt_out message_delivered message_failed pipeline_stage_changed contact_tag_added contact_tag_removed contact_dnd_changed drip_completed keyword_reply

Subscribe via POST /zapier/subscribe (see below). Payloads are JSON; see Event Payloads for the schema of each event type.

Zapier — OAuth 2.0

Authorization-code flow used by the published Zapier integration. Tokens returned are JWTs; use them as bearer tokens on every Zapier endpoint below.

GET /zapier/oauth/authorize Start authorization (renders login page)

Query

client_idstringZapier client ID required
redirect_uristringZapier callback URL required
response_typestringMust be code required
statestringZapier-provided state value (echoed back)

Renders a ReadySMS-branded login/consent page. After successful login, redirects to redirect_uri with ?code=<auth_code>&state=<state>.

POST /zapier/oauth/token Exchange code for access token

Body (application/x-www-form-urlencoded)

grant_typestringauthorization_code or refresh_token required
codestringAuth code from /authorize (for authorization_code)
refresh_tokenstringRefresh token (for refresh_token)
client_idstringZapier client ID required
client_secretstringZapier client secret required
redirect_uristringZapier callback URL (for authorization_code)

Response

{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 2592000,
  "refresh_token": "rtk_..."
}
GET /zapier/oauth/test Validate access token

Same behavior as GET /zapier/auth/test. Returns the authenticated user's profile if the bearer token is valid.

Zapier — Triggers

Subscribe/unsubscribe hooks. Zapier calls subscribe when a Zap turns on and unsubscribe when it turns off; ReadySMS then pushes matching events to the provided target_url.

GET /zapier/auth/test Auth test — returns authenticated user profile

Response

{
  "success": true,
  "user": {
    "id": 101,
    "email": "jane@example.com",
    "first_name": "Jane",
    "last_name": "Doe",
    "company_name": "Example Realty"
  }
}
POST /zapier/subscribe Register a webhook for an event

Body

eventstringOne of the 12 event names — see Outbound Webhooks required
target_urlstringZapier-provided webhook URL required

Response

{ "success": true, "data": { "id": 42 } }
DELETE /zapier/unsubscribe/:id Remove a webhook (called when Zap is turned off)

The :id path parameter is the ID returned from /subscribe.

GET /zapier/sample/:event Sample data for Zapier's trigger preview UI

Returns an array containing one sample payload matching the shape of a live webhook delivery for the given :event. Zapier uses this during Zap setup to let the user map output fields.

Valid values for :event — any event listed in Outbound Webhooks.

Zapier — Actions

Write operations exposed to Zapier users. Each action returns { success, data }.

POST /zapier/actions/send-sms Send an SMS

Body

tostringRecipient phone required
messagestringMessage body required
from_phone_number_idintegerSending number ID (defaults to first active)

Response

{
  "success": true,
  "data": {
    "message_id": "abc123",
    "to": "+14155551234",
    "from": "+15559876543",
    "segments": 1,
    "status": "sent"
  }
}

Atomically debits credits before send; automatically refunds on carrier failure.

POST /zapier/actions/create-contact Create a contact (dedupes by phone)

Body

phonestringPhone number required
first_namestringFirst name
last_namestringLast name
emailstringEmail address
tagsarray|stringTag(s) to apply
pipeline_stagestringDefaults to new_lead
sourcestringFree-form source label (defaults to zapier)
PUT /zapier/actions/update-contact Update a contact (by id or phone)

Body

contact_idintegerContact ID (either this or phone required)
phonestringPhone (either this or contact_id required)
first_namestring
last_namestring
emailstring
pipeline_stagestring
tagsarray|stringReplaces the contact's tag array
POST /zapier/actions/add-tag Add a tag to a contact

Body

tagstringTag to add required
contact_idintegerContact ID (either this or phone)
phonestringPhone (either this or contact_id)
POST /zapier/actions/remove-tag Remove a tag from a contact

Body

tagstringTag to remove required
contact_idintegerContact ID (either this or phone)
phonestringPhone (either this or contact_id)
POST /zapier/actions/add-to-campaign Enroll a contact in a drip sequence

Body

sequence_idintegerDrip sequence ID required
contact_idintegerContact ID (either this or phone)
phonestringPhone (either this or contact_id)

Response

{
  "success": true,
  "data": {
    "enrollment_id": 10,
    "contact_id": 101,
    "sequence_id": 5,
    "sequence_name": "7-Day Follow Up",
    "next_send_at": "2026-04-16T18:00:00.000Z"
  }
}

Zapier — Searches

Look up existing records. Both GET and POST variants are exposed because Zapier search steps can send either.

GET /zapier/search/contact Find a contact by phone or email

Query

phonestringPhone to match (either this or email)
emailstringEmail to match (either this or phone)

Response

{ "success": true, "data": [ { ...contact } ] }
POST /zapier/search/contact Same as GET — accepts JSON body

Same parameters as GET variant, passed in JSON body instead of query string.

Zapier — Dynamic Dropdowns

Populate Zapier's dropdown fields. Each returns an array of { id, label, ... } objects.

GET /zapier/tags List all unique contact tags
[{ "id": "hot-lead", "label": "hot-lead" }, ...]
GET /zapier/contacts List contacts (supports search)

Query

qstringName or phone search

Response

[{ "id": 101, "label": "Jane Doe (+14155551234)", "phone": "+14155551234" }, ...]
GET /zapier/phone-numbers List active sending numbers
[{ "id": 1, "label": "Main line", "phone_number": "+15559876543" }, ...]
GET /zapier/drip-sequences List drip sequences
[{ "id": 5, "label": "7-Day Follow Up", "name": "7-Day Follow Up" }, ...]

Zapier — Event Payloads

Shape of the JSON body POSTed to target_url when each event fires. These are also what GET /zapier/sample/:event returns for Zap setup previews.

PUSH new_message Inbound SMS received
{
  "id": 12345,
  "direction": "inbound",
  "from_number": "+15551234567",
  "to_number": "+15559876543",
  "body": "Hi, I'm interested in the property at 123 Main St.",
  "status": "received",
  "contact_id": 101,
  "contact_first_name": "John",
  "contact_last_name": "Smith",
  "contact_phone": "+15551234567",
  "contact_email": "john@example.com",
  "conversation_id": 50,
  "segments": 1,
  "sent_at": "2026-04-07T14:30:00.000Z"
}
PUSH new_contact Contact created
{
  "id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "tags": ["buyer", "hot-lead"],
  "pipeline_stage": "new_lead",
  "source": "inbound_sms",
  "status": "active",
  "created_at": "2026-04-07T14:30:00.000Z"
}
PUSH new_conversation New conversation started
{
  "conversation_id": 50,
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "first_message": "Hi, I'm interested in the property at 123 Main St.",
  "from_number": "+15551234567",
  "to_number": "+15559876543",
  "started_at": "2026-04-07T14:30:00.000Z"
}
PUSH new_opt_out Contact opted out (STOP keyword)
{
  "contact_id": 101,
  "first_name": "Jane",
  "last_name": "Doe",
  "phone": "+15559876543",
  "email": "jane@example.com",
  "opted_out_at": "2026-04-07T14:30:00.000Z",
  "keyword": "STOP"
}
PUSH message_delivered Outbound SMS confirmed delivered
{
  "message_id": 12345,
  "sendivo_message_id": "msg_abc123",
  "from_number": "+15559876543",
  "to_number": "+15551234567",
  "body": "Great news! We have new listings in your area.",
  "status": "delivered",
  "segments": 1,
  "cost": 0.0082,
  "delivered_at": "2026-04-07T14:30:05.000Z",
  "contact_id": 101,
  "contact_first_name": "John",
  "contact_last_name": "Smith"
}
PUSH message_failed Outbound SMS delivery failed
{
  "message_id": 12346,
  "sendivo_message_id": "msg_def456",
  "from_number": "+15559876543",
  "to_number": "+15550000000",
  "body": "Check out this listing!",
  "status": "failed",
  "status_description": "Undeliverable",
  "segments": 1,
  "cost": 0,
  "delivered_at": null,
  "contact_id": 102,
  "contact_first_name": "Bob",
  "contact_last_name": "Jones"
}
PUSH pipeline_stage_changed Contact moved to a new pipeline stage
{
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "old_stage": "new_lead",
  "new_stage": "qualified",
  "changed_at": "2026-04-07T14:30:00.000Z"
}
PUSH contact_tag_added Tag added to contact
{
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "tag": "hot-lead",
  "all_tags": ["buyer", "hot-lead"],
  "added_at": "2026-04-07T14:30:00.000Z"
}
PUSH contact_tag_removed Tag removed from contact
{
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "tag": "hot-lead",
  "all_tags": ["buyer"],
  "removed_at": "2026-04-07T14:30:00.000Z"
}
PUSH contact_dnd_changed Contact Do-Not-Disturb status changed
{
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "dnd_sms": true,
  "changed_at": "2026-04-07T14:30:00.000Z"
}
PUSH drip_completed Contact finished a drip sequence
{
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "sequence_id": 5,
  "sequence_name": "7-Day Follow Up",
  "completed_at": "2026-04-07T14:30:00.000Z"
}
PUSH keyword_reply Inbound reply matches a watched keyword
{
  "contact_id": 101,
  "first_name": "John",
  "last_name": "Smith",
  "phone": "+15551234567",
  "email": "john@example.com",
  "keyword": "YES",
  "body": "Yes I am interested",
  "from_number": "+15551234567",
  "to_number": "+15559876543",
  "received_at": "2026-04-07T14:30:00.000Z"
}

Need Help?

Email our integration team — we typically reply within one business day.

support@readysms.io · Help Center · Dashboard

ReadySMS · United States