---
title: "Labels"
description: "Every operation in this group: what it accepts, what it returns and the errors it can answer with."
url: "https://nextmail.sh/docs/api/reference/labels"
area: "API"
category: "Reference"
---

# Labels

Every operation in this group: what it accepts, what it returns and the errors it can answer with.

## Operations

The names a conversation can be filed under, several at once. Labels belong to the workspace, so every member and every key sees the same set, and renaming or deleting one changes it for everybody.

A label's id comes from the name it was created with and never changes, so a rename keeps every conversation labelled. Its colour is a hex value or one of seven gradient tokens, and `GET /labels/colors` lists the palette the app offers. Apply and remove labels on a conversation with `PATCH /threads/{id}`. `GET /threads?folder=USER_BIG_CLIENTS` lists every conversation carrying a label, and `labelIds` narrows a folder to the ones carrying it.

### `GET /labels`

List labels

Every user label in the workspace, sorted by name and then by id, a page at a time: follow `nextCursor` while `hasMore` is true to read them all. Each row carries its colour, how many conversations carry it and when it was created and last changed, which is everything the Labels table in the app shows.

System labels such as `INBOX`, `STARRED` and `UNREAD` are not listed. A thread carries them and `PATCH /threads/{id}` takes them, but they cannot be renamed, recoloured or deleted.

The order the app's sidebar shows is each person's own arrangement and is not exposed here.

Requires the `labels:read` scope.

- Scopes: `labels:read`.

**Query parameters**

- `limit` (`integer`, at least 1, at most 100, default `25`): Rows per page, 1 to 100.
- `cursor` (`string`): The previous page's `nextCursor`, passed back as it came. It is opaque: it holds where the last row sat in this list's order, so a row deleted or edited between pages never breaks the walk, and the next page starts at the first row that sorts after it. A value this list did not hand out is a 400 `invalid_cursor`.

**Returns**

- `200` `LabelList`: A page of labels, by name.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.list()`](https://nextmail.sh/docs/sdk/reference/labels#list), [`nextmail.labels.listAll()`](https://nextmail.sh/docs/sdk/reference/labels#listAll), [`nextmail.labels.iterate()`](https://nextmail.sh/docs/sdk/reference/labels#iterate); Python [`labels.list()`](https://nextmail.sh/docs/python/reference/labels#list), [`labels.list_all()`](https://nextmail.sh/docs/python/reference/labels#listAll), [`labels.iterate()`](https://nextmail.sh/docs/python/reference/labels#iterate); Ruby [`labels.list`](https://nextmail.sh/docs/ruby/reference/labels#list), [`labels.list_all`](https://nextmail.sh/docs/ruby/reference/labels#listAll), [`labels.iterate`](https://nextmail.sh/docs/ruby/reference/labels#iterate); PHP [`labels->list`](https://nextmail.sh/docs/php/reference/labels#list), [`labels->listAll`](https://nextmail.sh/docs/php/reference/labels#listAll), [`labels->iterate`](https://nextmail.sh/docs/php/reference/labels#iterate); Go [`Labels.List`](https://nextmail.sh/docs/go/reference/labels#list), [`Labels.ListAll`](https://nextmail.sh/docs/go/reference/labels#listAll), [`Labels.Iterate`](https://nextmail.sh/docs/go/reference/labels#iterate); Java [`labels().list`](https://nextmail.sh/docs/java/reference/labels#list), [`labels().listAll`](https://nextmail.sh/docs/java/reference/labels#listAll), [`labels().iterate`](https://nextmail.sh/docs/java/reference/labels#iterate); C# [`Labels.ListAsync`](https://nextmail.sh/docs/csharp/reference/labels#list), [`Labels.ListAllAsync`](https://nextmail.sh/docs/csharp/reference/labels#listAll), [`Labels.IterateAsync`](https://nextmail.sh/docs/csharp/reference/labels#iterate); CLI [`nextmail labels list`](https://nextmail.sh/docs/cli/reference/labels#labels-list); MCP [`getLabel`](https://nextmail.sh/docs/mcp/tools/organising#getLabel), [`getUserLabels`](https://nextmail.sh/docs/mcp/tools/organising#getUserLabels).

### `POST /labels`

Create a label

Creates a label and answers with it. The id comes from the name (`Big Clients` becomes `USER_BIG_CLIENTS`) and never changes afterwards. A workspace holds at most 50 labels.

Leave `color` out for a label with no colour, or send `color.backgroundColor` as a hex value or a gradient token. `GET /labels/colors` lists the swatches the app offers.

Requires the `labels:write` scope.

- Scopes: `labels:write`.

**Request body**

- `name` (`string`, required, 1 to 225 characters): Trimmed, then 1 to 225 characters. Also decides the id, which never changes afterwards.
- `color` (`LabelColorInput`): The colour. Leave it out for a label with no colour.
- `aiInstructions` (`string`, nullable, up to 500 characters): What the label is for, in plain words, up to 500 characters, such as `invoices and receipts`.
- `aiEnabled` (`boolean`): Have AI apply the label to arriving mail that fits `aiInstructions`. It needs instructions and a paid plan, and a workspace may have 10 such labels.

**Returns**

- `201` `Label`: Created, read back as it is stored.

**Errors**

- `409`: `label_name_taken`: another label already has that name, compared without case, or already holds the id this name gives because it was created under it and renamed since.
- `422`: `label_limit_reached` once the workspace holds 50 labels, or `invalid_parameter` for a colour that is neither a hex value nor a gradient token.
- The errors every operation can return: `400`, `401`, `403`, `404`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.create()`](https://nextmail.sh/docs/sdk/reference/labels#create); Python [`labels.create()`](https://nextmail.sh/docs/python/reference/labels#create); Ruby [`labels.create`](https://nextmail.sh/docs/ruby/reference/labels#create); PHP [`labels->create`](https://nextmail.sh/docs/php/reference/labels#create); Go [`Labels.Create`](https://nextmail.sh/docs/go/reference/labels#create); Java [`labels().create`](https://nextmail.sh/docs/java/reference/labels#create); C# [`Labels.CreateAsync`](https://nextmail.sh/docs/csharp/reference/labels#create); CLI [`nextmail labels create`](https://nextmail.sh/docs/cli/reference/labels#labels-create); MCP [`createLabel`](https://nextmail.sh/docs/mcp/tools/organising#createLabel).

### `GET /labels/colors`

List label colours

The fourteen solid colours and seven gradients the app offers when you make or edit a label, in the order it shows them. Each one says what to send as `color.backgroundColor`, the ink drawn on it, and for a gradient the colour at each end and one solid hex for places a gradient cannot go.

A fixed catalogue with no paging. Fetch it once and keep it. A label may also carry any other hex, set through this API, which the app keeps and offers back as its own swatch.

Requires the `labels:read` scope.

- Scopes: `labels:read`.

**Returns**

- `200` `LabelColorList`: The whole palette.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.listColors()`](https://nextmail.sh/docs/sdk/reference/labels#listColors); Python [`labels.list_colors()`](https://nextmail.sh/docs/python/reference/labels#listColors); Ruby [`labels.list_colors`](https://nextmail.sh/docs/ruby/reference/labels#listColors); PHP [`labels->listColors`](https://nextmail.sh/docs/php/reference/labels#listColors); Go [`Labels.ListColors`](https://nextmail.sh/docs/go/reference/labels#listColors); Java [`labels().listColors`](https://nextmail.sh/docs/java/reference/labels#listColors); C# [`Labels.ListColorsAsync`](https://nextmail.sh/docs/csharp/reference/labels#listColors); CLI [`nextmail labels list-colors`](https://nextmail.sh/docs/cli/reference/labels#labels-list-colors).

### `GET /labels/{id}`

Retrieve a label

One label by id, in the same shape as a row of the list. Ids are matched exactly, and a system id such as `INBOX` is a 404.

Requires the `labels:read` scope.

- Scopes: `labels:read`.

**Path parameters**

- `id` (`string`, required): Label id such as `USER_RECEIPTS`, matched case sensitively.

**Returns**

- `200` `Label`: The label.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.get()`](https://nextmail.sh/docs/sdk/reference/labels#get); Python [`labels.get()`](https://nextmail.sh/docs/python/reference/labels#get); Ruby [`labels.get`](https://nextmail.sh/docs/ruby/reference/labels#get); PHP [`labels->get`](https://nextmail.sh/docs/php/reference/labels#get); Go [`Labels.Get`](https://nextmail.sh/docs/go/reference/labels#get); Java [`labels().get`](https://nextmail.sh/docs/java/reference/labels#get); C# [`Labels.GetAsync`](https://nextmail.sh/docs/csharp/reference/labels#get); CLI [`nextmail labels get`](https://nextmail.sh/docs/cli/reference/labels#labels-get); MCP [`getLabel`](https://nextmail.sh/docs/mcp/tools/organising#getLabel), [`getUserLabels`](https://nextmail.sh/docs/mcp/tools/organising#getUserLabels).

### `PATCH /labels/{id}`

Rename or recolour a label

Renames a label, recolours it, or changes what AI does with it. Send any of `name`, `color`, `aiInstructions` and `aiEnabled`; a field left out stays as it is, and `color: null` or an empty `backgroundColor` clears the colour. The id never changes, so every conversation keeps the label through a rename.

With `aiEnabled` on, AI reads each arriving message against `aiInstructions` and adds the label when it fits. It skips spam, the Bin, encrypted mail and muted threads, and it only labels: it never archives or moves anything.

A key limited to particular addresses or domains, and the access token of an app anybody but the workspace owner connected, also need the `settings:write` scope, because a label belongs to the whole workspace.

Requires the `labels:write` scope.

- Scopes: `labels:write`.

**Path parameters**

- `id` (`string`, required): Label id such as `USER_RECEIPTS`.

**Request body**

- `name` (`string`, 1 to 225 characters): The new name, trimmed, 1 to 225 characters. Left out, the name stays.
- `color` (`object`, nullable): The new colour. `null` clears it. Left out, the colour stays.
  - `backgroundColor` (`string`, required, up to 32 characters): A hex colour (`#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`), stored upper-cased, or a gradient token: `gradient:sunset`, `gradient:ember`, `gradient:meadow`, `gradient:lagoon`, `gradient:aurora`, `gradient:berry`, `gradient:midnight`. The app's solid swatches are `#EF4444`, `#F97316`, `#EA9602`, `#DCB30B`, `#79BB19`, `#2EB45C`, `#00AD9B`, `#09A9CA`, `#3B82F6`, `#6366F1`, `#8B5CF6`, `#D946EF`, `#EC4899`, `#64748B`, and any other hex is kept as it is. An empty string means no colour. Anything else is a 422 `invalid_parameter` on `color.backgroundColor`.
  - `textColor` (`string`, up to 32 characters): Accepted and ignored. The ink is worked out from `backgroundColor`.
- `aiInstructions` (`string`, nullable, up to 500 characters): What the label is for, in plain words, up to 500 characters. `null` clears it, which also turns `aiEnabled` off. Left out, it stays.
- `aiEnabled` (`boolean`): Whether AI applies the label to arriving mail that fits `aiInstructions`. Turning it on needs instructions and a paid plan, and a workspace may have 10 such labels. Left out, it stays.

**Returns**

- `200` `Label`: Saved, read back as it is stored.

**Errors**

- `403`: `plan_required`: turning `aiEnabled` on needs a paid plan.
- `409`: `label_name_taken`: another label already has that name, compared without case.
- `422`: `invalid_parameter` for a body with none of `name`, `color`, `aiInstructions` and `aiEnabled`, a colour that is neither a hex value nor a gradient token, or `aiEnabled` turned on with no instructions. `ai_label_limit_reached` when the workspace already has as many labels that AI applies as it may.
- The errors every operation can return: `400`, `401`, `404`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.update()`](https://nextmail.sh/docs/sdk/reference/labels#update); Python [`labels.update()`](https://nextmail.sh/docs/python/reference/labels#update); Ruby [`labels.update`](https://nextmail.sh/docs/ruby/reference/labels#update); PHP [`labels->update`](https://nextmail.sh/docs/php/reference/labels#update); Go [`Labels.Update`](https://nextmail.sh/docs/go/reference/labels#update); Java [`labels().update`](https://nextmail.sh/docs/java/reference/labels#update); C# [`Labels.UpdateAsync`](https://nextmail.sh/docs/csharp/reference/labels#update); CLI [`nextmail labels update`](https://nextmail.sh/docs/cli/reference/labels#labels-update); MCP [`updateLabel`](https://nextmail.sh/docs/mcp/tools/organising#updateLabel).

### `DELETE /labels/{id}`

Delete a label

Deletes the label and takes it off every conversation that carried it, in one step. The conversations stay in whatever folder they were in. There is no undo: creating the same name again gives the same id, but the conversations do not get the label back.

A key limited to particular addresses or domains, and the access token of an app anybody but the workspace owner connected, also need the `settings:write` scope, because a label belongs to the whole workspace.

Requires the `labels:write` scope.

- Scopes: `labels:write`.

**Path parameters**

- `id` (`string`, required): Label id such as `USER_OLD_PROJECT`.

**Returns**

- `200` `DeletedLabel`: Deleted.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.delete()`](https://nextmail.sh/docs/sdk/reference/labels#delete); Python [`labels.delete()`](https://nextmail.sh/docs/python/reference/labels#delete); Ruby [`labels.delete`](https://nextmail.sh/docs/ruby/reference/labels#delete); PHP [`labels->delete`](https://nextmail.sh/docs/php/reference/labels#delete); Go [`Labels.Delete`](https://nextmail.sh/docs/go/reference/labels#delete); Java [`labels().delete`](https://nextmail.sh/docs/java/reference/labels#delete); C# [`Labels.DeleteAsync`](https://nextmail.sh/docs/csharp/reference/labels#delete); CLI [`nextmail labels delete`](https://nextmail.sh/docs/cli/reference/labels#labels-delete); MCP [`deleteLabel`](https://nextmail.sh/docs/mcp/tools/organising#deleteLabel).

### `POST /labels/{id}/test`

Try a label on recent mail

Runs the AI over the newest 20 inbox threads and says which ones the label would catch, without labelling anything. Send `aiInstructions` to try wording you have not saved yet, or leave it out to try what the label holds. It spends one AI action of the caller, which the labelling of arriving mail never does. A message that arrived encrypted is skipped.

Requires the `labels:write` and `threads:read` scopes.

- Scopes: `labels:write`, `threads:read`.

**Path parameters**

- `id` (`string`, required): The label, as `GET /labels` lists it.

**Request body**

- `aiInstructions` (`string`, 1 to 500 characters): What the label is for, in plain words, 1 to 500 characters. Left out, the instructions saved on the label are tried.

**Returns**

- `200` `LabelTest`: What the label would catch.

**Errors**

- `409`: `ai_not_configured`: AI is unavailable right now.
- `422`: `invalid_parameter`: there are no instructions to try, neither in the request nor on the label.
- `429`: `ai_quota_exceeded`: today's AI actions are used up. `rate_limited`: the key or app made more than 600 requests in 60 seconds. Wait for the seconds in `Retry-After`.
- The errors every operation can return: `400`, `401`, `403`, `404`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`nextmail.labels.test()`](https://nextmail.sh/docs/sdk/reference/labels#test); Python [`labels.test()`](https://nextmail.sh/docs/python/reference/labels#test); Ruby [`labels.test`](https://nextmail.sh/docs/ruby/reference/labels#test); PHP [`labels->test`](https://nextmail.sh/docs/php/reference/labels#test); Go [`Labels.Test`](https://nextmail.sh/docs/go/reference/labels#test); Java [`labels().test`](https://nextmail.sh/docs/java/reference/labels#test); C# [`Labels.TestAsync`](https://nextmail.sh/docs/csharp/reference/labels#test); CLI [`nextmail labels test`](https://nextmail.sh/docs/cli/reference/labels#labels-test); MCP [`testLabel`](https://nextmail.sh/docs/mcp/tools/organising#testLabel).

### Objects

#### `DeletedLabel`

`object`

- `object` (`string`, one of `"label"`)
- `id` (`string`)
- `deleted` (`boolean`, one of `true`)

#### `Label`

`object`

- `object` (`string`, one of `"label"`)
- `id` (`string`): Fixed when the label is created: `USER_` and the name upper-cased, with each run of spaces turned into `_`, so `Big Clients` is `USER_BIG_CLIENTS`. It never changes, so a renamed label keeps the id of its first name. Store the id rather than the name.
- `name` (`string`)
- `type` (`string`, one of `"user"`): Always `user`. System labels such as `INBOX`, `STARRED` and `UNREAD` are never listed, though a thread carries them and `PATCH /threads/{id}` takes them.
- `color` (`object`, nullable): How the label is painted. Null on a label saved with no colour.
  - `backgroundColor` (`string`): The stored colour: a hex value such as `#3B82F6`, stored upper-cased, or a gradient token such as `gradient:sunset`. `GET /labels/colors` lists the palette the app offers, with the two ends of every gradient.
  - `textColor` (`string`): The ink the app draws on that colour, `#18181B` or `#FFFFFF`. Worked out here from `backgroundColor` and never stored, so a `textColor` you sent is not echoed.
- `threadCount` (`integer`): Conversations carrying the label now, the number the Labels table in the app shows. A key limited to particular addresses counts only conversations delivered to them.
- `aiInstructions` (`string`, nullable): What the label is for, in plain words, such as `invoices and receipts`. With `aiEnabled` on, AI reads each arriving message against it. Null when none was written.
- `aiEnabled` (`boolean`): Whether AI applies the label to arriving mail that fits `aiInstructions`. It only ever adds the label, and never archives or moves mail.
- `createdAt` (`string`, nullable, format `date-time`): Null on a label made before these times were recorded.
- `updatedAt` (`string`, nullable, format `date-time`): The last rename or recolour. Null on a label made before these times were recorded.

#### `LabelColorEntry`

`object`

- `object` (`string`, one of `"label_color"`)
- `kind` (`string`, one of `"solid"`, `"gradient"`)
- `name` (`string`): The swatch name, such as `red` or `sunset`.
- `value` (`string`, one of `"#EF4444"`, `"#F97316"`, `"#EA9602"`, `"#DCB30B"`, `"#79BB19"`, `"#2EB45C"`, `"#00AD9B"`, `"#09A9CA"`, `"#3B82F6"`, `"#6366F1"`, `"#8B5CF6"`, `"#D946EF"`, `"#EC4899"`, `"#64748B"`, `"gradient:sunset"`, `"gradient:ember"`, `"gradient:meadow"`, `"gradient:lagoon"`, `"gradient:aurora"`, `"gradient:berry"`, `"gradient:midnight"`): What to send as `color.backgroundColor` to use this swatch.
- `solid` (`string`): One hex for places a gradient cannot be drawn, such as an icon. The same as `value` on a solid.
- `from` (`string`, nullable): Where a gradient starts, drawn at 135 degrees. Null on a solid.
- `to` (`string`, nullable): Where a gradient ends. Null on a solid.
- `textColor` (`string`): The ink the app draws on this swatch.

#### `LabelColorInput`

`object`

- `backgroundColor` (`string`, required, up to 32 characters): A hex colour (`#RGB`, `#RGBA`, `#RRGGBB` or `#RRGGBBAA`), stored upper-cased, or a gradient token: `gradient:sunset`, `gradient:ember`, `gradient:meadow`, `gradient:lagoon`, `gradient:aurora`, `gradient:berry`, `gradient:midnight`. The app's solid swatches are `#EF4444`, `#F97316`, `#EA9602`, `#DCB30B`, `#79BB19`, `#2EB45C`, `#00AD9B`, `#09A9CA`, `#3B82F6`, `#6366F1`, `#8B5CF6`, `#D946EF`, `#EC4899`, `#64748B`, and any other hex is kept as it is. An empty string means no colour. Anything else is a 422 `invalid_parameter` on `color.backgroundColor`.
- `textColor` (`string`, up to 32 characters): Accepted and ignored. The ink is worked out from `backgroundColor`.

#### `LabelColorList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`LabelColorEntry[]`)

#### `LabelList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`Label[]`)
- `hasMore` (`boolean`): True when another page follows. Pass `nextCursor` back as `cursor` to read it.
- `nextCursor` (`string`, nullable): An opaque cursor for the next page, or null on the last page. Pass it back unchanged.

#### `LabelTest`

`object`

- `object` (`string`, required, one of `"label_test"`)
- `labelId` (`string`, required)
- `examined` (`integer`, required): How many recent threads were read.
- `matched` (`object[]`, required): The threads the label would catch. A workspace may have 10 labels that AI applies.
  - `threadId` (`string`, required)
  - `subject` (`string`, required)
  - `sender` (`string`, required): The address the newest message is from.
  - `receivedOn` (`string`, required, nullable): When that message arrived, as its Date header gives it.
