Skip to main content
Check environment and feature availability before following this guide.
A scheduled message is a template send that happens later. You give the API a template, the data for its placeholders, one destination and a time; the evaluator sends it when the time comes. Creating and changing scheduled messages needs the scheduled:write scope, reading needs scheduled:read. Both are available to members and up. Credits are spent when the send happens, not when you schedule it. A scheduled send costs 2 credits.

Schedule one

The response is 201 with the scheduled message:
send_at comes back normalised to UTC. template_version_id records the version that was live when you scheduled, and it is the version that sends: activating a newer version later does not change a message already scheduled, including recurring ones. queue_policy_id is only set on messages created through the older policy-driven path and is null for anything you create here.

Following the live version instead

Pass "follow_live": true when you schedule, and the message is not pinned: it sends whichever version of the template is live at send time, and each recurrence does the same. template_version_id comes back null. To change an existing message, PATCH /v1/scheduled-messages/{id} with "follow_live": true to unpin it, or "follow_live": false to pin it to the version live now. Pinning a template that has no live version answers 409 with template_not_activated. Leaving follow_live out never changes the pin.

Retrying safely

Send an Idempotency-Key header with any unique string. Repeating the request with the same key and body within 24 hours replays the original response, marked Idempotency-Replayed: true, instead of scheduling a second message. The same key with a different body answers 409.

What can go wrong

Lifecycle

The last four are terminal. Delivery is checked on a roughly minute-based cadence; load, conditions, and service availability can delay it. The due time is not a guaranteed delivery deadline.

Manage it

All three changes are only possible while the status is scheduled. Afterwards the API answers 409 with code: "invalid_state" and the current status in details.

Reschedule

PATCH takes any of data, destination, schedule, recurrence and delivery_conditions; send only what changes. recurrence: null removes the recurrence and delivery_conditions: null removes the conditions. The deadline max_delay_at is recomputed from the new send_at and conditions.

Cancel

DELETE is a soft cancel: the row stays, with status: "cancelled", and the response is the message. Cancelling a recurring message stops the later occurrences too.

Send now

send-now makes the message due immediately and removes its delivery conditions; the evaluator attempts it on a subsequent tick. The response is 202 with the message, still scheduled until that tick.

See what happened

Every attempt the evaluator made appears in GET /v1/queue-executions, filterable by scheduled_message_id. A scheduled_send with result: "success" is the delivery; a constraint_check with result: "deferred" is a tick where the conditions held the message back, with the reasons in error_message.

In the web app

The Scheduled page at /scheduled lists your organization’s scheduled messages with a status filter, shows each one’s destination, recurrence and conditions, and offers reschedule (a new time and zone), cancel and send now for anything still scheduled. It also shows the create request above as a copy-paste curl. Creating a scheduled message is an API call; the page manages what the API created. Manage in the app · Execution logs