Skip to main content

Send later — schedule an Inbox reply end to end

Send later lets an agent write a reply to a conversation now and have it dispatched at a future timestamp, without building a macro or a campaign. The reply parks on the scheduled-outbound pipeline until the dispatcher picks it up, and you can list what’s pending or pull it back at any point before it fires. This guide walks the feature end to end: when it applies, the three endpoints behind it, the validation rules that decide what gets accepted, and a worked example you can paste.
1

Know what send later does

Compose now, dispatch later, parked on the scheduled outbound pipeline.
2

Schedule, list, cancel — the three endpoints

POST to schedule, GET to list pending, DELETE to cancel before dispatch.
3

Know when it isn't a macro

Standalone single-shot vs the macro’s scheduleFollowUp step.
4

Front-load the validation rules

Future timestamp, inside the 35-day horizon, body within limits.
5

Handle the no-destination failure

A conversation with no contact phone or email rejects with 422.
6

Understand the channel behavior

Defaults to the conversation’s channel; routed by your standard sender setup.
7

Run the worked example (SMS and email)

Schedule a reply, list pending, watch it promote, cancel before dispatch.
8

Know when NOT to use send later

Cadence belongs in macros; bulk and drip belong in campaigns.

1. What send later does

Send later is an Inbox composer affordance, not a separate sending system. The flow:
  1. An agent composes the reply exactly as they would for an immediate send.
  2. Instead of dispatching now, the reply is stored with a future scheduled_at timestamp and a scheduled status.
  3. The scheduled-message dispatcher fires roughly once a minute and promotes any reply whose scheduled_at has passed, sending it through the same channel path an immediate reply would take.
  4. Until dispatch, the reply is fully visible and fully cancellable: list the pending replies on the conversation, delete one to pull it back.
Because the reply rides the standard scheduled-outbound pipeline, anything your workspace can send immediately — SMS, WhatsApp, email, RCS, Viber, and the rest of the Inbox channel set — can be sent later, with an optional media attachment for MMS and WhatsApp. It’s your content, your timestamp. Send later is a tenant-owned control: nothing outside your workspace reads or alters a reply you scheduled, and a scheduled reply only ever goes to a contact already on the conversation. Whether the reply clears consent and quiet-hours gates at dispatch is decided by the same tenant-configured checks an immediate send passes through. Skip conditions for this guide. If you want a multi-step sequence (send, then close, then tag), use a macro with a scheduleFollowUp step — the section below tells you which. If you want the same message to a list of contacts, use a campaign. Send later is the single-shot case: one reply, one customer, one timestamp.

2. The three endpoints

Everything the composer’s Send later affordance does routes through three endpoints under the Inbox API. You can drive the same behavior from an integration.

Schedule a reply

POST /api/v1/inbox/conversations/{convId}/scheduled-replies
The request body is the only input: A successful schedule returns 201 with the parked row:

List pending scheduled replies

GET /api/v1/inbox/conversations/{convId}/scheduled-replies
The list contains only replies that are still scheduled — dispatched and cancelled rows drop out. Entries are ordered by scheduled_at, earliest first, and the server caps the response at 50 pending replies per conversation, with the cap echoed back as limit so you know when it bit. Each entry carries the recipient, body, media URL, and both author and macro attribution (scheduled_by_user_id, source_macro_id). Point your “Pending sends” drawer at this endpoint; it polls happily on a 30-second cadence.

Cancel a pending scheduled reply

DELETE /api/v1/inbox/scheduled-replies/{id}
Cancel flips the reply to cancelled, but only while it’s still parked. Once the dispatcher has promoted the row (queued, sending, sent, failed, or already cancelled), the endpoint returns 404 — by design, there’s no difference between “no such reply” and “too late to cancel” in the response you get, so a caller can’t probe for the moment a reply dispatches. There is no credit to refund at cancel time: balance is debited on the send path after the dispatcher promotes the row, so a still-parked reply has no charge posted against it.

3. Send later vs a macro’s scheduleFollowUp step

A macro can also schedule a follow-up — its scheduleFollowUp step rides the same scheduled-outbound pipeline. The difference is scope:
  • Send later is a standalone, single-shot schedule. One reply, one timestamp, no macro definition to author. This is the composer’s “Send at 9 AM tomorrow” case.
  • A macro’s scheduleFollowUp step is one step inside a multi-step run — it can sit alongside steps that change the conversation status, add tags, or create a task, and the whole chain runs as one run-log entry.
Reach for the endpoint on this page when the only thing you need to delay is one reply. Reach for a macro when the delayed reply is part of a repeatable sequence — say, “close the conversation, wait two days, send the satisfaction follow-up.” Both approaches schedule onto the same pipeline, so a macro-authored follow-up and a composer-authored reply park, dispatch, and cancel identically.

4. Validation rules, front-loaded

The schedule endpoint validates before anything is stored, so an invalid timestamp comes back as a 422 the composer can render inline, not a 500 after persistence. Three checks:
  1. scheduled_at must be an ISO-8601 timestamp in the future. A past or malformed value rejects with code INVALID_SCHEDULED_AT and the message scheduled_at must be in the future.
  2. scheduled_at must be within the 35-day platform horizon. Anything farther out rejects with the same code and a must be within 35 days message. The composer-side picker enforces the same window, so the API and the dashboard agree on what counts as far enough.
  3. The body must be 1–8,000 characters. An empty or over-long body rejects with INVALID_BODY and the field issues attached.
The dashboard picker blocks its Schedule send button on the same two timestamp rules, so most invalid input never reaches the API. When you integrate directly, treat a 422 as a fix-the-input signal rather than a retry signal.

5. The no-destination failure

Scheduling needs somewhere to send. The endpoint resolves the destination from the conversation: for email it uses the conversation’s email address; for every other channel it prefers the phone number and falls back to the email address. If the conversation has neither a contact phone nor a contact email, the request rejects with 422 and code NO_DESTINATION — so a missing destination never parks a reply that can never deliver. If you hit it in a workflow, enrich the contact record on the conversation first, then re-schedule.

6. Channel behavior

By default the reply goes out on the conversation’s own channel — a WhatsApp conversation schedules a WhatsApp reply, an email conversation schedules an email reply. You can override per-request with the channel field (for example, schedule an SMS on an email conversation), and the list is the full Inbox channel set, not just SMS and email. Either way, the send rides your workspace’s standard sender stack: the numbers, senders, and providers you configured under Inbox settings pick up the reply at dispatch exactly as if the agent had pressed send immediately. Nothing about scheduling changes carrier or sender selection — it only changes when the send leaves.

7. Worked example: schedule, list, watch, cancel

Two snippets — SMS and email.

(a) SMS — schedule a morning follow-up

Response: 201 with status: "scheduled" and the reply’s id. Keep the id — it’s what cancel and the list entry reference.

(b) Email — schedule a subject/cc-recipient summary for after hours

Response: 201, parked until 18:30 UTC.

List what’s still pending on the conversation

While the list contains the row, the draft still exists as status: scheduled. Poll it (the dashboard does this on a 30-second cadence).

Watch it promote

The dispatcher fires about once a minute. Once the scheduled time passes, the row dispatches, and it disappears from the pending list. In the dashboard the composer drawer updates on its next poll; over the API the next GET simply returns without that entry.

Cancel before dispatch

Response: 200 with status: "cancelled". If you instead get 404, the row already dispatched (or was already cancelled) — there is no window where a cancel races a dispatch silently; the 404 tells you the moment has passed.

8. When to NOT use send later

Send later is deliberately narrow. Three cases it is the wrong tool for:
  • A per-conversation cadence. If every conversation needs “follow up 48 hours after close”, encode it once as a macro with a scheduleFollowUp step, or a ticket-automation rule — not as an agent remembering to schedule each reply by hand.
  • Bulk or drip delivery. The same message to many contacts, or a multi-touch sequence over days, belongs in Campaigns, where audiences, throttling, and per-contact consent tracking live. Send later addresses one reply on one conversation.
  • Workflow gating. If the reply should wait for a human decision rather than a clock — a supervisor’s approval — use the reply-approval queue, which gates content. Send later gates only timing.

See also