Schedule a message

POST/api/v1/messages/scheduled
Requires anorganization API keywith the scopemessages:send

Accepts a message now and submits it at send_at. The body is exactly POST /messages, plus send_at.

It is a separate endpoint, and that is deliberate. Other providers put the delivery time on the send call, and the cost of that is a send endpoint whose response is a message on one call and a schedule on the next — two object types from one operation. Here you get a message from /messages and a scheduled_message from /messages/scheduled, always. POST /messages refuses a send_at rather than ignoring it: a scheduling parameter that is silently dropped delivers the message immediately, to everyone, at the wrong time, with nothing said.

Everything that can be checked now is checked now — the body, the recipients, the attachments, the limits, and that from is a verified Sender of yours. A scheduled send that fails validation three days later is a message that silently never went, and you find out from the recipient.

Suppression and rate limits are checked when it leaves, not when you schedule it. They are questions about the world at the moment of delivery: somebody who unsubscribes on Tuesday must not receive a message you scheduled on Monday. When that happens the row ends failed with failure_reason set, so it says what happened rather than disappearing.

send_at must be at least 60 seconds ahead — the sweep runs every minute and we will not promise a precision we do not have — and at most 30 days, because the whole message, attachments included, is stored here until it goes.

Parameters

NameInTypeDescription
Idempotency-KeyheaderstringMakes this request safe to retry. Use <app>-<ULID>, such as crm-01JBT8XQ2M9WYC3K4F6R7S8T9V; any other shape is a 422. Send the same key on every retry: a repeat gets the first answer with Idempotency-Replayed: true, a key reused for a different request is a 422, and one still running is a 409 with Retry-After. Keys last 24 hours, per organization.

Request body

  • send_atstringrequired

    When to send, in ISO 8601 with a time zone. At least 60 seconds and at most 30 days ahead. A time in the past is refused.

Request

POST/api/v1/messages/scheduled
curl -X POST 'https://app.mailyte.com/api/v1/messages/scheduled' \
  -H 'Authorization: Bearer mk_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": [
      "ada@example.com"
    ],
    "from": "hello@yourdomain.com",
    "subject": "Your weekly summary",
    "text": "Here is what happened this week.",
    "send_at": "2026-09-21T09:00:00Z"
  }'
The key names its own organization, so no X-Organization-ID header is needed.

Response

201 with the `scheduled_message`. Use its `id` to list, inspect or cancel it.

  • dataobject

    A message accepted now and submitted at send_at. It is not a message: a message has left, and this has not. Once it goes, message_id names the message it became.

    • objectstringscheduled_message
    • idstring

      Address this with GET and DELETE /messages/scheduled/{id}. It identifies the SCHEDULE, not the message — see message_id.

    • statusstringscheduled | sending | sent | cancelled | failed

      scheduled (waiting), sending (going out now; it can no longer be cancelled), sent, cancelled or failed.

    • modestringlive | test

      test when a test API key scheduled this message: when it is due it is stored in your Sandbox, never delivered. Otherwise live.

    • send_atstring

      When it will be sent, to the minute.

    • fromstring

      The sender, as scheduled.

    • toarray<string>

      The recipients, as scheduled. cc and bcc are held but not published back — a Bcc that reappears in a readable response is a Bcc that leaked.

    • subjectstring

      null MEANS NONE WAS SET — a message may legitimately have no subject, and a template supplies its own at render time.

    • message_idstring

      The Message-ID it was sent with. Null until it is sent, and for a cancelled or failed message.

    • sent_atstring

      When it was submitted. null MEANS IT HAS NOT BEEN.

    • cancelled_atstring

      When it was cancelled. null MEANS IT WAS NOT.

    • failure_reasonstring

      Why it could not be sent. Set only when status is failed. Suppression and rate limits are checked when the message is sent, not when it was scheduled.

    • created_atstring

      When it was accepted.

Returned inside the standard envelope.

Errors

StatusWhen
401The API key is missing, unknown, revoked or expired. All four answer identically, on purpose: distinguishing them would confirm which keys exist.
403The key is valid but may not do this: it lacks the required scope, its IP allowlist does not include you, or this endpoint does not accept API keys.
404No such resource in this organization.
422The request was understood but the values were not acceptable.
429Too many requests, or the organization has spent its sending allowance. `Retry-After` says how long to wait.

Every status, with what causes it and what to do, is on the error reference.