Skip to Content
SimulatorsResendScheduled emails

Simulators

Scheduled emails

A scheduled email is a record that waits for a send time the simulator never reaches by itself: it stays scheduled until you reschedule or cancel it. Reload the scenario so the ids below start at 1, then send one, move it and cancel it:

curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"design_studio"}'
curl -s "$RYSTIC_URL/emails" -H 'Authorization: Bearer re_placeholder' -H 'Content-Type: application/json' -d '{"from":"Acme <onboarding@resend.dev>","to":["delivered@resend.dev"],"subject":"Reminder","html":"<p>Due soon</p>","scheduled_at":"2026-03-05T09:00:00Z"}'
# -> {"id":"28c9b732-cf71-466d-8000-000000000001"}
curl -s "$RYSTIC_URL/emails/28c9b732-cf71-466d-8000-000000000001" -H 'Authorization: Bearer re_placeholder' | jq -c '{last_event, scheduled_at}'
# -> {"last_event":"scheduled","scheduled_at":"2026-03-05 09:00:00.000000+00"}
curl -s -X PATCH "$RYSTIC_URL/emails/28c9b732-cf71-466d-8000-000000000001" -H 'Authorization: Bearer re_placeholder' -H 'Content-Type: application/json' -d '{"scheduled_at":"2026-03-08T09:00:00Z"}'
# -> {"id":"28c9b732-cf71-466d-8000-000000000001","object":"email"}
curl -s "$RYSTIC_URL/emails/28c9b732-cf71-466d-8000-000000000001" -H 'Authorization: Bearer re_placeholder' | jq -c '{last_event, scheduled_at}'
# -> {"last_event":"scheduled","scheduled_at":"2026-03-08 09:00:00.000000+00"}
curl -s -X POST "$RYSTIC_URL/emails/28c9b732-cf71-466d-8000-000000000001/cancel" -H 'Authorization: Bearer re_placeholder'
# -> {"id":"28c9b732-cf71-466d-8000-000000000001","object":"email"}
curl -s "$RYSTIC_URL/emails/28c9b732-cf71-466d-8000-000000000001" -H 'Authorization: Bearer re_placeholder' | jq -c '{last_event}'
# -> {"last_event":"canceled"}
curl -s -X POST "$RYSTIC_URL/emails/28c9b732-cf71-466d-8000-000000000001/cancel" -H 'Authorization: Bearer re_placeholder' | jq -c '{name, message}'
# -> {"name":"validation_error","message":"You can only cancel scheduled emails."}

The clock decides what is in the past

The simulator checks scheduled_at against its own clock, when sending an email and when rescheduling one:

scheduled_atThe simulator returns
at or before the clock422 validation_error, The `scheduled_at` field must be a future date
more than 30 days ahead of the clock422 validation_error, The `scheduled_at` field must be within 30 days

Loading design_studio sets the clock to 2026-03-01T00:00:00Z. A boot under rystic run --clock starts the clock at the pinned instant instead, even with --scenario design_studio, but a later scenario load still sets the clock to 2026-03-01T00:00:00Z. The clock moves one millisecond whenever a request samples it, including some reads.

Pin the clock with POST /_rystic/clock before you choose dates from your wall clock, and choose a date later than the clock and no more than 30 days ahead of it. GET /_rystic/version reports initial_clock, the empty-boot epoch (2026-01-01T00:00:00Z by default), not the scenario’s clock. Never derive a date from it: it can sit before or after the scenario’s clock.

Forms of scheduled_at

The simulator accepts an ISO 8601 timestamp and the natural-language forms measured on Resend, which count hours, days and weeks from the clock (in 2 days). How Resend treats a date without a time or time zone is unconfirmed: it has not been checked against Resend.

Differs from Resend: the simulator does not accept every phrase Resend does.

Nothing fires

The send time never arrives by itself, and moving the clock does not send the email. A scheduled send records an email.scheduled event and no SMTP handoff, since Resend hands the mail to SMTP only at the scheduled time (Webhooks lists the events). A newly scheduled email reads scheduled at once, so an edit made right away never races.

Differs from Resend: a change made right after scheduling an email can race Resend’s readiness. Resend returned 422 to a cancel sent that soon.

Last updated on