---
title: "client.Drafts"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://openemail.uk/docs/go/reference/drafts"
area: "Go"
category: "Reference"
---

# client.Drafts

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

Unsent messages saved in the mailbox.

### `Drafts.List`

List one page of drafts

```go
List(ctx context.Context, opts ...openemail.RequestOption) (*openemail.Page, error)
```

Returns one page of saved drafts, most recently saved first. A row is only `object` and `id`, so call `Get` for recipients, subject and body. A draft is stored as a thread labelled `DRAFT`, so this reads the same index as `threads.list({ folder: 'draft' })`.

`query` takes the same search syntax as `Threads.List`. Plain words must all appear and match loosely, ignoring case, accents and separators, against the draft's subject, its sender and the first 4,000 characters of its body with markup stripped, while a quoted phrase is matched as written apart from case and accents, so `"ben jamin"` does not find "Ben-Jamin". Filler words such as `the` or `emails` are dropped when something else is left to search for. Recipients are stored as one list without roles and are not written for a draft, so `to:`, `cc:` and `bcc:` match nothing here. `subject:`, `body:` and `from:` narrow the search, and `after:`, `before:`, `newer_than:` and `older_than:` read the time the draft was last saved, in UTC, with `after:` including the day it names and `before:` excluding it. A draft saved with no subject is stored as `(no subject)`, so `subject:"no subject"` finds it. The drafts folder always applies, so `in:` and folder `is:` operators such as `is:sent` cannot widen the search beyond drafts, `in:anywhere` included.

The API pages with an opaque `pageToken`, which the SDK hands back as `NextCursor` and accepts as `openemail.WithCursor`. A page holds 25 drafts unless `openemail.WithLimit` says otherwise.

Scopes: `drafts:read`.

**Parameters**

- `openemail.WithQuery` (`string`): Mailbox search over the subject, sender and body preview. Plain words must all appear and match loosely, a quoted phrase has to appear as written, filler words are dropped when something else is left to search for, and operators such as `subject:` and `older_than:30d` narrow it. `to:`, `cc:` and `bcc:` match nothing on a draft, and the search cannot leave drafts.
- `openemail.WithLimit` (`int`): Drafts per page, a whole number from 1 to 100. Defaults to 25.
- `openemail.WithCursor` (`string`): The `NextCursor` of the previous page, passed back unchanged.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

A `*openemail.Page` with `Items`, `HasMore` and `NextCursor`. Each item is a map with `object` set to `draft` and `id`.

**Example**

```go
page, err := client.Drafts.List(ctx, openemail.WithQuery("subject:proposal"), openemail.WithLimit(20))
if err != nil {
	return err
}

for _, draft := range page.Items {
	fmt.Println(draft.String("id"))
}

fmt.Println(page.HasMore, page.NextCursor)
```

**Notes**

- The server offers a cursor whenever a page comes back full, so `HasMore` can be true on the last page and the following call returns no items.
- Saving a draft moves it to the top, so a draft edited while you page is not returned by later pages.
- A value `query` cannot use is ignored rather than narrowing, so a typo in a value widens the result instead of emptying it. That covers `category:`, `larger:`, `smaller:`, `size:`, `messagesize:`, `list:`, `rfc822msgid:`, `received:` and `sent:`, the category words such as `is:promotions`, a `has:` word naming no kind of attachment, an `importance:` other than `high` or `low`, an unreadable date and a duration whose unit is not `h`, `d`, `w`, `m` or `y`. An operator name it does not know, `project:` for instance, is searched as plain text.

Also available in: API [`GET /drafts`](https://openemail.uk/docs/api/reference/drafts#get-drafts); TypeScript [`drafts.list()`](https://openemail.uk/docs/sdk/reference/drafts#list); Python [`drafts.list()`](https://openemail.uk/docs/python/reference/drafts#list); Ruby [`drafts.list`](https://openemail.uk/docs/ruby/reference/drafts#list); PHP [`drafts->list`](https://openemail.uk/docs/php/reference/drafts#list); Java [`drafts().list`](https://openemail.uk/docs/java/reference/drafts#list); C# [`Drafts.ListAsync`](https://openemail.uk/docs/csharp/reference/drafts#list); CLI [`openemail drafts list`](https://openemail.uk/docs/cli/reference/drafts#drafts-list).

### `Drafts.ListAll`

Collect every draft into one slice

```go
ListAll(ctx context.Context, opts ...openemail.RequestOption) ([]openemail.Object, error)
```

Walks every page with the same `query` as `List` and returns once the last page is in, so the whole result sits in memory at once. Drafts rarely number in the thousands, which makes this the simplest way to read them all.

Each request asks for `openemail.WithLimit` drafts, 25 when you leave it out. Passing `openemail.WithCursor` starts the walk from that page instead of the first. The walk ends when `HasMore` is false, when the server stops offering a cursor, or when it repeats one, and a failure on any page fails the call and discards what was collected.

Scopes: `drafts:read`.

**Parameters**

- `openemail.WithQuery` (`string`): Mailbox search over the subject, sender and body preview, as in `List`. Plain words must all appear and match loosely, a quoted phrase has to appear as written, filler words are dropped when something else is left to search for, and operators such as `subject:` and `older_than:30d` narrow it. `to:`, `cc:` and `bcc:` match nothing on a draft, and the search cannot leave drafts.
- `openemail.WithLimit` (`int`): Page size for each request, a whole number from 1 to 100. Defaults to 25.
- `openemail.WithCursor` (`string`): A `NextCursor` to start the walk from instead of the first page.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

A `[]openemail.Object`, every matching draft as a map with `object` set to `draft` and `id`, most recently saved first.

**Example**

```go
drafts, err := client.Drafts.ListAll(ctx, openemail.WithLimit(100))
if err != nil {
	return err
}

for _, draft := range drafts {
	fmt.Println(draft.String("id"), draft.String("object"))
}
```

**Notes**

- Rows are ids only. Reading each draft afterwards is one `Get` per id.
- Each page is its own request with its own retries, so one failed attempt does not restart the walk.

Also available in: API [`GET /drafts`](https://openemail.uk/docs/api/reference/drafts#get-drafts); TypeScript [`drafts.listAll()`](https://openemail.uk/docs/sdk/reference/drafts#listAll); Python [`drafts.list_all()`](https://openemail.uk/docs/python/reference/drafts#listAll); Ruby [`drafts.list_all`](https://openemail.uk/docs/ruby/reference/drafts#listAll); PHP [`drafts->listAll`](https://openemail.uk/docs/php/reference/drafts#listAll); Java [`drafts().listAll`](https://openemail.uk/docs/java/reference/drafts#listAll); C# [`Drafts.ListAllAsync`](https://openemail.uk/docs/csharp/reference/drafts#listAll).

### `Drafts.Iterate`

Stream drafts one at a time across pages

```go
Iterate(ctx context.Context, opts ...openemail.RequestOption) *openemail.Iterator
```

Returns an iterator that yields drafts one by one and requests the next page only when the current one is used up. Nothing is fetched until the loop starts, and breaking out of it stops further requests.

The cursor marks a position in save time rather than a row count, so deleting drafts inside the loop does not make the walk skip the ones after them. Updating a draft during the walk moves it to the top, and it is not yielded a second time.

Scopes: `drafts:read`.

**Parameters**

- `openemail.WithQuery` (`string`): Mailbox search over the subject, sender and body preview, as in `List`. Plain words must all appear and match loosely, a quoted phrase has to appear as written, filler words are dropped when something else is left to search for, and operators such as `subject:` and `older_than:30d` narrow it. `to:`, `cc:` and `bcc:` match nothing on a draft, and the search cannot leave drafts.
- `openemail.WithLimit` (`int`): Page size for each request, a whole number from 1 to 100. Defaults to 25.
- `openemail.WithCursor` (`string`): A `NextCursor` to start from instead of the first page.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

An `*openemail.Iterator` yielding a map with `object` set to `draft` and `id` per step.

**Example**

```go
for draft, err := range client.Drafts.Iterate(ctx, openemail.WithQuery("older_than:30d")).All() {
	if err != nil {
		return err
	}

	fmt.Println(draft.String("id"))
}
```

**Notes**

- The iterator is lazy, so an abandoned loop costs only the pages it consumed.

Also available in: API [`GET /drafts`](https://openemail.uk/docs/api/reference/drafts#get-drafts); TypeScript [`drafts.iterate()`](https://openemail.uk/docs/sdk/reference/drafts#iterate); Python [`drafts.iterate()`](https://openemail.uk/docs/python/reference/drafts#iterate); Ruby [`drafts.iterate`](https://openemail.uk/docs/ruby/reference/drafts#iterate); PHP [`drafts->iterate`](https://openemail.uk/docs/php/reference/drafts#iterate); Java [`drafts().iterate`](https://openemail.uk/docs/java/reference/drafts#iterate); C# [`Drafts.IterateAsync`](https://openemail.uk/docs/csharp/reference/drafts#iterate).

### `Drafts.Get`

Read a draft's recipients, subject and body

```go
Get(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)
```

Returns the fields a draft was saved with: `to`, `cc`, `bcc`, `subject`, the body as `html`, the `from` address, the `threadId` it replies to and its attachment list. The id must belong to a thread labelled `DRAFT`, so an ordinary thread id is a 404 here even though `Threads.Get` opens it.

Values come back normalised rather than as sent. Recipients are bare addresses with display names dropped, an empty subject reads as `(no subject)`, and `from` is the address the draft was saved with. It is reported only while the workspace can still send as the stored sender, so a draft saved from an address that has since gone reads `from: null`, as does a draft saved without a sender. A draft saved with `text` and no `html` returns that text in `html`, unconverted.

`threadId` is null for a draft that does not reply to anything. `forwardOf` is the message in `threadId` the draft forwards, or null. `attachments` carries only `filename`, `contentType` and `size`, because draft attachments are stored as names and types without content: a forwarding draft attaches the forwarded message's files when it is sent, and any other file goes on the send itself.

Scopes: `drafts:read`.

**Parameters**

- `id` (`string`, required): Draft id, which starts with `draft-`.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An `openemail.Object` with `id`, `to`, `cc`, `bcc`, `subject`, `html`, `from`, `threadId`, `forwardOf` and `attachments` as a map with `filename`, `contentType` and `size`.

**Example**

```go
draft, err := client.Drafts.Get(ctx, "draft-5f0c2a9e-8b1d-4e7a-a3c6-2d9f41b7e0c8")
if err != nil {
	return err
}

fmt.Println(draft.String("from"), draft.String("subject"), draft.String("threadId"))
```

**Notes**

- A draft keeps the same id through every update.
- A missing draft fails with an `*openemail.Error` that matches `openemail.ErrNotFound`.

Also available in: API [`GET /drafts/{id}`](https://openemail.uk/docs/api/reference/drafts#get-drafts-id); TypeScript [`drafts.get()`](https://openemail.uk/docs/sdk/reference/drafts#get); Python [`drafts.get()`](https://openemail.uk/docs/python/reference/drafts#get); Ruby [`drafts.get`](https://openemail.uk/docs/ruby/reference/drafts#get); PHP [`drafts->get`](https://openemail.uk/docs/php/reference/drafts#get); Java [`drafts().get`](https://openemail.uk/docs/java/reference/drafts#get); C# [`Drafts.GetAsync`](https://openemail.uk/docs/csharp/reference/drafts#get); CLI [`openemail drafts get`](https://openemail.uk/docs/cli/reference/drafts#drafts-get).

### `Drafts.Create`

Save a new draft

```go
Create(ctx context.Context, body openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)
```

Saves an unsent message to the mailbox and returns its id. Every field is optional, so an empty object saves a blank draft. The body is strict: any key outside the fields below is a 422 `invalid_parameter`, and there is no field for attachments, which a send carries itself.

To forward an email, pass `threadId` and `forwardOf`, the id of a message in that thread from `Threads.Get`. The draft takes the names of that message's files and, unless you give a `subject`, its subject with `Fwd:` in front. `emails.send({ draftId })` then quotes the message below the body, attaches its files and threads it the way forwarding in the app does. `forwardOf` without `threadId`, or naming no message of that thread, is a 422 `invalid_parameter`.

Nothing is validated beyond length. Addresses in `to`, `cc` and `bcc` may carry a display name, as in `Ada Lovelace <ada@acme.com>`, and `from` is stored as given. A draft saved without `from` has no sender, and `Get` reads `from` back as null. `subject` is capped at 998 characters, and `html` and `text` at 1,000,000 each. When both bodies are sent only `html` is kept.

`threadId` records the conversation the draft replies to, but the draft is stored as a thread of its own under its new id. It shows up in `threads.list({ folder: 'draft' })`, not inside the original thread.

Scopes: `drafts:write`.

**Parameters**

- `to` (`[]string`): Recipient addresses, bare or with a display name.
- `cc` (`[]string`): Copy recipients, bare or with a display name.
- `bcc` (`[]string`): Blind copy recipients, bare or with a display name.
- `subject` (`string`): At most 998 characters. Empty is stored as `(no subject)`.
- `html` (`string`): Body markup, at most 1,000,000 characters. Kept over `text` when both are set.
- `text` (`string`): Body used only when `html` is absent, at most 1,000,000 characters. Stored as is and read back in `html`.
- `from` (`string`): Sender address, optionally with a display name. Left out, the draft is saved with no sender.
- `threadId` (`string`): Id of the thread this draft replies to, or the thread holding the message `forwardOf` names.
- `forwardOf` (`string`): Id of the message in `threadId` to forward. Sending the draft quotes it below the body and attaches its files.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An `openemail.Object` with `object` set to `draft` and `id`, where `id` is the new draft's id.

**Example**

```go
draft, err := client.Drafts.Create(ctx, openemail.Body{
	"from":    "Ada Lovelace <ada@acme.com>",
	"to":      []string{"grace@example.com"},
	"subject": "Engine notes for Thursday",
	"html":    "<p>Agenda below, comments welcome.</p>",
})
if err != nil {
	return err
}

fmt.Println(draft.String("id"))
```

**Notes**

- The SDK does not retry a create after a network failure, because the endpoint takes no idempotency key and a second attempt saves a second draft.
- A display name containing a comma splits into two broken recipients, because the lists are joined and split again on commas. Send such names without the comma.
- Draft ids are `draft-` followed by a UUID.

Also available in: API [`POST /drafts`](https://openemail.uk/docs/api/reference/drafts#post-drafts); TypeScript [`drafts.create()`](https://openemail.uk/docs/sdk/reference/drafts#create); Python [`drafts.create()`](https://openemail.uk/docs/python/reference/drafts#create); Ruby [`drafts.create`](https://openemail.uk/docs/ruby/reference/drafts#create); PHP [`drafts->create`](https://openemail.uk/docs/php/reference/drafts#create); Java [`drafts().create`](https://openemail.uk/docs/java/reference/drafts#create); C# [`Drafts.CreateAsync`](https://openemail.uk/docs/csharp/reference/drafts#create); CLI [`openemail drafts create`](https://openemail.uk/docs/cli/reference/drafts#drafts-create).

### `Drafts.Update`

Change fields on a saved draft

```go
Update(ctx context.Context, id string, body openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)
```

A partial update: each field you send replaces the stored value and each field you leave out keeps it. Arrays replace wholesale, so sending `to` with one address drops the rest. The limits and the strict body are the same as `Create`.

The draft must already exist. An unknown id, or the id of a thread that is not a draft, is a 404 rather than a new draft. The returned `id` is the one to keep using, and it is always the id you passed.

Sending `text` without `html` replaces the stored body with that text. The draft keeps its attachment list, and a draft that forwards a message keeps forwarding it unless `threadId` moves it to another conversation.

Scopes: `drafts:write`.

**Parameters**

- `id` (`string`, required): Draft id, which starts with `draft-`.
- `to` (`[]string`): Replacement recipients. An empty array clears them.
- `cc` (`[]string`): Replacement copy recipients.
- `bcc` (`[]string`): Replacement blind copy recipients.
- `subject` (`string`): Replacement subject, at most 998 characters.
- `html` (`string`): Replacement body markup, at most 1,000,000 characters.
- `text` (`string`): Replacement body used only when `html` is absent.
- `from` (`string`): Replacement sender. An empty string clears it, leaving the draft with no sender.
- `threadId` (`string`): Thread the draft replies to. An empty string detaches it.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An `openemail.Object` with `object` set to `draft` and `id`.

**Example**

```go
saved, err := client.Drafts.Update(ctx, "draft-5f0c2a9e-8b1d-4e7a-a3c6-2d9f41b7e0c8", openemail.Body{
	"to":      []string{"grace@example.com", "charles@example.com"},
	"subject": "Engine notes for Thursday, revised",
})
if err != nil {
	return err
}

fmt.Println(saved.String("id"))
```

**Notes**

- The SDK does not retry this call after a network failure. Read the draft with `Get` before trying again.
- An empty string is the only way to clear `threadId` or `from`, since leaving a field out keeps its stored value.

Also available in: API [`PATCH /drafts/{id}`](https://openemail.uk/docs/api/reference/drafts#patch-drafts-id); TypeScript [`drafts.update()`](https://openemail.uk/docs/sdk/reference/drafts#update); Python [`drafts.update()`](https://openemail.uk/docs/python/reference/drafts#update); Ruby [`drafts.update`](https://openemail.uk/docs/ruby/reference/drafts#update); PHP [`drafts->update`](https://openemail.uk/docs/php/reference/drafts#update); Java [`drafts().update`](https://openemail.uk/docs/java/reference/drafts#update); C# [`Drafts.UpdateAsync`](https://openemail.uk/docs/csharp/reference/drafts#update); CLI [`openemail drafts update`](https://openemail.uk/docs/cli/reference/drafts#drafts-update).

### `Drafts.Delete`

Delete a draft permanently

```go
Delete(ctx context.Context, id string, opts ...openemail.RequestOption) (openemail.Object, error)
```

Removes the draft, its stored body and its attachment entries from the mailbox. It does not go to the Bin and there is no undo.

The id must belong to a thread carrying the `DRAFT` label, so an ordinary thread id is a 404. Use `Threads.Trash` to remove real mail.

Scopes: `drafts:write`.

**Parameters**

- `id` (`string`, required): Draft id, which starts with `draft-`.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An `openemail.Object` with `object` set to `draft`, `id` and `deleted` set to `true`.

**Example**

```go
removed, err := client.Drafts.Delete(ctx, "draft-5f0c2a9e-8b1d-4e7a-a3c6-2d9f41b7e0c8")
if err != nil {
	return err
}

fmt.Println(removed.String("id"), removed.Bool("deleted"))
```

**Notes**

- The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.
- `Threads.Update` refuses `DRAFT` with 422 `label_not_directly_settable`, so an ordinary thread can never be turned into something this method deletes.

Also available in: API [`DELETE /drafts/{id}`](https://openemail.uk/docs/api/reference/drafts#delete-drafts-id); TypeScript [`drafts.delete()`](https://openemail.uk/docs/sdk/reference/drafts#delete); Python [`drafts.delete()`](https://openemail.uk/docs/python/reference/drafts#delete); Ruby [`drafts.delete`](https://openemail.uk/docs/ruby/reference/drafts#delete); PHP [`drafts->delete`](https://openemail.uk/docs/php/reference/drafts#delete); Java [`drafts().delete`](https://openemail.uk/docs/java/reference/drafts#delete); C# [`Drafts.DeleteAsync`](https://openemail.uk/docs/csharp/reference/drafts#delete); CLI [`openemail drafts delete`](https://openemail.uk/docs/cli/reference/drafts#drafts-delete).
