---
title: "nextmail.threads"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://nextmail.sh/docs/sdk/reference/threads"
area: "TypeScript"
category: "Reference"
---

# nextmail.threads

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

## Methods

Read, search, label, trash, snooze and mute conversations in the mailbox, read the inbox one tab at a time, and choose the tab the mail of a sender always goes to.

### `nextmail.threads.list()`

List one page of threads in a folder

```ts
list(options?: ThreadListOptions): Promise<Page<ThreadSummaryResource>>
```

Returns one page of threads from the mailbox index, ordered by each thread's latest message with the newest first unless `sort` says otherwise. A row is only `object` and `id`, so call `get` for the messages, labels and unread state.

`sort`, `dateFrom`, `dateTo` and `fromContacts` are the thread list's own controls: the four orders, a date range read against the newest message on each thread, and a filter to mail from saved contacts. Every order pages to the end, and a cursor carries on in the order it was handed out in, so send the same filters with it.

`folder` defaults to `inbox` and is matched against label ids after being upper cased, so `sent`, `archive`, `spam`, `trash`, `draft`, `snoozed`, `starred` and `unread` all work, `bin` is read as `trash`, and a user label id works as a folder too. A name that matches nothing returns an empty page rather than an error. `labelIds` narrows the folder further: a thread must carry the folder label and every id you pass. `category` keeps one inbox tab, `primary`, `promotions`, `updates`, `social` or `forums`, where `primary` is every thread in no other tab.

`query` takes the mailbox search syntax. Plain words must all appear, and each matches loosely: case, accents and separators are ignored and part of a longer word counts, so `min` finds "Benjamin". A quoted phrase is matched as written apart from case and accents, so `"ben jamin"` does not find "Ben-Jamin" while `"quarterly invoice"` finds "Quarterly invoice". When nothing matches exactly, close spellings are returned instead, so `benjimin` finds "Benjamin": a plain word, or the value of `from:`, `to:`, `cc:`, `subject:`, `body:`, `filename:` or `label:`, may differ from the start of a word by one typo when it has four to seven letters and by two when it has eight or more, while a quoted phrase, a word containing a digit, a shorter word and an excluded word still match exactly, and the pages that follow keep matching the same way. Filler words such as `the`, `about` or `emails` are dropped from a list of plain words when something else is left to search for, so `emails from john` searches for `john` alone. Operators such as `from:`, `to:`, `subject:`, `label:`, `is:unread`, `has:pdf`, `after:2026/01/31` and `newer_than:7d` narrow it, and `OR`, parentheses and a leading `-` combine them. `is:muted` finds muted threads, `is:waiting` the threads with a reminder nobody has answered yet, and `category:promotions` one inbox tab in any folder. Recipients are stored as one list without roles and never hold a Bcc, so `cc:` reads the same field as `to:` and `bcc:` matches nothing of its own. `from:me` is mail you sent, and `to:me` is mail carrying one of your own addresses, aliases included, among its recipients or as the address it was delivered to.

With `semantic: true` the plain words of `query` match by meaning rather than spelling. Describe the mail in a few words, `flight to Berlin` or `invoices I still owe`, in any language, and the threads closest in meaning come back best match first, together with every thread the words match as text, which ranks above one that only means the same. A thread is kept only when it is clearly closer to the words than the rest of the mailbox, so a description nothing fits returns only the text matches. Operators and filters narrow it as usual, `sort` does not apply, and a query with no plain words is a text search whatever `semantic` says. Meaning is read from the whole conversation, newest messages first, so an earlier message counts too, and a workspace that turned search by meaning off in its settings gets a text search instead.

Words and the `from:`, `to:`, `cc:`, `subject:` and `body:` operators read only the newest message on each thread: its sender, its recipients, its subject and the first 4,000 characters of its body with markup stripped. `filename:` and `has:` read every attachment on the whole conversation, and `label:`, `in:` and `is:` read the whole conversation. A plain word also matches the name of any attachment on the conversation, whichever message carried it. A sealed message has no body text to match. The search stays inside `folder` unless the query names a folder itself, with `in:` or a folder `is:` such as `is:sent`, and `in:anywhere` searches every folder, on its own as well as beside other terms. A drafts listing is the exception and stays in drafts whatever the query names.

Dates read the newest activity on the thread, in UTC. `after:` includes the day it names and `before:` excludes it, and a date can be written `YYYY/MM/DD`, `YYYY-MM-DD`, `YYYYMMDD`, as a bare year, or as epoch seconds or milliseconds. A short date reads day first (`16/09/2026`), unless the second number cannot be a month (`09/16/2026`), and a number above 12 settles it either way. The API pages with an opaque `pageToken`, which the SDK hands back as `nextCursor` and accepts as `options.cursor`.

Scopes: `threads:read`.

**Parameters**

- `options.folder` (`string`): Label the threads must carry, case insensitive. Defaults to `inbox`, and `bin` is read as `trash`.
- `options.query` (`string`): Mailbox search. 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 `from:ada`, `has:pdf` and `in:anywhere` narrow it.
- `options.labelIds` (`string | Array<string>`): Label ids a thread must all carry on top of `folder`, matched exactly. An array is sent comma separated.
- `options.sort` (`ThreadSort`): `newest` (the default), `oldest`, `sender` or `subject`, the four orders of the thread list in the app. `sender` and `subject` are alphabetical, newest first within one sender or subject.
- `options.dateFrom` (`Date | string`): Keeps threads whose newest message arrived at or after this instant. A `Date` or an ISO 8601 string with a time and an offset.
- `options.dateTo` (`Date | string`): Keeps threads whose newest message arrived at or before this instant. Both ends are included, and `dateFrom` after `dateTo` is a 422.
- `options.fromContacts` (`boolean`): When true, keeps only threads whose newest message came from a saved contact. A key limited to some addresses reads the contacts its owner saved.
- `options.semantic` (`boolean`): When true, the plain words of `query` match by meaning instead of spelling, in any language, and the closest threads come first along with every thread the words match as text. Operators and the other filters still apply and `sort` does not. Ignored when the workspace has search by meaning turned off.
- `options.category` (`InboxCategory`): Keeps only the threads sorted into this inbox tab: `primary`, `promotions`, `updates`, `social` or `forums`. `primary` is every thread in no other tab. In `query`, `category:promotions` filters the same way in any folder.
- `options.address` (`string`): Keeps only the threads delivered to this address, the address filter of the thread list. One the key does not reach returns nothing rather than failing.
- `options.limit` (`number`): Threads per page, a whole number from 1 to 100. Defaults to 25.
- `options.cursor` (`string`): The `nextCursor` of the previous page, passed back unchanged.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Page<ThreadSummaryResource>` with `items`, `hasMore` and `nextCursor`. Each item is `{ object: 'thread', id }`.

**Example**

```ts
const page = await nextmail.threads.list({ folder: 'inbox', query: 'from:ada has:pdf', limit: 50 })

const lastWeek = await nextmail.threads.list({ sort: 'oldest', dateFrom: new Date(Date.now() - 7 * 864e5), fromContacts: true })
console.log(lastWeek.items.length)

const trip = await nextmail.threads.list({ folder: 'archive', query: 'flight to Berlin', semantic: true })
console.log(trip.items[0]?.id)

const offers = await nextmail.threads.list({ category: 'promotions' })
console.log(offers.items.length)

console.log(page.items.map((thread) => thread.id))

if (page.nextCursor) {
    const next = await nextmail.threads.list({ folder: 'inbox', query: 'from:ada has:pdf', limit: 50, cursor: page.nextCursor })
    console.log(next.items.length)
}
```

**Notes**

- The server offers a cursor whenever a page comes back full, so `hasMore` can be true on what turns out to be the last page, and the next call then returns no items.
- Threads that arrived in the same instant are ordered by id, so a page boundary between two of them never skips or repeats one.
- A thread that receives mail while you page moves ahead of the cursor and 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 `larger:`, `smaller:`, `size:`, `messagesize:`, `list:`, `rfc822msgid:`, `received:` and `sent:`, a `category:` naming no inbox tab, 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.
- A narrowed key only sees threads delivered to the addresses it covers, every address on a whole domain it holds included. A thread still has to carry the folder and every one of `labelIds`, the same as for any other key.

Also available in: API [`GET /threads`](https://nextmail.sh/docs/api/reference/threads#get-threads); Python [`threads.list()`](https://nextmail.sh/docs/python/reference/threads#list); Ruby [`threads.list`](https://nextmail.sh/docs/ruby/reference/threads#list); PHP [`threads->list`](https://nextmail.sh/docs/php/reference/threads#list); Go [`Threads.List`](https://nextmail.sh/docs/go/reference/threads#list); Java [`threads().list`](https://nextmail.sh/docs/java/reference/threads#list); C# [`Threads.ListAsync`](https://nextmail.sh/docs/csharp/reference/threads#list); CLI [`nextmail threads list`](https://nextmail.sh/docs/cli/reference/threads#threads-list).

### `nextmail.threads.listAll()`

Collect every thread in a folder into one array

```ts
listAll(options?: ThreadListOptions): Promise<Array<ThreadSummaryResource>>
```

Walks every page with the same filters as `list` and resolves once the last page is in, so the whole result sits in memory at once. That suits a label view or a small folder. For a large inbox, `iterate` lets you stop as soon as you have what you need.

Each request asks for `options.limit` threads, 25 when you leave it out, so raising it to 100 needs a quarter of the round trips. Passing `options.cursor` 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.

A failure on any page rejects the promise and discards everything collected so far.

Scopes: `threads:read`.

**Parameters**

- `options.folder` (`string`): Label the threads must carry, case insensitive. Defaults to `inbox`.
- `options.query` (`string`): Mailbox search, 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 `from:ada`, `has:pdf` and `in:anywhere` narrow it.
- `options.labelIds` (`string | Array<string>`): Label ids a thread must all carry on top of `folder`, matched exactly.
- `options.sort` (`ThreadSort`): `newest` (the default), `oldest`, `sender` or `subject`, the four orders of the thread list in the app. `sender` and `subject` are alphabetical, newest first within one sender or subject.
- `options.dateFrom` (`Date | string`): Keeps threads whose newest message arrived at or after this instant. A `Date` or an ISO 8601 string with a time and an offset.
- `options.dateTo` (`Date | string`): Keeps threads whose newest message arrived at or before this instant. Both ends are included, and `dateFrom` after `dateTo` is a 422.
- `options.fromContacts` (`boolean`): When true, keeps only threads whose newest message came from a saved contact. A key limited to some addresses reads the contacts its owner saved.
- `options.semantic` (`boolean`): When true, the plain words of `query` match by meaning instead of spelling, in any language, and the closest threads come first along with every thread the words match as text. Operators and the other filters still apply and `sort` does not. Ignored when the workspace has search by meaning turned off.
- `options.category` (`InboxCategory`): Keeps only the threads sorted into this inbox tab: `primary`, `promotions`, `updates`, `social` or `forums`. `primary` is every thread in no other tab.
- `options.address` (`string`): Keeps only the threads delivered to this address, the address filter of the thread list. One the key does not reach returns nothing rather than failing.
- `options.limit` (`number`): Page size for each request, a whole number from 1 to 100. Defaults to 25.
- `options.cursor` (`string`): A `nextCursor` to start the walk from instead of the first page.
- `options.signal` (`AbortSignal`): Cancels the request in flight and rejects the whole walk.
- `options.apiKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

`Array<ThreadSummaryResource>`, every matching thread as `{ object: 'thread', id }`, newest first.

**Example**

```ts
const snoozed = await nextmail.threads.listAll({ folder: 'snoozed', limit: 100 })

console.log(`${snoozed.length} threads are waiting to wake`)
```

**Notes**

- Each page is its own request with its own retries, so a network blip on page five does not restart the walk from page one.
- Rows are ids only. Reading the threads afterwards is one `get` per id.

Also available in: API [`GET /threads`](https://nextmail.sh/docs/api/reference/threads#get-threads); Python [`threads.list_all()`](https://nextmail.sh/docs/python/reference/threads#listAll); Ruby [`threads.list_all`](https://nextmail.sh/docs/ruby/reference/threads#listAll); PHP [`threads->listAll`](https://nextmail.sh/docs/php/reference/threads#listAll); Go [`Threads.ListAll`](https://nextmail.sh/docs/go/reference/threads#listAll); Java [`threads().listAll`](https://nextmail.sh/docs/java/reference/threads#listAll); C# [`Threads.ListAllAsync`](https://nextmail.sh/docs/csharp/reference/threads#listAll).

### `nextmail.threads.iterate()`

Stream threads one at a time across pages

```ts
iterate(options?: ThreadListOptions): AsyncGenerator<ThreadSummaryResource, void, undefined>
```

Returns an async generator that yields threads 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, so this is the way to scan a large folder for the first match.

Filters behave as in `list`, and each request asks for `options.limit` threads, 25 by default. The cursor marks a position in time rather than a row count, so trashing, archiving or relabelling threads inside the loop does not make the walk skip the ones after them. A thread that receives new mail during the walk moves ahead of the cursor and is not yielded again. With `semantic: true` the cursor counts places in the ranked list instead, so moving a thread out of the folder during the walk can shift the ones after it.

Scopes: `threads:read`.

**Parameters**

- `options.folder` (`string`): Label the threads must carry, case insensitive. Defaults to `inbox`.
- `options.query` (`string`): Mailbox search, 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 `from:ada`, `has:pdf` and `in:anywhere` narrow it.
- `options.labelIds` (`string | Array<string>`): Label ids a thread must all carry on top of `folder`, matched exactly.
- `options.sort` (`ThreadSort`): `newest` (the default), `oldest`, `sender` or `subject`, the four orders of the thread list in the app. `sender` and `subject` are alphabetical, newest first within one sender or subject.
- `options.dateFrom` (`Date | string`): Keeps threads whose newest message arrived at or after this instant. A `Date` or an ISO 8601 string with a time and an offset.
- `options.dateTo` (`Date | string`): Keeps threads whose newest message arrived at or before this instant. Both ends are included, and `dateFrom` after `dateTo` is a 422.
- `options.fromContacts` (`boolean`): When true, keeps only threads whose newest message came from a saved contact. A key limited to some addresses reads the contacts its owner saved.
- `options.semantic` (`boolean`): When true, the plain words of `query` match by meaning instead of spelling, in any language, and the closest threads come first along with every thread the words match as text. Operators and the other filters still apply and `sort` does not. Ignored when the workspace has search by meaning turned off.
- `options.category` (`InboxCategory`): Keeps only the threads sorted into this inbox tab: `primary`, `promotions`, `updates`, `social` or `forums`. `primary` is every thread in no other tab.
- `options.address` (`string`): Keeps only the threads delivered to this address, the address filter of the thread list. One the key does not reach returns nothing rather than failing.
- `options.limit` (`number`): Page size for each request, a whole number from 1 to 100. Defaults to 25.
- `options.cursor` (`string`): A `nextCursor` to start from instead of the first page.
- `options.signal` (`AbortSignal`): Cancels the request in flight and ends the iteration with that error.
- `options.apiKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

`AsyncGenerator<ThreadSummaryResource, void, undefined>` yielding `{ object: 'thread', id }` per step.

**Example**

```ts
for await (const summary of nextmail.threads.iterate({ folder: 'inbox', query: 'receipt newer_than:30d', limit: 100 })) {
    const thread = await nextmail.threads.get(summary.id)

    if (thread.hasUnread) {
        await nextmail.threads.update(summary.id, { read: true })
    }
}
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages it consumed.
- An aborted `options.signal` throws `NextmailNetworkError` out of the loop rather than ending it quietly.

Also available in: API [`GET /threads`](https://nextmail.sh/docs/api/reference/threads#get-threads); Python [`threads.iterate()`](https://nextmail.sh/docs/python/reference/threads#iterate); Ruby [`threads.iterate`](https://nextmail.sh/docs/ruby/reference/threads#iterate); PHP [`threads->iterate`](https://nextmail.sh/docs/php/reference/threads#iterate); Go [`Threads.Iterate`](https://nextmail.sh/docs/go/reference/threads#iterate); Java [`threads().iterate`](https://nextmail.sh/docs/java/reference/threads#iterate); C# [`Threads.IterateAsync`](https://nextmail.sh/docs/csharp/reference/threads#iterate).

### `nextmail.threads.get()`

Read a thread with every message on it

```ts
get(id: string, options?: RequestScope): Promise<ThreadResource>
```

Returns the whole conversation, oldest message first, with the labels the thread sits in and its unread state. Unsent draft replies are included in `messages` with `isDraft: true`, which is why `messageCount`, the length of `messages`, can be higher than `totalReplies`, which counts only real messages.

Messages are passed through as the mailbox stored them, so `MessageResource` is an open record rather than a fixed field list. The one field the API commits to is `encryption`. When `encryption.format` is `pgp-mime`, `pgp-inline` or `smime-encrypted`, the message is sealed and its body is empty or holds armour. `pgp-signed` and `smime-signed` are ordinary readable mail. A message with no `encryption` at all was stored before detection existed, so its absence says nothing about whether it was plaintext.

Each message's `tags` and `unread` are overwritten on read with the thread's current labels and unread flag, so they describe the thread and not that one message.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id, as returned by `list` or carried on a message.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadResource` with `id` echoed from the request, `messages`, `labels` as `{ id, name, color }`, `messageCount`, `hasUnread`, `totalReplies` and `deliveredTo`, the workspace address the thread belongs to (null when none was recorded).

**Example**

```ts
const thread = await nextmail.threads.get('CAHk7pQ2x9LmZ4-mail.example.com')
const sealedFormats = ['pgp-mime', 'pgp-inline', 'smime-encrypted']

for (const message of thread.messages) {
    const sealed = sealedFormats.includes(String(message.encryption?.format))
    console.log(message.id, message.subject, sealed ? 'sealed' : 'readable')
}

console.log(thread.labels.map((label) => label.id))
```

**Notes**

- A thread delivered to no address the key covers is a 404, exactly like one that does not exist.
- `hasUnread` mirrors the `UNREAD` label, and `update` with `read` is how you change it.
- Draft ids from `drafts.list` open here too, because a draft is stored as a thread labelled `DRAFT`.

Also available in: API [`GET /threads/{id}`](https://nextmail.sh/docs/api/reference/threads#get-threads-id); Python [`threads.get()`](https://nextmail.sh/docs/python/reference/threads#get); Ruby [`threads.get`](https://nextmail.sh/docs/ruby/reference/threads#get); PHP [`threads->get`](https://nextmail.sh/docs/php/reference/threads#get); Go [`Threads.Get`](https://nextmail.sh/docs/go/reference/threads#get); Java [`threads().get`](https://nextmail.sh/docs/java/reference/threads#get); C# [`Threads.GetAsync`](https://nextmail.sh/docs/csharp/reference/threads#get); CLI [`nextmail threads get`](https://nextmail.sh/docs/cli/reference/threads#threads-get).

### `nextmail.threads.update()`

Mark a thread read or unread and change its labels

```ts
update(id: string, patch: ThreadPatch, options?: RequestScope): Promise<UpdatedThreadResource>
```

Adds and removes labels on a thread in one call. `read` is shorthand for the `UNREAD` label: `true` removes it and `false` adds it. At least one of `read`, a non empty `addLabelIds` or a non empty `removeLabelIds` is required, each list takes at most 50 ids, and any other key is a 422 because the body is strict.

Folders are labels too, so archiving is `addLabelIds: ['ARCHIVE']` with `removeLabelIds: ['INBOX']`, the same pair the app uses. `TRASH`, `SNOOZED` and `DRAFT` are refused in either list with 422 `label_not_directly_settable`, because each needs a step this route cannot take. Use `trash` and `snooze` for those.

User label ids come from `labels.list`, and the system ids such as `ARCHIVE`, `STARRED` and `UNREAD` are taken in any case. An id in `addLabelIds` that names no label is refused with 422 `label_not_found` and nothing on the thread changes, so create the label with `labels.create` first. An unknown id in `removeLabelIds` is not an error, since the thread cannot carry it. Removals are applied before additions, so an id in both lists ends up on the thread.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `patch.read` (`boolean`): `true` removes `UNREAD`, `false` adds it.
- `patch.addLabelIds` (`Array<string>`): Label ids to put on the thread, at most 50, each naming a label, never `TRASH`, `SNOOZED` or `DRAFT`.
- `patch.removeLabelIds` (`Array<string>`): Label ids to take off the thread, at most 50, never `TRASH`, `SNOOZED` or `DRAFT`.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`UpdatedThreadResource` with `id`, `addedLabelIds` and `removedLabelIds`. Both lists echo the request, with system ids upper cased, plus the `UNREAD` change that `read` implies, not what actually changed.

**Example**

```ts
const updated = await nextmail.threads.update('CAHk7pQ2x9LmZ4-mail.example.com', {
    read: true,
    addLabelIds: ['ARCHIVE', 'USER_RECEIPTS'],
    removeLabelIds: ['INBOX']
})

console.log(updated.addedLabelIds, updated.removedLabelIds)
```

**Notes**

- The SDK retries this call after a network failure or a retryable status, which is safe because adding a label already present or removing one already gone changes nothing.
- The thread is looked up before the body is validated, so a wrong id is a 404 even when the patch is also invalid.
- User label ids look like `USER_RECEIPTS`. Take them from `labels.list` rather than building them.

Also available in: API [`PATCH /threads/{id}`](https://nextmail.sh/docs/api/reference/threads#patch-threads-id); Python [`threads.update()`](https://nextmail.sh/docs/python/reference/threads#update); Ruby [`threads.update`](https://nextmail.sh/docs/ruby/reference/threads#update); PHP [`threads->update`](https://nextmail.sh/docs/php/reference/threads#update); Go [`Threads.Update`](https://nextmail.sh/docs/go/reference/threads#update); Java [`threads().update`](https://nextmail.sh/docs/java/reference/threads#update); C# [`Threads.UpdateAsync`](https://nextmail.sh/docs/csharp/reference/threads#update); CLI [`nextmail threads update`](https://nextmail.sh/docs/cli/reference/threads#threads-update).

### `nextmail.threads.trash()`

Move a thread to the Bin

```ts
trash(id: string, options?: RequestScope): Promise<TrashedThreadResource>
```

Adds `TRASH` and removes `INBOX`, `SPAM`, `SNOOZED` and `ARCHIVE` in one step, which is what the app's delete does. Doing half of that through `update` would leave the thread listed in both the Bin and its old folder, which is why `update` refuses `TRASH`.

Nothing is deleted. The thread stays readable with `get` and lists under `folder: 'trash'`. Calling this on a thread that is already in the Bin changes nothing and returns the same body, which is why the SDK retries it.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`TrashedThreadResource`, `{ object: 'thread', id, trashed: true }`.

**Example**

```ts
const result = await nextmail.threads.trash('CAHk7pQ2x9LmZ4-mail.example.com')

console.log(result.id, result.trashed)
```

**Notes**

- `restore` takes a thread back out of the Bin. `update` refuses `TRASH` in `removeLabelIds` as well as in `addLabelIds`.
- Trashing a snoozed thread also cancels its scheduled wake, so it does not reappear in the inbox later.
- A thread delivered to no address the key covers is a 404.

Also available in: API [`POST /threads/{id}/trash`](https://nextmail.sh/docs/api/reference/threads#post-threads-id-trash); Python [`threads.trash()`](https://nextmail.sh/docs/python/reference/threads#trash); Ruby [`threads.trash`](https://nextmail.sh/docs/ruby/reference/threads#trash); PHP [`threads->trash`](https://nextmail.sh/docs/php/reference/threads#trash); Go [`Threads.Trash`](https://nextmail.sh/docs/go/reference/threads#trash); Java [`threads().trash`](https://nextmail.sh/docs/java/reference/threads#trash); C# [`Threads.TrashAsync`](https://nextmail.sh/docs/csharp/reference/threads#trash); CLI [`nextmail threads trash`](https://nextmail.sh/docs/cli/reference/threads#threads-trash).

### `nextmail.threads.snooze()`

Hide a thread until a set time

```ts
snooze(id: string, wakeAt: Date | string, options?: RequestScope): Promise<SnoozedThreadResource>
```

Adds `SNOOZED`, removes `INBOX` and stores a wake time, all in one call. Both halves matter: the label hides the thread and the stored wake time is what brings it back. A thread labelled `SNOOZED` any other way would never return, which is why `update` refuses that label.

`wakeAt` takes a `Date` or an ISO 8601 string. The SDK converts a `Date` with `toISOString()`, and the server answers 422 `invalid_parameter` on `wakeAt` when the value does not parse or is not in the future. Snoozing a thread that is already snoozed replaces its wake time.

Threads are woken by an hourly sweep, so one comes back at the first sweep after `wakeAt`, up to about an hour late. Opening the Snoozed folder in the app wakes overdue threads straight away. A thread always wakes into the inbox.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `wakeAt` (`Date | string`, required): When the thread should return, a future instant.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`SnoozedThreadResource` with `id` and `snoozedUntil`, the wake time normalised to a UTC ISO string.

**Example**

```ts
const wakeAt = new Date()
wakeAt.setUTCDate(wakeAt.getUTCDate() + 1)
wakeAt.setUTCHours(9, 0, 0, 0)

const snoozed = await nextmail.threads.snooze('CAHk7pQ2x9LmZ4-mail.example.com', wakeAt)

console.log(snoozed.snoozedUntil)
```

**Notes**

- Only `INBOX` is removed, so a thread snoozed from another folder keeps that folder's label while it sleeps and wakes carrying both.
- A string with no zone offset is read in the server's local zone, so send `Z` or an explicit offset.
- The SDK retries this call, which is safe because a repeat stores the same wake time.

Also available in: API [`POST /threads/{id}/snooze`](https://nextmail.sh/docs/api/reference/threads#post-threads-id-snooze); Python [`threads.snooze()`](https://nextmail.sh/docs/python/reference/threads#snooze); Ruby [`threads.snooze`](https://nextmail.sh/docs/ruby/reference/threads#snooze); PHP [`threads->snooze`](https://nextmail.sh/docs/php/reference/threads#snooze); Go [`Threads.Snooze`](https://nextmail.sh/docs/go/reference/threads#snooze); Java [`threads().snooze`](https://nextmail.sh/docs/java/reference/threads#snooze); C# [`Threads.SnoozeAsync`](https://nextmail.sh/docs/csharp/reference/threads#snooze); CLI [`nextmail threads snooze`](https://nextmail.sh/docs/cli/reference/threads#threads-snooze).

### `nextmail.threads.unsnooze()`

Bring a snoozed thread back now

```ts
unsnooze(id: string, options?: RequestScope): Promise<SnoozedThreadResource>
```

Adds `INBOX`, removes `SNOOZED` and deletes the stored wake time, so the thread returns immediately and the hourly sweep leaves it alone afterwards.

A thread that is not snoozed is left where it is, so an archived thread stays archived, and the response still reports `snoozedUntil: null`.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`SnoozedThreadResource` with `id` and `snoozedUntil` set to null.

**Example**

```ts
const thread = await nextmail.threads.get('CAHk7pQ2x9LmZ4-mail.example.com')

if (thread.labels.some((label) => label.id === 'SNOOZED')) {
    const result = await nextmail.threads.unsnooze(thread.id)
    console.log(result.snoozedUntil)
}
```

**Notes**

- Removing `SNOOZED` through `update` is refused with 422 `label_not_directly_settable`, because it would leave the wake time scheduled.
- Safe to retry, and the SDK does: a second call applies the same labels and deletes a wake time that is already gone.

Also available in: API [`POST /threads/{id}/unsnooze`](https://nextmail.sh/docs/api/reference/threads#post-threads-id-unsnooze); Python [`threads.unsnooze()`](https://nextmail.sh/docs/python/reference/threads#unsnooze); Ruby [`threads.unsnooze`](https://nextmail.sh/docs/ruby/reference/threads#unsnooze); PHP [`threads->unsnooze`](https://nextmail.sh/docs/php/reference/threads#unsnooze); Go [`Threads.Unsnooze`](https://nextmail.sh/docs/go/reference/threads#unsnooze); Java [`threads().unsnooze`](https://nextmail.sh/docs/java/reference/threads#unsnooze); C# [`Threads.UnsnoozeAsync`](https://nextmail.sh/docs/csharp/reference/threads#unsnooze); CLI [`nextmail threads unsnooze`](https://nextmail.sh/docs/cli/reference/threads#threads-unsnooze).

### `nextmail.threads.mute()`

Mute a thread

```ts
mute(id: string, options?: RequestScope): Promise<MutedThreadResource>
```

Mutes a conversation, as Mute does in the app. A thread in the inbox moves to the archive, and from then on new mail in it goes straight to the archive, unread, with no push, sound or desktop notice. A webhook for that mail carries `muted: true`.

Thread state belongs to the whole workspace, so the thread is muted for everyone on it, and mail addressed only to you does not bring it back. Calling it again changes nothing, which is why the SDK retries it. `update` with `MUTE` in `addLabelIds` does the same.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`MutedThreadResource`, `{ object: 'thread', id, muted: true }`.

**Example**

```ts
const muted = await nextmail.threads.mute('CAHk7pQ2x9LmZ4-mail.example.com')

console.log(muted.id, muted.muted)
```

**Notes**

- Only a thread in the inbox moves. One in Sent, the archive or Snoozed stays where it is.
- A muted thread carries the built-in `MUTE` label, which `get` shows among its labels and `is:muted` finds in `query`.
- `unmute` takes the mute off. A thread delivered to no address the key covers is a 404.

Also available in: API [`POST /threads/{id}/mute`](https://nextmail.sh/docs/api/reference/threads#post-threads-id-mute); Python [`threads.mute()`](https://nextmail.sh/docs/python/reference/threads#mute); Ruby [`threads.mute`](https://nextmail.sh/docs/ruby/reference/threads#mute); PHP [`threads->mute`](https://nextmail.sh/docs/php/reference/threads#mute); Go [`Threads.Mute`](https://nextmail.sh/docs/go/reference/threads#mute); Java [`threads().mute`](https://nextmail.sh/docs/java/reference/threads#mute); C# [`Threads.MuteAsync`](https://nextmail.sh/docs/csharp/reference/threads#mute); CLI [`nextmail threads mute`](https://nextmail.sh/docs/cli/reference/threads#threads-mute).

### `nextmail.threads.unmute()`

Take the mute off a thread

```ts
unmute(id: string, options?: RequestScope): Promise<MutedThreadResource>
```

Takes the mute off, so the next message in the thread arrives in the inbox and alerts people again.

The thread stays where it is. Move it back with `update` and `INBOX` in `addLabelIds` if you want it in the inbox now. A thread that is not muted is left as it is, which is why the SDK retries this call.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`MutedThreadResource`, `{ object: 'thread', id, muted: false }`.

**Example**

```ts
const thread = await nextmail.threads.get('CAHk7pQ2x9LmZ4-mail.example.com')

if (thread.labels.some((label) => label.id === 'MUTE')) {
    const result = await nextmail.threads.unmute(thread.id)
    console.log(result.muted)
}
```

**Notes**

- Unmuting is for the whole workspace, as muting is: the thread alerts everyone on it again.
- A thread delivered to no address the key covers is a 404.

Also available in: API [`POST /threads/{id}/unmute`](https://nextmail.sh/docs/api/reference/threads#post-threads-id-unmute); Python [`threads.unmute()`](https://nextmail.sh/docs/python/reference/threads#unmute); Ruby [`threads.unmute`](https://nextmail.sh/docs/ruby/reference/threads#unmute); PHP [`threads->unmute`](https://nextmail.sh/docs/php/reference/threads#unmute); Go [`Threads.Unmute`](https://nextmail.sh/docs/go/reference/threads#unmute); Java [`threads().unmute`](https://nextmail.sh/docs/java/reference/threads#unmute); C# [`Threads.UnmuteAsync`](https://nextmail.sh/docs/csharp/reference/threads#unmute); CLI [`nextmail threads unmute`](https://nextmail.sh/docs/cli/reference/threads#threads-unmute).

### `nextmail.threads.listMessages()`

List the messages of a thread in a fixed shape

```ts
listMessages(id: string, options?: RequestScope): Promise<Array<MailboxMessageResource>>
```

Returns every message on the thread, oldest first, as `MailboxMessageResource`: `from`, `to`, `cc`, `bcc` and `replyTo` as `{ name, email }`, `date` as an ISO 8601 instant, `messageId`, `inReplyTo` and `references`, the body as `text` and `html`, the `attachments` without their bytes, the `headers` Nextmail keeps and `encryption`. Unsent draft replies are in the list with `draft: true`.

These fields are a contract: they are added to, never renamed or taken away, whatever the mailbox stores underneath. `threads.get` still returns each message the way the mailbox stored it, so use this when your code reads messages field by field.

`text` is the plain text body, or the text drawn from the HTML when the message came as HTML only. `html` is null for a plain text message. `headers` holds From, To, Cc, Reply-To, Subject, Date, Message-ID, In-Reply-To, References, List-Unsubscribe and List-Unsubscribe-Post when the message had them, and nothing else, because the rest of what it arrived with is not kept.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id, as returned by `list`.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Array<MailboxMessageResource>`, oldest first.

**Example**

```ts
const messages = await nextmail.threads.listMessages('thr_2b8e4c1a9d7f4e3b8a6c5d0e')

for (const message of messages) {
    console.log(message.date, message.from.email, message.subject)
    console.log(message.text?.slice(0, 200))
}
```

**Notes**

- A thread delivered to no address the key covers is a 404, exactly like one that does not exist.
- Read an attachment's bytes with `listAttachments(id, message.id)`.

Also available in: API [`GET /threads/{id}/messages`](https://nextmail.sh/docs/api/reference/threads#get-threads-id-messages); Python [`threads.list_messages()`](https://nextmail.sh/docs/python/reference/threads#listMessages); Ruby [`threads.list_messages`](https://nextmail.sh/docs/ruby/reference/threads#listMessages); PHP [`threads->listMessages`](https://nextmail.sh/docs/php/reference/threads#listMessages); Go [`Threads.ListMessages`](https://nextmail.sh/docs/go/reference/threads#listMessages); Java [`threads().listMessages`](https://nextmail.sh/docs/java/reference/threads#listMessages); C# [`Threads.ListMessagesAsync`](https://nextmail.sh/docs/csharp/reference/threads#listMessages); CLI [`nextmail threads list-messages`](https://nextmail.sh/docs/cli/reference/threads#threads-list-messages).

### `nextmail.threads.getMessage()`

Read one message of a thread in a fixed shape

```ts
getMessage(id: string, messageId: string, options?: RequestScope): Promise<MailboxMessageResource>
```

Returns one message as `MailboxMessageResource`, the shape `listMessages` lists. The thread is checked first and then the message, so a `messageId` that is not on that thread is a 404 even when it exists elsewhere in the mailbox.

The `emailId` of an `email.received` webhook is the `messageId` to pass here, beside its `threadId`.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id the message belongs to.
- `messageId` (`string`, required): The message, as `id` on a message `listMessages` returns.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`MailboxMessageResource`.

**Example**

```ts
const message = await nextmail.threads.getMessage('thr_2b8e4c1a9d7f4e3b8a6c5d0e', 'msg_7c2e9a41b3d84f6e9a0c1d2e')

console.log(message.from, message.date)
console.log(message.attachments.map((file) => file.filename))
```

**Notes**

- Both ids must match: a real message id paired with the wrong thread id is a 404 `resource_not_found`.
- A thread delivered to no address the key covers is a 404 before the message is looked at.

Also available in: API [`GET /threads/{id}/messages/{messageId}`](https://nextmail.sh/docs/api/reference/threads#get-threads-id-messages-messageid); Python [`threads.get_message()`](https://nextmail.sh/docs/python/reference/threads#getMessage); Ruby [`threads.get_message`](https://nextmail.sh/docs/ruby/reference/threads#getMessage); PHP [`threads->getMessage`](https://nextmail.sh/docs/php/reference/threads#getMessage); Go [`Threads.GetMessage`](https://nextmail.sh/docs/go/reference/threads#getMessage); Java [`threads().getMessage`](https://nextmail.sh/docs/java/reference/threads#getMessage); C# [`Threads.GetMessageAsync`](https://nextmail.sh/docs/csharp/reference/threads#getMessage); CLI [`nextmail threads get-message`](https://nextmail.sh/docs/cli/reference/threads#threads-get-message).

### `nextmail.threads.listAttachments()`

List a message's attachments with their content

```ts
listAttachments(id: string, messageId: string, options?: RequestScope): Promise<Array<AttachmentResource>>
```

Returns the attachments of one message with each file's bytes inlined as base64 in `content`. The thread is checked first and then the message, so a `messageId` that is not on that thread is a 404 even when it exists elsewhere in the mailbox. Take message ids from the `messages` of `threads.get`.

This is the display list. The ciphertext of an encrypted envelope is in it and downloads like any other file, named `encrypted-message.asc` when it arrived without a name. The PGP/MIME version part and any detached signature are held out on purpose. Every part keeps its id in the message's `encryption.parts`, and for those two the id is a correlation key only: no route returns their bytes.

Every file comes back whole in a single response, with no size cap and no range reads, so a message carrying large files makes a large response.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id the message belongs to.
- `messageId` (`string`, required): Message id from that thread's `messages`.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Array<AttachmentResource>`, each with `attachmentId`, `filename`, `contentType`, `size` and base64 `content`.

**Example**

```ts
const thread = await nextmail.threads.get('CAHk7pQ2x9LmZ4-mail.example.com')
const latest = thread.messages[thread.messages.length - 1]
const files = await nextmail.threads.listAttachments(thread.id, String(latest.id))

for (const file of files) {
    const bytes = Buffer.from(file.content ?? '', 'base64')
    console.log(file.filename, file.contentType, bytes.byteLength)
}
```

**Notes**

- When the stored bytes for a file cannot be found, `content` is an empty string rather than null, so check its length before decoding.
- Both ids must match: a real message id paired with the wrong thread id is a 404 `resource_not_found`.
- A thread delivered to no address the key covers is a 404 before the message is looked at.

Also available in: API [`GET /threads/{id}/messages/{messageId}/attachments`](https://nextmail.sh/docs/api/reference/threads#get-threads-id-messages-messageid-attachments); Python [`threads.list_attachments()`](https://nextmail.sh/docs/python/reference/threads#listAttachments); Ruby [`threads.list_attachments`](https://nextmail.sh/docs/ruby/reference/threads#listAttachments); PHP [`threads->listAttachments`](https://nextmail.sh/docs/php/reference/threads#listAttachments); Go [`Threads.ListAttachments`](https://nextmail.sh/docs/go/reference/threads#listAttachments); Java [`threads().listAttachments`](https://nextmail.sh/docs/java/reference/threads#listAttachments); C# [`Threads.ListAttachmentsAsync`](https://nextmail.sh/docs/csharp/reference/threads#listAttachments); CLI [`nextmail threads list-attachments`](https://nextmail.sh/docs/cli/reference/threads#threads-list-attachments).

### `nextmail.threads.listNotes()`

List the notes on a thread

```ts
listNotes(id: string, options?: RequestScope): Promise<Array<ThreadNoteResource>>
```

Resolves every note on one thread, the Notes panel of the reading pane: pinned notes first, then the rest in the order they were arranged.

Notes are private to a person. A key reads and writes the notes of the workspace owner, and an app those of the person who connected it. A key limited to particular addresses reaches only the notes on threads that arrived at them.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Array<ThreadNoteResource>`, each with `id`, `threadId`, `content`, `color`, `pinned`, `order`, `createdAt` and `updatedAt`.

**Example**

```ts
const notes = await nextmail.threads.listNotes('CAHk7pQ2x9LmZ4-mail.example.com')

for (const note of notes) console.log(note.pinned ? 'pinned' : '', note.content)
```

**Notes**

- A thread delivered to no address the key covers is a 404.

Also available in: API [`GET /threads/{id}/notes`](https://nextmail.sh/docs/api/reference/notes#get-threads-id-notes); Python [`threads.list_notes()`](https://nextmail.sh/docs/python/reference/threads#listNotes); Ruby [`threads.list_notes`](https://nextmail.sh/docs/ruby/reference/threads#listNotes); PHP [`threads->listNotes`](https://nextmail.sh/docs/php/reference/threads#listNotes); Go [`Threads.ListNotes`](https://nextmail.sh/docs/go/reference/threads#listNotes); Java [`threads().listNotes`](https://nextmail.sh/docs/java/reference/threads#listNotes); C# [`Threads.ListNotesAsync`](https://nextmail.sh/docs/csharp/reference/threads#listNotes); CLI [`nextmail threads list-notes`](https://nextmail.sh/docs/cli/reference/threads#threads-list-notes).

### `nextmail.threads.createNote()`

Add a note to a thread

```ts
createNote(id: string, body: ThreadNoteCreate, options?: RequestScope): Promise<ThreadNoteResource>
```

Pins a private note to a thread, as the Notes panel does. A new note goes after the others, and `pinned` keeps it at the top.

Notes are private to a person. A key reads and writes the notes of the workspace owner, and an app those of the person who connected it. A key limited to particular addresses reaches only the notes on threads that arrived at them.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `body.content` (`string`, required): The text of the note, up to 20,000 characters. Leading and trailing spaces are trimmed.
- `body.color` (`ThreadNoteColor`): One of the eight the app offers: `default`, `red`, `orange`, `yellow`, `green`, `blue`, `purple` or `pink`. Defaults to `default`.
- `body.pinned` (`boolean`): Keeps the note above the others. Defaults to false.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadNoteResource` for the new note.

**Example**

```ts
const note = await nextmail.threads.createNote('CAHk7pQ2x9LmZ4-mail.example.com', {
    content: 'Waiting on the signed contract before replying.',
    color: 'yellow',
    pinned: true
})

console.log(note.id)
```

**Notes**

- The SDK does not retry it, because a second call would add a second note.

Also available in: API [`POST /threads/{id}/notes`](https://nextmail.sh/docs/api/reference/notes#post-threads-id-notes); Python [`threads.create_note()`](https://nextmail.sh/docs/python/reference/threads#createNote); Ruby [`threads.create_note`](https://nextmail.sh/docs/ruby/reference/threads#createNote); PHP [`threads->createNote`](https://nextmail.sh/docs/php/reference/threads#createNote); Go [`Threads.CreateNote`](https://nextmail.sh/docs/go/reference/threads#createNote); Java [`threads().createNote`](https://nextmail.sh/docs/java/reference/threads#createNote); C# [`Threads.CreateNoteAsync`](https://nextmail.sh/docs/csharp/reference/threads#createNote); CLI [`nextmail threads create-note`](https://nextmail.sh/docs/cli/reference/threads#threads-create-note).

### `nextmail.threads.updateNote()`

Change a note

```ts
updateNote(id: string, noteId: string, patch: ThreadNoteUpdate, options?: RequestScope): Promise<ThreadNoteResource>
```

Changes the text of a note, its colour or whether it is pinned. Give at least one of the three. A note id that is not on this thread is a 404, even when the note exists on another thread.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `noteId` (`string`, required): The note's id, from `listNotes`.
- `patch.content` (`string`): The new text, up to 20,000 characters.
- `patch.color` (`ThreadNoteColor`): The new colour.
- `patch.pinned` (`boolean`): Pins or unpins it.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadNoteResource` as it is now.

**Example**

```ts
const note = await nextmail.threads.updateNote('CAHk7pQ2x9LmZ4-mail.example.com', 'b3d1f0c2-7a4e-4f7b-9c1d-2e8f6a5b4c3d', { pinned: false })

console.log(note.pinned)
```

**Notes**

- Safe to repeat: setting the same values twice leaves the note as it was.

Also available in: API [`PATCH /threads/{id}/notes/{noteId}`](https://nextmail.sh/docs/api/reference/notes#patch-threads-id-notes-noteid); Python [`threads.update_note()`](https://nextmail.sh/docs/python/reference/threads#updateNote); Ruby [`threads.update_note`](https://nextmail.sh/docs/ruby/reference/threads#updateNote); PHP [`threads->updateNote`](https://nextmail.sh/docs/php/reference/threads#updateNote); Go [`Threads.UpdateNote`](https://nextmail.sh/docs/go/reference/threads#updateNote); Java [`threads().updateNote`](https://nextmail.sh/docs/java/reference/threads#updateNote); C# [`Threads.UpdateNoteAsync`](https://nextmail.sh/docs/csharp/reference/threads#updateNote); CLI [`nextmail threads update-note`](https://nextmail.sh/docs/cli/reference/threads#threads-update-note).

### `nextmail.threads.deleteNote()`

Delete a note

```ts
deleteNote(id: string, noteId: string, options?: RequestScope): Promise<DeletedThreadNoteResource>
```

Deletes a note for good. There is no bin for notes.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `noteId` (`string`, required): The note's id, from `listNotes`.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`DeletedThreadNoteResource`, `{ object: 'note', id, threadId, deleted: true }`.

**Example**

```ts
await nextmail.threads.deleteNote('CAHk7pQ2x9LmZ4-mail.example.com', 'b3d1f0c2-7a4e-4f7b-9c1d-2e8f6a5b4c3d')
```

**Notes**

- The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.

Also available in: API [`DELETE /threads/{id}/notes/{noteId}`](https://nextmail.sh/docs/api/reference/notes#delete-threads-id-notes-noteid); Python [`threads.delete_note()`](https://nextmail.sh/docs/python/reference/threads#deleteNote); Ruby [`threads.delete_note`](https://nextmail.sh/docs/ruby/reference/threads#deleteNote); PHP [`threads->deleteNote`](https://nextmail.sh/docs/php/reference/threads#deleteNote); Go [`Threads.DeleteNote`](https://nextmail.sh/docs/go/reference/threads#deleteNote); Java [`threads().deleteNote`](https://nextmail.sh/docs/java/reference/threads#deleteNote); C# [`Threads.DeleteNoteAsync`](https://nextmail.sh/docs/csharp/reference/threads#deleteNote); CLI [`nextmail threads delete-note`](https://nextmail.sh/docs/cli/reference/threads#threads-delete-note).

### `nextmail.threads.reorderNotes()`

Arrange the notes on a thread

```ts
reorderNotes(id: string, ids: Array<string>, options?: RequestScope): Promise<Array<ThreadNoteResource>>
```

Sets the order of every note on a thread at once, first to last, as dragging them in the Notes panel does. Pinned notes still come first.

`ids` has to name every note on the thread exactly once. Anything else is a 422 `invalid_parameter` on `ids`, and nothing moves.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `ids` (`Array<string>`, required): Every note id on the thread, in the order you want them.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Array<ThreadNoteResource>` in the new order.

**Example**

```ts
const notes = await nextmail.threads.listNotes('CAHk7pQ2x9LmZ4-mail.example.com')
const reversed = notes.map((note) => note.id).reverse()

await nextmail.threads.reorderNotes('CAHk7pQ2x9LmZ4-mail.example.com', reversed)
```

**Notes**

- Safe to repeat: the same order twice leaves the notes as they were.

Also available in: API [`POST /threads/{id}/notes/reorder`](https://nextmail.sh/docs/api/reference/notes#post-threads-id-notes-reorder); Python [`threads.reorder_notes()`](https://nextmail.sh/docs/python/reference/threads#reorderNotes); Ruby [`threads.reorder_notes`](https://nextmail.sh/docs/ruby/reference/threads#reorderNotes); PHP [`threads->reorderNotes`](https://nextmail.sh/docs/php/reference/threads#reorderNotes); Go [`Threads.ReorderNotes`](https://nextmail.sh/docs/go/reference/threads#reorderNotes); Java [`threads().reorderNotes`](https://nextmail.sh/docs/java/reference/threads#reorderNotes); C# [`Threads.ReorderNotesAsync`](https://nextmail.sh/docs/csharp/reference/threads#reorderNotes); CLI [`nextmail threads reorder-notes`](https://nextmail.sh/docs/cli/reference/threads#threads-reorder-notes).

### `nextmail.threads.counts()`

Count the mail in each folder

```ts
counts(options?: ThreadCountsOptions): Promise<MailboxCountsResource>
```

Resolves what the sidebar of the app shows: how many conversations each folder holds and how many of them are unread, how many drafts are waiting, how many conversations each of your labels has in each folder, how many inbox conversations sit in each inbox tab, and how many inbox conversations arrived at each address of the workspace.

`folders` has one row per folder, named by its label id in lower case: `inbox`, `sent`, `spam`, `archive`, `trash` and `snoozed`, then `draft`, whose `count` is the drafts waiting to be sent and whose `unread` is always 0, then `unread`, whose `count` is the unread conversations in the inbox. `labels` has one row for each of your labels and each folder it has conversations in, `{ id, folder, count, unread }`, so a label with nothing in a folder has no row for it. `categories` has one row for each inbox tab, `{ category, count, unread }`, counting inbox conversations only: `primary`, `promotions`, `updates`, `social` and `forums`, where `primary` is everything not sorted into another tab. `unlabelled` counts the inbox conversations that carry none of your labels, with `count`, `unread` and the same count for each tab under `categories`, which is what the inbox of the app lists unless Labelled mail is ticked in its Filters menu. `addresses` counts the inbox per address the mail was delivered to, with `null` for mail that recorded none. `team` counts the team views of a shared inbox for the person the key or app acts for: `assigned` the open conversations assigned to them, `unassigned` the open conversations nobody has, each with `count` and `unread`, and `mentions` the conversations outside Spam and the bin where a team comment mentions them, with `count`, `unread` and `unseen`, the conversations with a mention newer than the last time they opened its comments in the app. A conversation is open while it is in the inbox.

A key limited to particular addresses counts only the mail that arrived at them and the drafts written from them. `address` narrows every count to one address, and one the key does not reach counts nothing rather than failing.

Scopes: `threads:read`.

**Parameters**

- `options.address` (`string`): Count only the mail delivered to this address.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`MailboxCountsResource`, `{ object: 'mailbox_counts', folders, addresses, labels, categories, unlabelled, team }`, each folder `{ label, count, unread }`, each address `{ address, count }`, each label `{ id, folder, count, unread }` and each category `{ category, count, unread }`. `unlabelled` is `{ count, unread, categories }` for the inbox conversations with no label. `team` is `{ assigned, unassigned, mentions }`, each `{ count, unread }`, and `mentions` also has `unseen`.

**Example**

```ts
const counts = await nextmail.threads.counts()

const inbox = counts.folders.find((folder) => folder.label === 'inbox')

console.log(`${inbox?.unread ?? 0} unread of ${inbox?.count ?? 0}`)

for (const row of counts.addresses) console.log(row.address ?? '(none)', row.count)

for (const row of counts.labels.filter((label) => label.folder === 'inbox')) console.log(row.id, row.unread)

for (const tab of counts.categories) console.log(tab.category, tab.count, tab.unread)

console.log(`${counts.team.unassigned.count} open and nobody has them`)
```

**Notes**

- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /threads/counts`](https://nextmail.sh/docs/api/reference/threads#get-threads-counts); Python [`threads.counts()`](https://nextmail.sh/docs/python/reference/threads#counts); Ruby [`threads.counts`](https://nextmail.sh/docs/ruby/reference/threads#counts); PHP [`threads->counts`](https://nextmail.sh/docs/php/reference/threads#counts); Go [`Threads.Counts`](https://nextmail.sh/docs/go/reference/threads#counts); Java [`threads().counts`](https://nextmail.sh/docs/java/reference/threads#counts); C# [`Threads.CountsAsync`](https://nextmail.sh/docs/csharp/reference/threads#counts); CLI [`nextmail threads counts`](https://nextmail.sh/docs/cli/reference/threads#threads-counts).

### `nextmail.threads.summary()`

Read the summary of a thread

```ts
summary(id: string, options?: RequestScope): Promise<ThreadAiSummaryResource>
```

Resolves the short AI summary the reading pane shows above a thread. A summary is written the first time it is asked for and then kept, and the first request after a newer message arrives writes it again, so reading one is cheap.

`state` is `ready` with the text in `summary`, `pending` while the first one is being written, `failed` when the last try did not work, or `none` when there is nothing to summarise, such as a thread holding a message that arrived encrypted, whose body Nextmail never reads. While a newer summary is being written, the one before it comes back as `ready`. A `pending` answer starts the writing, so ask again a few seconds later, and after a `failed` one asking again tries once more.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadAiSummaryResource`, `{ object: 'thread_summary', threadId, state, summary }`, with `summary` null unless `state` is `ready`.

**Example**

```ts
let result = await nextmail.threads.summary('CAHk7pQ2x9LmZ4-mail.example.com')

if (result.state === 'pending') {
    await new Promise((resolve) => setTimeout(resolve, 5000))
    result = await nextmail.threads.summary('CAHk7pQ2x9LmZ4-mail.example.com')
}

console.log(result.summary ?? result.state)
```

**Notes**

- Summaries spend no AI actions, whether one is written or only read.
- A thread delivered to no address the key covers is a 404.

Also available in: API [`GET /threads/{id}/summary`](https://nextmail.sh/docs/api/reference/threads#get-threads-id-summary); Python [`threads.summary()`](https://nextmail.sh/docs/python/reference/threads#summary); Ruby [`threads.summary`](https://nextmail.sh/docs/ruby/reference/threads#summary); PHP [`threads->summary`](https://nextmail.sh/docs/php/reference/threads#summary); Go [`Threads.Summary`](https://nextmail.sh/docs/go/reference/threads#summary); Java [`threads().summary`](https://nextmail.sh/docs/java/reference/threads#summary); C# [`Threads.SummaryAsync`](https://nextmail.sh/docs/csharp/reference/threads#summary); CLI [`nextmail threads summary`](https://nextmail.sh/docs/cli/reference/threads#threads-summary).

### `nextmail.threads.replySuggestions()`

Suggest replies to a thread

```ts
replySuggestions(id: string, options?: RequestScope): Promise<ReplySuggestionsResource>
```

Resolves up to three short replies to the latest message of the thread, the ones the reading pane offers under it. Each has a `label`, a few words that say what it answers, and a `body`, the reply itself in plain text, ready to send with `emails.send` or to keep with `drafts.create`. They are written in the language of that message and the voice of the mail this workspace sends, from the conversation, what was written to this correspondent before, what is known about their organisation and, when the message asks for a time, the busy times of the calendar.

Suggestions are written once for each new message and kept, so asking again is free until another message arrives. `state` is `ready` with the suggestions, `pending` while they are being written, so ask again a few seconds later, or `none` when the message needs no reply: your own reply came last, or it is a newsletter, an automated notice, mail from a no-reply address, spam or an encrypted message. Every thread is `none` in a workspace that turned `replySuggestions` off with `settings.update`.

`sources` names the knowledge base items the suggestions drew on: the pinned notes at the levels of the receiving address and the items whose passages matched the message, each a `KnowledgeSourceRef` with `id`, `title`, `kind`, `scope` and `url`. It is empty when none were used.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ReplySuggestionsResource`, `{ object: 'reply_suggestions', threadId, messageId, state, suggestions, sources }`, with `messageId` naming the message they answer, `suggestions` empty unless `state` is `ready` and `sources` the knowledge base items they drew on.

**Example**

```ts
let result = await nextmail.threads.replySuggestions('CAHk7pQ2x9LmZ4-mail.example.com')

if (result.state === 'pending') {
    await new Promise((resolve) => setTimeout(resolve, 5000))
    result = await nextmail.threads.replySuggestions('CAHk7pQ2x9LmZ4-mail.example.com')
}

for (const suggestion of result.suggestions) {
    console.log(`${suggestion.label}: ${suggestion.body}`)
}
```

**Notes**

- Writing suggestions spends none of the workspace's AI actions. A workspace has suggestions written for a limited number of new messages a day, and past it a thread reads `none` until the next day.
- A thread delivered to no address the key covers is a 404.
- A server without AI answers 409 `ai_not_configured`.

Also available in: API [`GET /threads/{id}/reply-suggestions`](https://nextmail.sh/docs/api/reference/threads#get-threads-id-reply-suggestions); Python [`threads.reply_suggestions()`](https://nextmail.sh/docs/python/reference/threads#replySuggestions); Ruby [`threads.reply_suggestions`](https://nextmail.sh/docs/ruby/reference/threads#replySuggestions); PHP [`threads->replySuggestions`](https://nextmail.sh/docs/php/reference/threads#replySuggestions); Go [`Threads.ReplySuggestions`](https://nextmail.sh/docs/go/reference/threads#replySuggestions); Java [`threads().replySuggestions`](https://nextmail.sh/docs/java/reference/threads#replySuggestions); C# [`Threads.ReplySuggestionsAsync`](https://nextmail.sh/docs/csharp/reference/threads#replySuggestions); CLI [`nextmail threads reply-suggestions`](https://nextmail.sh/docs/cli/reference/threads#threads-reply-suggestions).

### `nextmail.threads.restore()`

Take a thread out of the Bin

```ts
restore(id: string, options?: RequestScope): Promise<RestoredThreadResource>
```

Puts a thread back in the inbox, out of the Bin and out of Spam, which is what Restore from Bin and Move to inbox do in the app. It is the undo of `trash`.

Calling it on a thread that is already in the inbox changes nothing and returns the same body, which is why the SDK retries it.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`RestoredThreadResource`, `{ object: 'thread', id, restored: true }`.

**Example**

```ts
await nextmail.threads.trash('CAHk7pQ2x9LmZ4-mail.example.com')

const restored = await nextmail.threads.restore('CAHk7pQ2x9LmZ4-mail.example.com')

console.log(restored.restored)
```

**Notes**

- A thread that was archived before it went to the Bin comes back to the inbox, not to the archive.
- A thread delivered to no address the key covers is a 404.

Also available in: API [`POST /threads/{id}/restore`](https://nextmail.sh/docs/api/reference/threads#post-threads-id-restore); Python [`threads.restore()`](https://nextmail.sh/docs/python/reference/threads#restore); Ruby [`threads.restore`](https://nextmail.sh/docs/ruby/reference/threads#restore); PHP [`threads->restore`](https://nextmail.sh/docs/php/reference/threads#restore); Go [`Threads.Restore`](https://nextmail.sh/docs/go/reference/threads#restore); Java [`threads().restore`](https://nextmail.sh/docs/java/reference/threads#restore); C# [`Threads.RestoreAsync`](https://nextmail.sh/docs/csharp/reference/threads#restore); CLI [`nextmail threads restore`](https://nextmail.sh/docs/cli/reference/threads#threads-restore).

### `nextmail.threads.delete()`

Delete a thread for good

```ts
delete(id: string, options?: RequestScope): Promise<DeletedThreadResource>
```

Deletes every message in a thread, with its attachments, which is what Delete from Bin does in the app. It cannot be undone.

It works on a thread in any folder, so call `trash` instead when you only mean to throw the thread away: a trashed thread stays readable and `restore` brings it back.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`DeletedThreadResource`, `{ object: 'thread', id, deleted: true }`.

**Example**

```ts
await nextmail.threads.delete('CAHk7pQ2x9LmZ4-mail.example.com')
```

**Notes**

- The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.
- A 500 `thread_delete_failed` means nothing was removed, so the call is safe to repeat.
- A thread delivered to no address the key covers is a 404.

Also available in: API [`DELETE /threads/{id}`](https://nextmail.sh/docs/api/reference/threads#delete-threads-id); Python [`threads.delete()`](https://nextmail.sh/docs/python/reference/threads#delete); Ruby [`threads.delete`](https://nextmail.sh/docs/ruby/reference/threads#delete); PHP [`threads->delete`](https://nextmail.sh/docs/php/reference/threads#delete); Go [`Threads.Delete`](https://nextmail.sh/docs/go/reference/threads#delete); Java [`threads().delete`](https://nextmail.sh/docs/java/reference/threads#delete); C# [`Threads.DeleteAsync`](https://nextmail.sh/docs/csharp/reference/threads#delete); CLI [`nextmail threads delete`](https://nextmail.sh/docs/cli/reference/threads#threads-delete).

### `nextmail.threads.unsubscribe()`

Unsubscribe from the sender of a thread

```ts
unsubscribe(id: string, options?: RequestScope): Promise<UnsubscribeResultResource>
```

Does what the Unsubscribe button of the reading pane does: finds the subscription behind the newest message of the thread that carries a `List-Unsubscribe` header and unsubscribes from it the way the sender asks for, with a one-click request or an unsubscribe email. A sender that only offers a page cannot be unsubscribed by a program, so `method` is `link` and `url` is the page a person has to open.

A thread with no unsubscribe header is a 422 `unsubscribe_unsupported`, and a thread delivered to no address the key covers is a 404.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`UnsubscribeResultResource`, `{ object: 'unsubscribe', threadId, subscriptionId, method, url, binned }`.

**Example**

```ts
const result = await nextmail.threads.unsubscribe('CAHk7pQ2x9LmZ4-mail.example.com')

console.log(result.method)
```

**Notes**

- The SDK does not retry it, because a second call can send a second unsubscribe email.

Also available in: API [`POST /threads/{id}/unsubscribe`](https://nextmail.sh/docs/api/reference/subscriptions#post-threads-id-unsubscribe); Python [`threads.unsubscribe()`](https://nextmail.sh/docs/python/reference/threads#unsubscribe); Ruby [`threads.unsubscribe`](https://nextmail.sh/docs/ruby/reference/threads#unsubscribe); PHP [`threads->unsubscribe`](https://nextmail.sh/docs/php/reference/threads#unsubscribe); Go [`Threads.Unsubscribe`](https://nextmail.sh/docs/go/reference/threads#unsubscribe); Java [`threads().unsubscribe`](https://nextmail.sh/docs/java/reference/threads#unsubscribe); C# [`Threads.UnsubscribeAsync`](https://nextmail.sh/docs/csharp/reference/threads#unsubscribe); CLI [`nextmail threads unsubscribe`](https://nextmail.sh/docs/cli/reference/threads#threads-unsubscribe).

### `nextmail.threads.getEvent()`

Read the event a thread carries

```ts
getEvent(id: string, options?: RequestScope): Promise<CalendarEventResource>
```

Returns the calendar event that came with an invitation in the thread, as the invitation card of the reading pane shows it. Answer it with `calendar.respondToEvent`.

Scopes: `calendar:read`.

**Parameters**

- `id` (`string`, required): Thread id, as `threads.list` returns it.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`CalendarEventResource`, as `calendar.getEvent` returns it.

**Example**

```ts
const event = await nextmail.threads.getEvent('thr_6d1a9c4e2b7f30d85e1c4a92')

console.log(event.summary, event.start)
```

**Notes**

- A thread with no event, or one the key does not reach, is a 404.

Also available in: API [`GET /threads/{id}/event`](https://nextmail.sh/docs/api/reference/calendar#get-threads-id-event); Python [`threads.get_event()`](https://nextmail.sh/docs/python/reference/threads#getEvent); Ruby [`threads.get_event`](https://nextmail.sh/docs/ruby/reference/threads#getEvent); PHP [`threads->getEvent`](https://nextmail.sh/docs/php/reference/threads#getEvent); Go [`Threads.GetEvent`](https://nextmail.sh/docs/go/reference/threads#getEvent); Java [`threads().getEvent`](https://nextmail.sh/docs/java/reference/threads#getEvent); C# [`Threads.GetEventAsync`](https://nextmail.sh/docs/csharp/reference/threads#getEvent); CLI [`nextmail threads get-event`](https://nextmail.sh/docs/cli/reference/threads#threads-get-event).

### `nextmail.threads.listSenderCategories()`

List the senders that always go to one inbox tab

```ts
listSenderCategories(options?: RequestScope): Promise<Array<SenderCategoryResource>>
```

Resolves the senders whose mail always goes to one inbox tab, by address. New mail is sorted into Primary, Promotions, Updates, Social or Forums by plain rules, and a choice saved here wins over them. `list` takes `category` to read one tab.

The choices belong to the workspace and apply to every address in it. A workspace holds at most 2,000 of them, and they come back as one plain array with no paging.

Scopes: `threads:read`.

**Parameters**

- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Array<SenderCategoryResource>`, each `{ object: 'sender_category', sender, category, updatedAt }`, with `sender` in lower case and `category` the tab their mail goes to.

**Example**

```ts
const choices = await nextmail.threads.listSenderCategories()

for (const choice of choices) console.log(choice.sender, choice.category)
```

**Notes**

- A key or an app limited to particular addresses or domains can read the choices but not change them.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /sender-categories`](https://nextmail.sh/docs/api/reference/threads#get-sender-categories); Python [`threads.list_sender_categories()`](https://nextmail.sh/docs/python/reference/threads#listSenderCategories); Ruby [`threads.list_sender_categories`](https://nextmail.sh/docs/ruby/reference/threads#listSenderCategories); PHP [`threads->listSenderCategories`](https://nextmail.sh/docs/php/reference/threads#listSenderCategories); Go [`Threads.ListSenderCategories`](https://nextmail.sh/docs/go/reference/threads#listSenderCategories); Java [`threads().listSenderCategories`](https://nextmail.sh/docs/java/reference/threads#listSenderCategories); C# [`Threads.ListSenderCategoriesAsync`](https://nextmail.sh/docs/csharp/reference/threads#listSenderCategories); CLI [`nextmail threads list-sender-categories`](https://nextmail.sh/docs/cli/reference/threads#threads-list-sender-categories).

### `nextmail.threads.setSenderCategory()`

Always sort a sender into one inbox tab

```ts
setSenderCategory(email: string, category: InboxCategory, options?: RequestScope): Promise<SenderCategoryResource>
```

Saves the tab for one sender and moves the inbox threads whose newest message is from them, up to the newest 1,000, into it. Mail from them is sorted there from now on, whatever the rules would say. Saving again replaces the choice.

A workspace holds at most 2,000 choices, and the call past that is a 422 `sender_category_limit_reached`. They apply to every address in the workspace, so a key or an app limited to particular addresses or domains can read them but not change them, and gets a 422 `capability_unsupported`.

Scopes: `threads:write`.

**Parameters**

- `email` (`string`, required): The address of the sender, compared without case.
- `category` (`InboxCategory`, required): The inbox tab their mail goes to: `primary`, `promotions`, `updates`, `social` or `forums`.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`SenderCategoryResource`, `{ object: 'sender_category', sender, category, updatedAt }`, with `sender` in lower case.

**Example**

```ts
const choice = await nextmail.threads.setSenderCategory('news@example.com', 'promotions')

console.log(choice.sender, choice.category, choice.updatedAt)
```

**Notes**

- An address or a category that is not valid is a 422 `invalid_parameter`.
- Choosing `primary` keeps a sender out of the other tabs, which is how to stop a newsletter you read from being sorted away.
- The SDK retries this call after a network failure, which is safe because saving the same choice twice leaves the same choice.

Also available in: API [`PUT /sender-categories/{email}`](https://nextmail.sh/docs/api/reference/threads#put-sender-categories-email); Python [`threads.set_sender_category()`](https://nextmail.sh/docs/python/reference/threads#setSenderCategory); Ruby [`threads.set_sender_category`](https://nextmail.sh/docs/ruby/reference/threads#setSenderCategory); PHP [`threads->setSenderCategory`](https://nextmail.sh/docs/php/reference/threads#setSenderCategory); Go [`Threads.SetSenderCategory`](https://nextmail.sh/docs/go/reference/threads#setSenderCategory); Java [`threads().setSenderCategory`](https://nextmail.sh/docs/java/reference/threads#setSenderCategory); C# [`Threads.SetSenderCategoryAsync`](https://nextmail.sh/docs/csharp/reference/threads#setSenderCategory); CLI [`nextmail threads set-sender-category`](https://nextmail.sh/docs/cli/reference/threads#threads-set-sender-category).

### `nextmail.threads.clearSenderCategory()`

Remove the tab chosen for a sender

```ts
clearSenderCategory(email: string, options?: RequestScope): Promise<DeletedSenderCategoryResource>
```

Removes the choice, so new mail from the sender is sorted by the rules again. Threads already sorted stay where they are.

Scopes: `threads:write`.

**Parameters**

- `email` (`string`, required): The address of the sender, compared without case.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`DeletedSenderCategoryResource`, `{ object: 'sender_category', sender, deleted: true }`, with `sender` in lower case.

**Example**

```ts
const removed = await nextmail.threads.clearSenderCategory('news@example.com')

console.log(removed.sender, removed.deleted)
```

**Notes**

- A sender with no saved choice is a 404. The SDK does not retry a delete, so a 404 on your own second attempt after a lost response means the first one worked.
- A key or an app limited to particular addresses or domains cannot change the choices, and gets a 422 `capability_unsupported`.

Also available in: API [`DELETE /sender-categories/{email}`](https://nextmail.sh/docs/api/reference/threads#delete-sender-categories-email); Python [`threads.clear_sender_category()`](https://nextmail.sh/docs/python/reference/threads#clearSenderCategory); Ruby [`threads.clear_sender_category`](https://nextmail.sh/docs/ruby/reference/threads#clearSenderCategory); PHP [`threads->clearSenderCategory`](https://nextmail.sh/docs/php/reference/threads#clearSenderCategory); Go [`Threads.ClearSenderCategory`](https://nextmail.sh/docs/go/reference/threads#clearSenderCategory); Java [`threads().clearSenderCategory`](https://nextmail.sh/docs/java/reference/threads#clearSenderCategory); C# [`Threads.ClearSenderCategoryAsync`](https://nextmail.sh/docs/csharp/reference/threads#clearSenderCategory); CLI [`nextmail threads clear-sender-category`](https://nextmail.sh/docs/cli/reference/threads#threads-clear-sender-category).

### `nextmail.threads.getTeam()`

Read who has a thread

```ts
getTeam(id: string, options?: RequestScope): Promise<ThreadTeamResource>
```

Resolves the thread as the team sees it: who it is assigned to, who marked it done, how many comments it has, who else is writing a reply in the app right now, and everyone who can see it.

Only people who can see a thread can be given it or mentioned on it: the workspace owner, members who reach every address, and members who hold its address or its domain. A key acts for the workspace owner.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadTeamResource` with `assignment`, `commentCount`, `replying` and `team`.

**Example**

```ts
const team = await nextmail.threads.getTeam('CAHk7pQ2x9LmZ4-mail.example.com')

console.log(team.assignment.assignee?.email ?? 'nobody', team.team.length)
```

**Notes**

- A thread delivered to no address the key covers is a 404.

Also available in: API [`GET /threads/{id}/team`](https://nextmail.sh/docs/api/reference/team-inbox#get-threads-id-team); Python [`threads.get_team()`](https://nextmail.sh/docs/python/reference/threads#getTeam); Ruby [`threads.get_team`](https://nextmail.sh/docs/ruby/reference/threads#getTeam); PHP [`threads->getTeam`](https://nextmail.sh/docs/php/reference/threads#getTeam); Go [`Threads.GetTeam`](https://nextmail.sh/docs/go/reference/threads#getTeam); Java [`threads().getTeam`](https://nextmail.sh/docs/java/reference/threads#getTeam); C# [`Threads.GetTeamAsync`](https://nextmail.sh/docs/csharp/reference/threads#getTeam); CLI [`nextmail threads get-team`](https://nextmail.sh/docs/cli/reference/threads#threads-get-team).

### `nextmail.threads.assign()`

Assign a thread

```ts
assign(id: string, assigneeId: string, options?: RequestScope): Promise<ThreadAssignmentResource>
```

Gives the thread to one person, who is emailed unless they assigned it to themselves.

Only people who can see a thread can be given it or mentioned on it: the workspace owner, members who reach every address, and members who hold its address or its domain. A key acts for the workspace owner.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `assigneeId` (`string`, required): The user id of the person to give it to, as `getTeam` lists them in `team`.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadAssignmentResource`, who has the thread now.

**Example**

```ts
const assignment = await nextmail.threads.assign('CAHk7pQ2x9LmZ4-mail.example.com', 'usr_2f9c1d')

console.log(assignment.assignee?.email)
```

**Notes**

- Somebody who cannot see the thread is a 422 `invalid_parameter` on `assigneeId`.

Also available in: API [`PUT /threads/{id}/assignee`](https://nextmail.sh/docs/api/reference/team-inbox#put-threads-id-assignee); Python [`threads.assign()`](https://nextmail.sh/docs/python/reference/threads#assign); Ruby [`threads.assign`](https://nextmail.sh/docs/ruby/reference/threads#assign); PHP [`threads->assign`](https://nextmail.sh/docs/php/reference/threads#assign); Go [`Threads.Assign`](https://nextmail.sh/docs/go/reference/threads#assign); Java [`threads().assign`](https://nextmail.sh/docs/java/reference/threads#assign); C# [`Threads.AssignAsync`](https://nextmail.sh/docs/csharp/reference/threads#assign); CLI [`nextmail threads assign`](https://nextmail.sh/docs/cli/reference/threads#threads-assign).

### `nextmail.threads.unassign()`

Unassign a thread

```ts
unassign(id: string, options?: RequestScope): Promise<ThreadAssignmentResource>
```

Leaves the thread with nobody.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadAssignmentResource` with `assignee` null.

**Example**

```ts
await nextmail.threads.unassign('CAHk7pQ2x9LmZ4-mail.example.com')
```

Also available in: API [`DELETE /threads/{id}/assignee`](https://nextmail.sh/docs/api/reference/team-inbox#delete-threads-id-assignee); Python [`threads.unassign()`](https://nextmail.sh/docs/python/reference/threads#unassign); Ruby [`threads.unassign`](https://nextmail.sh/docs/ruby/reference/threads#unassign); PHP [`threads->unassign`](https://nextmail.sh/docs/php/reference/threads#unassign); Go [`Threads.Unassign`](https://nextmail.sh/docs/go/reference/threads#unassign); Java [`threads().unassign`](https://nextmail.sh/docs/java/reference/threads#unassign); C# [`Threads.UnassignAsync`](https://nextmail.sh/docs/csharp/reference/threads#unassign); CLI [`nextmail threads unassign`](https://nextmail.sh/docs/cli/reference/threads#threads-unassign).

### `nextmail.threads.markDone()`

Mark a thread done

```ts
markDone(id: string, options?: RequestScope): Promise<ThreadAssignmentResource>
```

Archives the thread for everyone and records who marked it done. A new reply brings it back to the inbox, and `reopen` moves it back by hand.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadAssignmentResource` with `doneBy` and `doneAt`.

**Example**

```ts
const assignment = await nextmail.threads.markDone('CAHk7pQ2x9LmZ4-mail.example.com')

console.log(assignment.doneAt)
```

Also available in: API [`POST /threads/{id}/done`](https://nextmail.sh/docs/api/reference/team-inbox#post-threads-id-done); Python [`threads.mark_done()`](https://nextmail.sh/docs/python/reference/threads#markDone); Ruby [`threads.mark_done`](https://nextmail.sh/docs/ruby/reference/threads#markDone); PHP [`threads->markDone`](https://nextmail.sh/docs/php/reference/threads#markDone); Go [`Threads.MarkDone`](https://nextmail.sh/docs/go/reference/threads#markDone); Java [`threads().markDone`](https://nextmail.sh/docs/java/reference/threads#markDone); C# [`Threads.MarkDoneAsync`](https://nextmail.sh/docs/csharp/reference/threads#markDone); CLI [`nextmail threads mark-done`](https://nextmail.sh/docs/cli/reference/threads#threads-mark-done).

### `nextmail.threads.reopen()`

Reopen a thread

```ts
reopen(id: string, options?: RequestScope): Promise<ThreadAssignmentResource>
```

Moves a done thread back to the inbox for everyone.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadAssignmentResource` with `doneBy` and `doneAt` null.

**Example**

```ts
await nextmail.threads.reopen('CAHk7pQ2x9LmZ4-mail.example.com')
```

Also available in: API [`POST /threads/{id}/reopen`](https://nextmail.sh/docs/api/reference/team-inbox#post-threads-id-reopen); Python [`threads.reopen()`](https://nextmail.sh/docs/python/reference/threads#reopen); Ruby [`threads.reopen`](https://nextmail.sh/docs/ruby/reference/threads#reopen); PHP [`threads->reopen`](https://nextmail.sh/docs/php/reference/threads#reopen); Go [`Threads.Reopen`](https://nextmail.sh/docs/go/reference/threads#reopen); Java [`threads().reopen`](https://nextmail.sh/docs/java/reference/threads#reopen); C# [`Threads.ReopenAsync`](https://nextmail.sh/docs/csharp/reference/threads#reopen); CLI [`nextmail threads reopen`](https://nextmail.sh/docs/cli/reference/threads#threads-reopen).

### `nextmail.threads.listComments()`

List the comments on a thread

```ts
listComments(id: string, options?: RequestScope): Promise<Array<ThreadCommentResource>>
```

Resolves the team comments on one thread, oldest first. Everyone who can see the thread reads them, and the sender never does.

Scopes: `threads:read`.

**Parameters**

- `id` (`string`, required): Thread id.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`Array<ThreadCommentResource>`, each with `author`, `body`, `mentions`, `createdAt` and `editedAt`.

**Example**

```ts
const comments = await nextmail.threads.listComments('CAHk7pQ2x9LmZ4-mail.example.com')

for (const comment of comments) console.log(comment.author?.email, comment.body)
```

**Notes**

- A thread delivered to no address the key covers is a 404.

Also available in: API [`GET /threads/{id}/comments`](https://nextmail.sh/docs/api/reference/team-inbox#get-threads-id-comments); Python [`threads.list_comments()`](https://nextmail.sh/docs/python/reference/threads#listComments); Ruby [`threads.list_comments`](https://nextmail.sh/docs/ruby/reference/threads#listComments); PHP [`threads->listComments`](https://nextmail.sh/docs/php/reference/threads#listComments); Go [`Threads.ListComments`](https://nextmail.sh/docs/go/reference/threads#listComments); Java [`threads().listComments`](https://nextmail.sh/docs/java/reference/threads#listComments); C# [`Threads.ListCommentsAsync`](https://nextmail.sh/docs/csharp/reference/threads#listComments); CLI [`nextmail threads list-comments`](https://nextmail.sh/docs/cli/reference/threads#threads-list-comments).

### `nextmail.threads.createComment()`

Comment on a thread

```ts
createComment(id: string, body: ThreadCommentCreate, options?: RequestScope): Promise<ThreadCommentResource>
```

Adds a comment for the team. Everybody in `mentions` is emailed.

Only people who can see a thread can be given it or mentioned on it: the workspace owner, members who reach every address, and members who hold its address or its domain. A key acts for the workspace owner.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `body.body` (`string`, required): What the comment says, up to 10,000 characters. Leading and trailing spaces are trimmed.
- `body.mentions` (`Array<string>`): The user ids of people to mention, at most 20. Each has to be able to see the thread.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadCommentResource` for the new comment.

**Example**

```ts
const comment = await nextmail.threads.createComment('CAHk7pQ2x9LmZ4-mail.example.com', {
    body: 'Can you take this one?',
    mentions: ['usr_2f9c1d']
})

console.log(comment.id)
```

**Notes**

- The SDK does not retry it, because a second call would add a second comment.
- Somebody in `mentions` who cannot see the thread is a 422 `invalid_parameter` on `mentions`.

Also available in: API [`POST /threads/{id}/comments`](https://nextmail.sh/docs/api/reference/team-inbox#post-threads-id-comments); Python [`threads.create_comment()`](https://nextmail.sh/docs/python/reference/threads#createComment); Ruby [`threads.create_comment`](https://nextmail.sh/docs/ruby/reference/threads#createComment); PHP [`threads->createComment`](https://nextmail.sh/docs/php/reference/threads#createComment); Go [`Threads.CreateComment`](https://nextmail.sh/docs/go/reference/threads#createComment); Java [`threads().createComment`](https://nextmail.sh/docs/java/reference/threads#createComment); C# [`Threads.CreateCommentAsync`](https://nextmail.sh/docs/csharp/reference/threads#createComment); CLI [`nextmail threads create-comment`](https://nextmail.sh/docs/cli/reference/threads#threads-create-comment).

### `nextmail.threads.updateComment()`

Change a comment

```ts
updateComment(id: string, commentId: string, body: ThreadCommentCreate, options?: RequestScope): Promise<ThreadCommentResource>
```

Replaces the text and the mentions of a comment. Only its author can change it, and anybody newly mentioned is emailed.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `commentId` (`string`, required): The comment id, as `listComments` returns it.
- `body.body` (`string`, required): The new text, up to 10,000 characters.
- `body.mentions` (`Array<string>`): The user ids it mentions now, at most 20. Leave it out to mention nobody.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`ThreadCommentResource` as it is now.

**Example**

```ts
await nextmail.threads.updateComment('CAHk7pQ2x9LmZ4-mail.example.com', 'tcm_8a1f', {
    body: 'Done, sent the quote.'
})
```

**Notes**

- Someone else's comment, or one on another thread, is a 404.

Also available in: API [`PATCH /threads/{id}/comments/{commentId}`](https://nextmail.sh/docs/api/reference/team-inbox#patch-threads-id-comments-commentid); Python [`threads.update_comment()`](https://nextmail.sh/docs/python/reference/threads#updateComment); Ruby [`threads.update_comment`](https://nextmail.sh/docs/ruby/reference/threads#updateComment); PHP [`threads->updateComment`](https://nextmail.sh/docs/php/reference/threads#updateComment); Go [`Threads.UpdateComment`](https://nextmail.sh/docs/go/reference/threads#updateComment); Java [`threads().updateComment`](https://nextmail.sh/docs/java/reference/threads#updateComment); C# [`Threads.UpdateCommentAsync`](https://nextmail.sh/docs/csharp/reference/threads#updateComment); CLI [`nextmail threads update-comment`](https://nextmail.sh/docs/cli/reference/threads#threads-update-comment).

### `nextmail.threads.deleteComment()`

Delete a comment

```ts
deleteComment(id: string, commentId: string, options?: RequestScope): Promise<DeletedThreadCommentResource>
```

Deletes a comment for good. Its author can delete it, and so can the workspace owner, which a key acts for.

Scopes: `threads:write`.

**Parameters**

- `id` (`string`, required): Thread id.
- `commentId` (`string`, required): The comment id, as `listComments` returns it.
- `options.signal` (`AbortSignal`): Cancels the request.
- `options.apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

`DeletedThreadCommentResource` with `deleted: true`.

**Example**

```ts
await nextmail.threads.deleteComment('CAHk7pQ2x9LmZ4-mail.example.com', 'tcm_8a1f')
```

Also available in: API [`DELETE /threads/{id}/comments/{commentId}`](https://nextmail.sh/docs/api/reference/team-inbox#delete-threads-id-comments-commentid); Python [`threads.delete_comment()`](https://nextmail.sh/docs/python/reference/threads#deleteComment); Ruby [`threads.delete_comment`](https://nextmail.sh/docs/ruby/reference/threads#deleteComment); PHP [`threads->deleteComment`](https://nextmail.sh/docs/php/reference/threads#deleteComment); Go [`Threads.DeleteComment`](https://nextmail.sh/docs/go/reference/threads#deleteComment); Java [`threads().deleteComment`](https://nextmail.sh/docs/java/reference/threads#deleteComment); C# [`Threads.DeleteCommentAsync`](https://nextmail.sh/docs/csharp/reference/threads#deleteComment); CLI [`nextmail threads delete-comment`](https://nextmail.sh/docs/cli/reference/threads#threads-delete-comment).
