Questions · Updated 2026-09-28

How do I schedule an email to send later?

Schedule an email to send later by passing scheduled_at on POST /emails, as an ISO 8601 time or a phrase such as "in 1 hour" or "tomorrow 9am". The message waits with status scheduled, and PATCH /emails/{id} moves it.

Schedule an email to send later by adding scheduled_at to the body of POST /emails. The field description says it takes "ISO 8601 or a short phrase ("in 1 hour", "tomorrow 9am")". The message is stored with status scheduled, the event recorded is email.scheduled, and GET /emails/{id} returns the resolved time in scheduled_at. scheduledAt is read as the same field; its description is "Resend Node SDK name for scheduled_at."

What the field accepts

Four forms parse. An ISO 8601 timestamp. A relative phrase, in followed by a count and a unit, where the units are seconds, minutes, hours, days or weeks; any other unit is refused with the sentence "Use seconds, minutes, hours, days or weeks." A day with a clock time: today, tomorrow or a weekday name, then a time such as 17:30 or 9am, with at allowed between them. And the word now. Relative phrases have no timezone. A wall-clock phrase resolves in the server's timezone, which is UTC in production, so write tomorrow 9am as the UTC hour you mean, or send an ISO time with its own offset.

A phrase that does not parse is refused with invalid_parameter, and the message is the parser's own sentence: "Could not read "…" as a time. Use an ISO time ("2026-09-05T09:00:00Z"), "in 30 minutes", "tomorrow 9am", "today 17:30", or a weekday with a time ("friday 14:00")." A time too far out is refused with "scheduled_at is more than 30 days ahead." A time already in the past is not refused on POST /emails: the message is queued at once, because a clock a few seconds off between your process and the API must not cost a send.

curl -sS -X POST https://api.agentisend.com/emails \
  -H "Authorization: Bearer $AGENTISEND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"from":"billing@yourdomain.com","to":"customer@example.com"}'
201
{
  "id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
  "simulated": true,
  "warnings": []
}
Response

Checking and moving it

GET /emails/{id}/explain on a scheduled message returns verdict scheduled, the sentence "Waiting for its scheduled time." and one action whose call is POST /emails/{id}/cancel. To move it instead, call PATCH /emails/{id} with a new scheduled_at. That operation's summary opens "Move a scheduled email to a new time" and calls it "the same act as" the reschedule route, "under the verb a Resend integration already uses." POST /emails/{id}/reschedule is that route, and its summary adds that it "Works from scheduled AND canceled — a cancel is never terminal." Both take the grammar above and both return id, status and the new scheduled_at.

A reschedule differs from a first send in one way: a time that has already passed is refused rather than sent, with a sentence that ends "or "now" to send it straight away". Pass now when that is what you mean. A message whose delivery is settled — sent, delivered, bounced, failed — cannot be moved, and the refusal says "its delivery is already settled."

curl -sS -X PATCH https://api.agentisend.com/emails/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42 \
  -H "Authorization: Bearer $AGENTISEND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
200
{
  "id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
  "scheduled_at": "2026-09-04T09:14:00Z",
  "status": "string"
}
Response

Every scheduled message is a normal message in GET /emails, so status=scheduled lists what is waiting, and POST /emails/bulk-cancel with before clears everything due before a time.

Next