Skip to main content
Check environment and feature availability before following this guide.
schedule.send_at is the earliest a scheduled message can go. delivery_conditions can hold it past that moment until the conditions are met, up to a deadline you choose.
Once the message is due, the evaluator checks the conditions every minute. While they hold it back the status stays scheduled, and each check is recorded in GET /v1/queue-executions as a constraint_check with result: "deferred" and the reasons in error_message, for example outside time window 09:00–17:00 (Europe/Brussels).

Time windows

The zone is the condition’s own timezone, otherwise the message’s schedule.timezone, otherwise UTC. A window may cross midnight: "start": "22:00", "end": "06:00" covers the evening of a listed day and the small hours that follow it.

The deadline

max_delay_at in the response is send_at + max_delay_minutes, the moment the wait ends. It is null when there is no max_delay_minutes. The evaluator recomputes it when you reschedule, and each occurrence of a recurring message gets its own. When max_delay_at passes and the conditions still do not hold:
  • on_miss: "send", or no on_miss at all: the message is sent at that tick regardless.
  • on_miss: "expire": the message becomes expired, a terminal status, with error_message starting expired: followed by the reasons, and nothing is sent. The execution log records a constraint_check with result: "failed".
A message that is expired cannot be rescheduled or sent; schedule a new one.

Changing the conditions

PATCH /v1/scheduled-messages/{id} with a new delivery_conditions replaces the whole object; with "delivery_conditions": null it removes the hold. POST …/send-now removes the conditions and the deadline as well as making the message due, so it goes on the next tick.

Unsupported conditions

Presence-based delivery is not enabled. Create and PATCH requests with a presence condition return 422; remove it and use a supported time window. Schedule a message · Execution logs