Scheduled Sends
Defer a send to a future timestamp via scheduled_at.
Request
POST /api/v1/emails
{
"from": "hi@yourdomain.com",
"to": ["user@example.com"],
"subject": "Reminder",
"text": "Your trial ends tomorrow.",
"scheduled_at": "2026-05-01T12:00:00Z"
}
scheduled_at must be ISO 8601 UTC. If the time is ≤ 1 second from now, the send is treated as immediate.
Behavior
- Message stored with
status: "scheduled"andscheduled_atpopulated. - Provider is not called at request time.
- Vercel Cron hits
/api/cron/send-scheduledevery minute:- Selects up to 100 messages where
status = 'scheduled'ANDscheduled_at <= now(). - Calls SES/SNS for each.
- Updates status to
sent(orfailed) and firesmessage.sentwebhook.
- Selects up to 100 messages where
- Test-mode keys skip the provider call; scheduled behavior is the same.
Canceling a scheduled send
Not yet exposed — requires an internal endpoint to flip status away from scheduled before the cron picks it up. Planned.
Operational notes
- Cron cadence is once per minute. Expect up to 60s of jitter beyond
scheduled_at. - Cron authenticates via
CRON_SECRET(optional — if unset, endpoint is open). - Usage is incremented when the cron sends the message, not when it's scheduled.