Build an email draft lifecycle with explicit state transitions
Create, update and retire drafts while preserving source messages and separating drafting from sending.
Use send_at as a durable schedule while accounting for cron cadence, permissions and failed sends.
A future send_at on an email draft marks it scheduled. This avoids keeping an agent process alive until a deadline and lets an operator inspect the pending message as a normal resource. Create the draft under /v0/emails/inboxes/:inbox_id/emails/drafts with its recipients, content and intended time, then save the returned draft identity. Scheduling is a declaration of intended future work, not evidence that the message was sent. Your user interface should keep scheduled messages visibly distinct from sent messages and ordinary unscheduled proposals.
Accept a human’s local timezone in your application, resolve daylight-saving ambiguity where applicable and store an ISO-8601 UTC timestamp. Show the resolved date, time and timezone in the approval view. Lupin’s Worker cron runs every five minutes and attempts due drafts, so send_at is not a promise of second-level delivery precision. A draft due just after a tick can wait for the next run, and provider or infrastructure delays can add more time. Do not describe a scheduled email as a mechanism for strict real-time notifications or a guaranteed arrival deadline.
Due-send failures can move a draft to failed for inspection. Read send_status and the current resource before deciding how to recover. A configuration problem, recipient rejection or missing authority needs a different fix from a temporary service outage. Preserve the original authorized attempt identity and investigate any uncertain provider outcome rather than moving send_at forward repeatedly until something appears to succeed. If your application offers retry, show the known state and reason so the operator can distinguish a failed submission from a message that may already have been accepted.
PATCH can change send_at, including clearing a nullable field, and draft deletion can remove an abandoned proposal. Make cancellation a deliberate action before the due attempt and recognize that a racing send may already be in progress. After success, the draft is removed and the sent email becomes the relevant resource. Monitor scheduled backlog, overdue items and failures separately from recipient delivery. Your next step is a local clock-controlled fixture that covers a future schedule, a due draft, a failed provider response and a cancellation, without using a live recipient or waiting for real cron time.
A schedule view should show the requested send time, the resolved UTC value and the current send_status. After the deadline, label a still-pending item overdue rather than sent. This lets an operator distinguish a timezone misunderstanding from cron delay or a failed submission without relying on a model to reconstruct scheduling state from conversational history.
Reviewed against checked-in Lupin API handlers and documentation. Example requests were not executed against live providers.
Prepared with AI assistance by Lupin editorial. How we research and maintain these guides.