Sending

Scheduling

Schedule an email up to 30 days ahead with scheduled_at, then reschedule or cancel it while it is still scheduled.

Add scheduled_at to POST /emails to send an email later. A scheduled email is accepted now, returns an ID immediately, and is released at the time you chose.

Set a send time

scheduled_at accepts two forms.

ISO 8601. Always include a time zone, for example Z for UTC or an offset such as +02:00.

JSON
{ "scheduled_at": "2026-11-20T09:00:00Z" }

Natural language in the form in N unit, where the unit is second, minute, min, hour, hr, day or week, singular or plural.

JSON
{ "scheduled_at": "in 1 hour" }

Other phrases, such as tomorrow, are not understood and return a validation_error.

Shell
curl -X POST https://api.sooner.dev/emails \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@mail.yourcompany.com>",
    "to": ["ada@example.com"],
    "subject": "Your trial ends tomorrow",
    "html": "<p>Your trial ends tomorrow.</p>",
    "scheduled_at": "in 2 days"
  }'

Rules

  • The time must be at most 30 days from now. Further out returns 422 validation_error.
  • A time that is in the past, or within about one second from now, sends the email immediately instead of scheduling it.
  • Scheduled emails have status scheduled and last_event scheduled. The scheduled_at field on the email shows the chosen time.
  • POST /emails/batch does not accept scheduled_at.
  • A scheduled email counts toward your quota when you create it. If you cancel it, the count is returned.
  • Checks happen again when the email is released. Recipients added to the suppression list in the meantime are dropped, and if none remain the email fails. If the sending domain is no longer verified, the email fails too.

Reschedule

PATCH /emails/{id} changes the send time of an email that is still scheduled. It needs a full_access key.

Shell
curl -X PATCH https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0 \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "scheduled_at": "2026-11-21T09:00:00Z" }'
JSON
{ "object": "email", "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }

The same 30-day limit applies, counted from the moment of the update.

Cancel

POST /emails/{id}/cancel cancels a scheduled email. It needs a full_access key. The status becomes canceled, the email is never sent, and its quota count is returned.

Shell
curl -X POST https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0/cancel \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{ "object": "email", "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }

Only emails with status scheduled can be rescheduled or canceled. For anything else, the API returns 422 validation_error: "Only scheduled emails can be updated." or "Only scheduled emails can be canceled."

With the Node SDK

TypeScript
import { Resend } from "resend";

const resend = new Resend(process.env.SOONER_API_KEY, { baseUrl: "https://api.sooner.dev" });

const { data } = await resend.emails.send({
  from: "Acme <hello@mail.yourcompany.com>",
  to: ["ada@example.com"],
  subject: "Your trial ends tomorrow",
  html: "<p>Your trial ends tomorrow.</p>",
  scheduledAt: "in 2 days",
});

await resend.emails.update({ id: data!.id, scheduledAt: "2026-11-21T09:00:00Z" });
await resend.emails.cancel(data!.id);

Scheduled broadcasts work the same way. See Broadcasts.