Schedule and cancel
Send later, move it, or stop it.
Runs any of 2 calls on your workspace.
Scheduling
scheduledAt takes an ISO-8601 instant or a duration like PT2H, up to a year out. A scheduled message answers 202 with status: "scheduled" and stays cancellable right up until it sends.
cancellableForSeconds does the same for an immediate send: the composer's undo-send window, exposed rather than hardcoded to a user setting that has no business deciding an API's behaviour. The two are mutually exclusive: a scheduled message is already cancellable.
followUpAt works with either, and with a plain send. It takes an ISO 8601 instant or a duration such as P3D, later than the time the message goes out, and if nobody has replied by then the thread returns to the top of the inbox, unread. Cancelling the message cancels its reminder. GET /follow-ups lists the reminders, and Threads describes them.
Schedule a send
Two hours out. The response carries the id you will need to move or stop it.
curl -X POST "$OE/emails" -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "from": "billing@acme.com", "to": ["ada@example.com"], "subject": "Your September invoice", "html": "<p>Invoice attached.</p>", "scheduledAt": "PT2H" }'HTTP/1.1 202 Accepted Location: /emails/msg_8cd0a316930b4482a8b8440b { "object": "email", "id": "msg_8cd0a316930b4482a8b8440b", "status": "scheduled", "scheduledAt": "2026-09-01T10:19:41.345Z", "cancellableUntil": "2026-09-01T10:19:41.345Z", "messageId": null, "transport": null, "sentAt": null }202, not 200: something still has to happen to it. An immediate send answers 200 with status: "sent", so a caller can branch on the status code alone.
Move it
PATCH /emails/{id} with scheduledAt, while it is queued or scheduled. The same call changes what it says and who it goes to, as the last example shows.
curl -X PATCH "$OE/emails/msg_8cd0a316930b4482a8b8440b" \ -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "scheduledAt": "PT6H" }'{ "object": "email", "id": "msg_8cd0a316930b4482a8b8440b", "status": "scheduled", "scheduledAt": "2026-09-01T14:20:15.881Z" }Stop it
POST /emails/{id}/cancel.
curl -X POST "$OE/emails/msg_8cd0a316930b4482a8b8440b/cancel" -H "$AUTH"{ "object": "email", "id": "msg_8cd0a316930b4482a8b8440b", "status": "cancelled" }Cancelling twice is not an error. The second call returns the same cancelled message, so a retry is safe.
Cancelling one that has already gone is a 409 email_not_cancellable. It cannot be un-sent, and answering anything else would be a lie.
Change it
PATCH /emails/{id} with any of subject, html, text, from, to, cc and bcc, while it is queued or scheduled. A field you leave out keeps its value, and a recipient list replaces the stored one whole.
curl -X PATCH "$OE/emails/msg_8cd0a316930b4482a8b8440b" \ -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "subject": "Your September invoice, corrected", "to": ["ada@example.com", "grace@example.com"] }'{ "object": "email", "id": "msg_8cd0a316930b4482a8b8440b", "status": "scheduled", "subject": "Your September invoice, corrected", "scheduledAt": "2026-09-01T14:20:15.881Z" }from is checked as it is on a send, so it has to be an address the key may send as.
A message translated when it was accepted keeps its wording, so a new subject, html or text on it is a 409 translation_locked. One that was encrypted keeps its wording and its recipients. Cancel those and send again.
Google Workspace