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_at | The simulator returns |
|---|---|
| at or before the clock | 422 validation_error, The `scheduled_at` field must be a future date |
| more than 30 days ahead of the clock | 422 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.