SDK
Schedule and cancel
`scheduledAt`, `emails.reschedule` and `emails.cancel`.
Sending later
await openemail.emails.send({ ...message, scheduledAt: 'PT1H' })await openemail.emails.send({ ...message, scheduledAt: new Date('2027-01-01T09:00:00Z') })await openemail.emails.send({ ...message, scheduledAt: '2027-01-01T09:00:00.000Z' })A Date, an ISO-8601 instant, or a duration like PT1H / P2D. Up to a year out, never in the past.
Moving and stopping
const queued = await openemail.emails.send({ ...message, scheduledAt: 'PT1H' }) await openemail.emails.reschedule(queued.id, new Date(Date.now() + 86_400_000))await openemail.emails.cancel(queued.id)Only queued and scheduled messages can be stopped; anything further on is a conflict_error, because some of it is already in somebody's mailbox. Cancelling an already-cancelled message succeeds and changes nothing.
An undo window instead
await openemail.emails.send({ ...message, cancellableForSeconds: 30 })A scheduled message is already cancellable until it goes, so the two cannot be combined, and the server refuses it. Use this one for an undo-send window on an immediate message.
Parameters: scheduling
scheduledAtDate | string- When to send, on `emails.send`: a `Date`, an ISO-8601 instant, or a duration like `PT1H` or `P2D`, which the client renders to a string for the wire. At least one second in the future and at most 365 days out, with either bound a `validation_error` on `scheduledAt`, and natural language is not accepted, because parsing "next Tuesday" wrongly sends a message at a time that cannot be taken back.
cancellableForSecondsnumber- An undo-send window on an IMMEDIATE send: an integer from 0 to 900, defaulting to 0. Any value above 0 is refused alongside `scheduledAt`, which is already cancellable until it goes, and a message held this way sits at `queued` rather than `scheduled`. It is the same deferral mechanism with a short delay.
idstringrequired- The `msg_…` id, and the first argument to both `emails.cancel` and `emails.reschedule`. Both need `emails:send` rather than a scope of their own, and both resolve within the key's own workspace, so an id belonging to another one is a `not_found_error` exactly like an id that never existed.
reschedule.scheduledAtDate | stringrequired- The new time, as the second argument to `emails.reschedule`, parsed by the same rules and against the same one-year window, and the only thing the underlying `PATCH /emails/{id}` will change. A duration is relative to when the SERVER reads it, so a retried reschedule lands slightly later than the first would have: later, never earlier.
Response: EmailResource
object'email'- Always `email`. Both calls answer with the whole message rather than an acknowledgement, so nothing has to be re-fetched to see what changed; `emails.send` returns this same shape plus `replayed`.
idstring- The `msg_…` handle. Stable for the life of the message and the id every other call on it takes.
statusEmailStatus- `cancelled` after a cancel and `scheduled` after a reschedule, including for a message that was only `queued` behind an undo window, which a reschedule turns into a real schedule. Only `queued` and `scheduled` messages can be moved or stopped; anything further on is a `conflict_error` with code `email_not_cancellable`, because some of it is already in somebody's mailbox.
scheduledAtstring | null- The ISO instant the message is due to be dispatched. Set for an undo window as well as for a `scheduledAt` send, since the two are one mechanism, and null on a plain immediate send.
cancellableUntilstring | null- When cancelling stops working, which is the same instant as `scheduledAt` on both deferred paths. Null on an immediate send, which has already gone by the time the call returns.
sentAtstring | null- When the message actually left. Null while it waits, and null for ever on a cancelled one.
messageIdstring | null- The RFC 5322 Message-ID, null until the MIME exists, so always null on a message these two calls can act on. Not something to address the API with, and not what a later bounce comes back on either: the sending service rewrites the header on the way out.
threadIdstring | null- The thread this message belongs to, taken from the request and rewritten with whatever the transport reports once it sends. Null when it is not a reply.
transportEmailTransport | (string & {}) | null- How the bytes left, and null until dispatch, so null on every message a cancel or a reschedule can return. A test-mode send records `test`, and the union stays open so a transport this SDK does not yet name is not a breaking change.
attemptsnumber- How many times dispatch has claimed this row. It is incremented by the claim rather than by a successful send, and is 0 for anything still waiting.
lastErrorstring | null- The last failure recorded against the message, null while nothing has failed. A deferred send whose job could not be enqueued is written here as `Could not schedule: …` and moved to `failed`, which is the one way a scheduled message stops being cancellable without anybody asking.
fromstring- The address the message was authorised to go out as, stored bare and lower-cased. Not always the address that was asked for (a narrowed key that names no `from` resolves to the first address it may use), and any display name is dropped here, because `emails.list`'s `from` filter compares on equality.