Skip to the documentation
PHP

Drafts

`drafts->list`, `listAll`, `iterate`, `get`, `create`, `update` and `delete`.

Every method

drafts.php
$page = $client->drafts->list(query: 'invoice', limit: 25);$draft = $client->drafts->get('draft-5f0c2a9e-8b1d-4e7a-a3c6-2d9f41b7e0c8');echo count($page), ' ', $draft['subject'], PHP_EOL; $created = $client->drafts->create([    'to' => ['[email protected]'],    'cc' => [],    'bcc' => [],    'subject' => 'Your September invoice',    'html' => '<p>Draft body.</p>',    'from' => '[email protected]',    'threadId' => 'CAHk7pQ2x9LmZ4-mail.example.com',]); $updated = $client->drafts->update($created['id'], ['subject' => 'Revised']);$client->drafts->delete($updated['id']);

update keeps the draft’s id, so the value it answers with is always the one you passed. An unknown id is a 404, thrown as a NotFoundException, rather than a new draft.

The fields of a draft are the keys of one array under the API’s names, so the thread a draft replies to is threadId. A draft comes back as an array keyed in camelCase, so $draft['subject'] reads the subject. Every write answers with only object and id, so read the whole draft with get.

list pages the way threads->list does. The API’s pageToken comes back as nextCursor and goes in as cursor:, and listAll and iterate follow it for you. iterate returns a Generator that yields one draft at a time. A page holds 25 drafts unless limit: asks for up to 100. query: takes the search syntax of threads->list, and the search never leaves the drafts. A row is only object and id, so call get for the recipients, subject and body.

The drafts list states no hasMore, so hasMore is true whenever a cursor came back. The server offers one whenever a page is full, so a last page that happens to be full is followed by one empty page.

A draft is stored as a thread labelled DRAFT, which is why $client->threads->list(folder: 'draft') lists the same drafts. get, update and delete answer an ordinary thread id with a 404, even though threads->get opens it. delete removes a draft for good. It does not go to the Bin and there is no undo.

To send a draft, pass its id to emails->send as draftId. The draft supplies the content and the send supplies the envelope. A draft cannot be combined with template or translate.

send_draft.php
$client->emails->send([    'from' => '[email protected]',    'to' => '[email protected]',    'draftId' => 'draft-5f0c2a9e-8b1d-4e7a-a3c6-2d9f41b7e0c8',]);

Parameters: drafts->create and drafts->update

toarray
Recipient addresses as a list of strings, not the array forms `emails->send` accepts, because this endpoint joins the list into the comma-separated one the driver wants. A string may carry a display name, as in `Ada Lovelace <[email protected]>`, but a name that contains a comma splits into two broken recipients. Unlike `emails->send`, the client does not wrap a lone string in a list here, so pass `['[email protected]']`. On create an omitted list saves as empty. On update an omitted field leaves the stored recipients alone, since the handler reads the draft first and merges.
ccarray
Cc addresses, in the same form as `to`. Empty on create when omitted, and untouched on update when omitted.
bccarray
Bcc addresses, in the same form as `to`. Empty on create when omitted, and untouched on update when omitted.
subjectstring
The draft’s subject, at most 998 characters, the RFC 5322 line limit. On create it defaults to an empty string, and an empty subject is stored as `(no subject)`, so a draft always has one.
htmlstring
The draft body as markup, at most 1,000,000 characters. It is the body that wins. `html` and `text` feed the driver’s single message field, so sending both stores this one.
textstring
A plain-text body, at most 1,000,000 characters, used only when `html` is absent. The draft stores one body rather than two parts, so text supplied here comes back unconverted on `html` when the draft is read.
fromstring or null
The sender address to save on the draft, with or without a display name. Left out on create, the draft has no sender. On update it is carried through from the stored draft when omitted. The driver rebuilds the whole message from what it is handed, so a partial patch that dropped it would silently change the chosen sender. An empty string or null on update clears it.
threadIdstring
Attach the draft to an existing thread so it saves as a reply. Like `from`, it is carried through on update when omitted, because rebuilding the message without it would detach the reply from its thread. An empty string on update detaches it. The draft is still stored as a thread of its own under its own id, so it is listed with the drafts rather than inside the thread it replies to.

Leave a field out to keep it. Passing null is not the same. The client sends it, and every field refuses it with a 422 invalid_parameter except from on update, where null clears the sender. Run an array of optional values through array_filter($fields, static fn(mixed $value): bool => $value !== null) before you pass it. The body is strict as well. A field outside these eight is refused the same way, and there is no field for attachments.

Response: a draft (drafts->get)

objectstring
Always `draft`.
idstring
The draft’s id, `draft-` followed by a UUID. Writes answer with only `object` and `id` rather than a whole draft, so read the id off the result instead of reusing the one you sent.
toarray
The recipient addresses as the draft stored them, bare, with any display name dropped. An empty list, never null, when the draft has none.
ccarray
Cc addresses as stored. An empty list, never null, when the draft has none.
bccarray
Bcc addresses as stored. An empty list, never null, when the draft has none.
subjectstring
The stored subject, never null. A draft saved without one reads `(no subject)`, the placeholder the mailbox stores, so compare with that rather than checking for an empty string.
htmlstring
The stored body, or an empty string where the draft has none. There is no separate text field on the way out, so a draft saved with `text` alone is returned here.
fromstring or null
The address the draft was saved with, reported only while the workspace can still send as it. It is null for a draft saved without a sender or from an address that has since gone.
threadIdstring or null
The thread the draft replies to, or null for a draft that starts a new conversation.
attachmentsarray
Each entry has only `filename` and `contentType`, because draft attachments are stored as names and types without content. An `update` empties this list.