Mailfully logo

Scheduled sends

Schedule an email up to 90 days ahead with scheduled_at, then reschedule or cancel it before it dispatches.

Any send can carry a scheduled_at timestamp. The API accepts it immediately with a 202, holds the message, and dispatches it at the scheduled time.

Schedule a send

# any time in the next 90 days; a past time sends at once
curl -X POST https://api.mailfully.com/v1/emails \
  -H "Authorization: Bearer mf_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "orders@mail.acme.com",
    "to": "you@yourcompany.com",
    "subject": "Your order shipped",
    "text": "Order 4212 shipped.",
    "scheduled_at": "2026-07-10T09:00:00Z"
  }'

The response is the standard acceptance, echoing the scheduled_at that was stored:

{
  "object": "email",
  "id": "01JZFYK2NAB3C5D7E9F1G3H5J7",
  "status": "queued",
  "type": "transactional",
  "to": ["you@yourcompany.com"],
  "cc": [],
  "bcc": [],
  "from": "orders@mail.acme.com",
  "subject": "Your order shipped",
  "scheduled_at": "2026-07-10T09:00:00.000Z",
  "created_at": "2026-09-11T14:15:00.000Z"
}

Rules for scheduled_at:

  • Format: an ISO 8601 datetime string, e.g. 2026-07-10T09:00:00Z.
  • Maximum horizon: 90 days ahead. Beyond that, the send is rejected with 422 validation_error (param: "scheduled_at"): scheduled_at cannot be more than 90 days in the future.
  • No minimum: a scheduled_at in the past or at the current instant is accepted and dispatched immediately. It is never an error.

Templates render at accept, not at dispatch: a scheduled send referencing a template carries the content rendered when the 202 was returned, and later template edits don't touch it. See Templates.

How scheduled sends appear

No separate scheduled status exists. A scheduled send is a queued message with a future scheduled_at. It shows up in GET /v1/emails under status=queued, with the same scheduled_at as the acceptance above.

The detail response from GET /v1/emails/{id} has no status field; read last_event instead, which stays queued until dispatch, alongside the scheduled_at timestamp.

Reschedule

PATCH /v1/emails/{id} moves a scheduled send to a new time.

# any time in the next 90 days
curl -X PATCH https://api.mailfully.com/v1/emails/01JZFYK2NAB3C5D7E9F1G3H5J7 \
  -H "Authorization: Bearer mf_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "scheduled_at": "2026-07-12T09:00:00Z" }'

A 200 returns the full email detail with the new scheduled_at.

Constraints:

  • Only sends scheduled more than 15 minutes ahead can be moved. A send due in 15 minutes or less, and a send that went out at once, cannot be moved. Trying to reschedule one returns:
{
  "error": {
    "type": "validation_error",
    "message": "This send cannot be rescheduled; only sends scheduled more than 15 minutes ahead can be moved.",
    "param": "scheduled_at"
  }
}
  • The new time must be strictly in the future and at most 90 days out. This is stricter than the original send, where a past scheduled_at means "send now"; on reschedule a past or current time is a 422 validation_error (param: "scheduled_at").
  • Only queued sends move. A canceled send returns 409 conflict (A canceled email cannot be rescheduled.); any other non-queued status returns 409 conflict (Only a queued email can be rescheduled.).

Cancel

POST /v1/emails/{id}/cancel stops a send that hasn't dispatched yet. It works for any queued or held message, scheduled or not; unlike reschedule, it has no 15-minute restriction.

curl -X POST https://api.mailfully.com/v1/emails/01JZFYK2NAB3C5D7E9F1G3H5J7/cancel \
  -H "Authorization: Bearer mf_live_xxxxxxxxxxxx"

A 200 returns the email detail, now canceled:

{
  "object": "email",
  "id": "01JZFYK2NAB3C5D7E9F1G3H5J7",
  "to": ["you@yourcompany.com"],
  "from": "orders@mail.acme.com",
  "created_at": "2026-07-08T14:02:11.000Z",
  "subject": "Your order shipped",
  "html": null,
  "text": "Order 4212 shipped.",
  "cc": [],
  "bcc": [],
  "reply_to": [],
  "last_event": "canceled",
  "scheduled_at": "2026-07-10T09:00:00.000Z",
  "tags": []
}

Cancel semantics:

  • Idempotent. Canceling an already-canceled send returns 200 again with the canceled email. Retrying is safe.
  • Too late once sending starts. Once a message is past queued or held, cancel returns 409 conflict: Only a queued email can be canceled. If sending starts during your request, the 409 conflict message is The email is already being sent and can no longer be canceled.
  • Canceled sends are never billed. A cancel that lands before dispatch means no delivery attempt and no metering.