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
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:
{ "id": "01JZFYK2NAB3C5D7E9F1G3H5J7" }
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_atin 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, so it shows up in GET /v1/emails under status=queued:
{
"id": "01JZFYK2NAB3C5D7E9F1G3H5J7",
"to": ["you@yourcompany.com"],
"from": "orders@mail.acme.com",
"subject": "Your order shipped",
"status": "queued",
"last_event": "queued",
"recipient_domain": "yourcompany.com",
"tags": [],
"created_at": "2026-07-08T14:02:11.000Z",
"scheduled_at": "2026-07-10T09:00:00.000Z"
}
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.
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 scheduled 15 minutes out or less (or sent immediately) is already committed to the delivery queue with a fixed delay; a farther-out send sits on a scheduler and can be moved. Attempting to reschedule a near-term send 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_atmeans "send now"; on reschedule a past or current time is a422 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 returns409 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 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
200again with the canceled email. Retrying is safe. - Too late once sending starts. If the message has left
queued, cancel returns409 conflict:Only a queued email can be canceled.If the delivery worker claims it during your request, the409 conflictmessage isThe 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.
Related
- The
scheduled_atfield onPOST /v1/emails PATCH /v1/emails/{id}andPOST /v1/emails/{id}/cancel- How sending works covers the full message lifecycle