Scheduled texts
Schedule one text to one contact for later. Nothing is charged until it sends. To send later what you would send with Send a message, call POST /sms/schedule with the same text in body or message and a send_at. It takes from as Send a message does, but not from_strategy.
#Schedule a text
Queues one text to one of your contacts for a later time. Nothing is sent or charged now. When the time comes, Ready sends it like any other text and charges your per-segment rate then. Needs the send_sms permission. Test keys can’t call it.
Parameters
messagestringRequired- The text to send. Merge fields are filled in for the contact when it sends.
bodyis the same field, as on Send a message; when both come,bodyis used. send_atISO 8601 stringRequired- When to send it, like
2026-11-20T15:00:00Z. IncludeZor a time zone offset, such as2026-11-20T15:00:00-06:00: a time without one is read as UTC, and a date alone as midnight UTC. It must be more than 30 seconds from now.
More parameters
contact_idinteger | string- The contact to text. Send this or
to. tostring- The contact’s phone number. Matched to your contacts on the last 10 digits. When no contact has that number, Ready adds one and schedules the text, as Send a message does.
conversation_idinteger | string- The conversation to put it in. Kept only when it’s your conversation with this contact.
fromE.164 string- One of your numbers to send it from. Leave it out and Ready picks when it sends.
media_urlstring- One web address that starts with
http://orhttps://, sent as a picture text. Get one from Upload a file. confirm_empty_mergeboolean- Send
trueto schedule it even when a merge field is blank for this contact.
Any other field is refused with unknown_field.
Returns
data.idinteger- The scheduled text’s id. Use it to cancel it.
data.send_atstring- The send time, in UTC.
duplicateboolean- Only present, as
true, when you sent the same text to the same contact for the same time again. Nothing new was queued;data.idis the first one.
When it sends
Ready checks for due texts about once a minute. Every check a normal send runs happens then, not when you schedule:
- A contact who opted out by then doesn’t get it.
- During the contact’s quiet hours it waits for the next allowed time. If that is too far off, it fails instead of going out late.
- If your balance is too low, it isn’t sent and you aren’t charged. It either fails, or waits with status
balance_heldand sends by itself once you top up. - If your registration was suspended after you scheduled it, it fails.
- A text more than 24 hours past its time, for example after an outage, fails instead of going out late.
Each of these leaves the text in List scheduled texts with an error that says why.
A blank merge field answers 200 with "success": false, error_code: "empty_merge_field" and empty_fields. Nothing was scheduled. Send again with "confirm_empty_merge": true to schedule it with the blank.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | message or send_at is missing or blank, or a field has the wrong type. |
| 400 | unknown_field | A field this endpoint doesn’t take. |
| 400 | invalid_from | from isn’t one of your numbers. Nothing was scheduled. |
| 400 | invalid_media_url | media_url isn’t one http:// or https:// address. |
| 400 | no code | send_at isn’t a date, or it’s less than 30 seconds from now. |
| 400 | NO_APPROVED_CAMPAIGN | Your registration isn’t approved. Also NUMBER_SETUP_UNCONFIRMED, when Ready can’t confirm the number’s setup. |
| 403 | permission_denied | Your role doesn’t have send_sms. |
| 403 | test_key_not_allowed | You used a test key. Use a live key. |
| 403 | number_not_assigned | A team member picked a from number that isn’t assigned to them. |
| 404 | no code | The contact_id or conversation_id isn’t yours. |
| 409 | duplicate_schedule | The same text is being scheduled right now. Check your scheduled texts before you try again. |
| 500 | contact_lookup_failed | Ready couldn’t check the contact. Nothing was scheduled; retry. |
| 503 | from_check_unavailable | Ready couldn’t check the from number. Nothing was scheduled; retry. |
| 503 | merge_check_failed | Ready couldn’t check the merge fields. Nothing was scheduled; retry. |
| 503 | schedule_dedupe_unavailable | Ready couldn’t tell if this text was already scheduled, so it held this one. Check your scheduled texts before you retry. |
#List scheduled texts
Your account’s scheduled texts that haven’t gone out yet, soonest first, plus the ones that failed in the last 3 days with the reason. Texts that were sent or cancelled aren’t listed. Returns up to 200.
Parameters
contact_idinteger, query- Only this contact’s scheduled texts.
Returns
data[].idinteger- The scheduled text’s id.
data[].contact_idinteger- The contact it goes to.
data[].to_numberstring- The number it goes to.
data[].conversation_idinteger | null- The conversation it goes in, when one was given.
data[].bodystring- The text as you wrote it, merge fields not filled in.
data[].send_atstring- When it sends. Quiet hours or a balance hold can move it later.
data[].statusenumpending(waiting),sending(going out now),balance_held(waiting for you to top up) orfailed.data[].errorstring | null- Why it failed or is waiting, in a sentence you can show. The wording can change, so don’t match on it.
data[].cancelablebooleantruewhen Cancel a scheduled text would cancel it (pendingorbalance_held).truncatedbooleantruewhen more scheduled texts wait past these 200. Filter bycontact_idto see one contact’s texts in full.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | contact_id isn’t a whole number. |
| 500 | scheduled_list_failed | The list couldn’t be loaded. Retry before you schedule anything new, so you don’t schedule a text twice. |
#Cancel a scheduled text
Cancels a scheduled text that hasn’t gone out. Only a pending or balance_held text can be cancelled. Nothing was charged, so there’s nothing to refund. Needs the send_sms permission. Test keys can’t call it.
Parameters
idinteger, pathRequired- The scheduled text’s id.
Returns
{ "success": true }. Canceling again answers 409 with status: "canceled".
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | id isn’t a whole number. |
| 403 | permission_denied | Your role doesn’t have send_sms. |
| 403 | test_key_not_allowed | You used a test key. Use a live key. |
| 404 | not_cancelable | No scheduled text with that id on your account. status is null. |
| 409 | not_cancelable | It can’t be cancelled anymore. status says why: sending, sent, canceled or failed. |
| 500 | cancel_failed | It wasn’t cancelled and is still scheduled. Retry. |