---
title: "Directory sync"
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/directory-sync"
area: "API"
category: "Reference"
---

# Directory sync

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

## Operations

Directory sync (SCIM) lets a company directory, such as Okta or Microsoft Entra, add people to the workspace and take them out again. These calls are the settings the owner sees on the Privacy tab: turn it on and off, read where it stands and who it synced, and choose the roles it gives. The directory itself calls the SCIM endpoints under `/scim/v2` with the token. Reading needs `members:read` and every change needs `members:write`. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`.

### `GET /directory-sync`

Read directory sync

Whether directory sync is on, the base URL to give the directory, the last four characters of its token and when the directory last used it, how many people it synced and where each stands, and the groups it synced with the role each gives. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`.

Requires the `members:read` scope.

- Scopes: `members:read`.

**Returns**

- `200` `DirectorySync`: Where directory sync stands.

**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 [`members.getDirectorySync()`](https://nextmail.sh/docs/sdk/reference/members#getDirectorySync); Python [`members.get_directory_sync()`](https://nextmail.sh/docs/python/reference/members#getDirectorySync); Ruby [`members.get_directory_sync`](https://nextmail.sh/docs/ruby/reference/members#getDirectorySync); PHP [`members->getDirectorySync`](https://nextmail.sh/docs/php/reference/members#getDirectorySync); Go [`Members.GetDirectorySync`](https://nextmail.sh/docs/go/reference/members#getDirectorySync); Java [`members().getDirectorySync`](https://nextmail.sh/docs/java/reference/members#getDirectorySync); C# [`Members.GetDirectorySyncAsync`](https://nextmail.sh/docs/csharp/reference/members#getDirectorySync); CLI [`nextmail members get-directory-sync`](https://nextmail.sh/docs/cli/reference/members#members-get-directory-sync); MCP [`getDirectorySync`](https://nextmail.sh/docs/mcp/tools/workspace#getDirectorySync).

### `POST /directory-sync`

Turn on directory sync

Turns directory sync on and returns the token the directory sends as its bearer token. When it is already on, this makes a new token and the old one stops working at once. The token is in this answer only and can never be read again, so store it in the directory straight away. Directory sync comes with the Business and Enterprise plans: on any other plan the call is a 403 `plan_required`. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`. A key limited to particular addresses or domains is refused with 422 `capability_unsupported`, because the people it changes reach beyond them.

Requires the `members:write` scope.

- Scopes: `members:write`.
- Asks an OAuth access token for a verification code.

**Returns**

- `201` `DirectorySyncToken`: Directory sync is on, with a new token.

**Errors**

- `403`: The key lacks the scope, or may not send as that address. `step_up_required`: the call was made with an OAuth access token that has not been verified in the last 60 minutes. Ask for a code with `POST /security/step-up`, send it to `POST /security/step-up/verify`, then repeat the call. The person can also choose Allow changes for 60 minutes on the app in Account settings, Connected apps, on the Nextmail website. An API key is never asked for a code.
- The errors every operation can return: `400`, `401`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`members.connectDirectorySync()`](https://nextmail.sh/docs/sdk/reference/members#connectDirectorySync); Python [`members.connect_directory_sync()`](https://nextmail.sh/docs/python/reference/members#connectDirectorySync); Ruby [`members.connect_directory_sync`](https://nextmail.sh/docs/ruby/reference/members#connectDirectorySync); PHP [`members->connectDirectorySync`](https://nextmail.sh/docs/php/reference/members#connectDirectorySync); Go [`Members.ConnectDirectorySync`](https://nextmail.sh/docs/go/reference/members#connectDirectorySync); Java [`members().connectDirectorySync`](https://nextmail.sh/docs/java/reference/members#connectDirectorySync); C# [`Members.ConnectDirectorySyncAsync`](https://nextmail.sh/docs/csharp/reference/members#connectDirectorySync); CLI [`nextmail members connect-directory-sync`](https://nextmail.sh/docs/cli/reference/members#members-connect-directory-sync); MCP [`connectDirectorySync`](https://nextmail.sh/docs/mcp/tools/workspace#connectDirectorySync).

### `DELETE /directory-sync`

Turn off directory sync

Turns directory sync off. Its token stops working at once and the people and groups it synced are forgotten, while everyone who already joined stays a member. Turning it off when it is off changes nothing. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`. A key limited to particular addresses or domains is refused with 422 `capability_unsupported`, because the people it changes reach beyond them.

Requires the `members:write` scope.

- Scopes: `members:write`.
- Asks an OAuth access token for a verification code.

**Returns**

- `200` `DirectorySyncDisconnected`: Directory sync is off.

**Errors**

- `403`: The key lacks the scope, or may not send as that address. `step_up_required`: the call was made with an OAuth access token that has not been verified in the last 60 minutes. Ask for a code with `POST /security/step-up`, send it to `POST /security/step-up/verify`, then repeat the call. The person can also choose Allow changes for 60 minutes on the app in Account settings, Connected apps, on the Nextmail website. An API key is never asked for a code.
- The errors every operation can return: `400`, `401`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`members.disconnectDirectorySync()`](https://nextmail.sh/docs/sdk/reference/members#disconnectDirectorySync); Python [`members.disconnect_directory_sync()`](https://nextmail.sh/docs/python/reference/members#disconnectDirectorySync); Ruby [`members.disconnect_directory_sync`](https://nextmail.sh/docs/ruby/reference/members#disconnectDirectorySync); PHP [`members->disconnectDirectorySync`](https://nextmail.sh/docs/php/reference/members#disconnectDirectorySync); Go [`Members.DisconnectDirectorySync`](https://nextmail.sh/docs/go/reference/members#disconnectDirectorySync); Java [`members().disconnectDirectorySync`](https://nextmail.sh/docs/java/reference/members#disconnectDirectorySync); C# [`Members.DisconnectDirectorySyncAsync`](https://nextmail.sh/docs/csharp/reference/members#disconnectDirectorySync); CLI [`nextmail members disconnect-directory-sync`](https://nextmail.sh/docs/cli/reference/members#members-disconnect-directory-sync); MCP [`disconnectDirectorySync`](https://nextmail.sh/docs/mcp/tools/workspace#disconnectDirectorySync).

### `GET /directory-sync/people`

List the people the directory synced

The people the directory synced into the workspace, a page at a time and in the order it sent them, with where each stands: `member` once they joined, `invited` while an invitation waits, `waiting` before one is sent, and `inactive` when the directory switched them off. Pages are counted from 1 as SCIM pages are: pass `startIndex` plus the number of people returned for the next one, while that is at most `total`. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`.

Requires the `members:read` scope.

- Scopes: `members:read`.

**Query parameters**

- `startIndex` (`integer`, at least 1): Where the page starts, counted from 1. Leave it out for the first page.
- `count` (`integer`, at least 1, at most 100): How many people to return, from 1 to 100. Leave it out for 100.

**Returns**

- `200` `DirectoryPersonList`: A page of people.

**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 [`members.listDirectoryPeople()`](https://nextmail.sh/docs/sdk/reference/members#listDirectoryPeople); Python [`members.list_directory_people()`](https://nextmail.sh/docs/python/reference/members#listDirectoryPeople); Ruby [`members.list_directory_people`](https://nextmail.sh/docs/ruby/reference/members#listDirectoryPeople); PHP [`members->listDirectoryPeople`](https://nextmail.sh/docs/php/reference/members#listDirectoryPeople); Go [`Members.ListDirectoryPeople`](https://nextmail.sh/docs/go/reference/members#listDirectoryPeople); Java [`members().listDirectoryPeople`](https://nextmail.sh/docs/java/reference/members#listDirectoryPeople); C# [`Members.ListDirectoryPeopleAsync`](https://nextmail.sh/docs/csharp/reference/members#listDirectoryPeople); CLI [`nextmail members list-directory-people`](https://nextmail.sh/docs/cli/reference/members#members-list-directory-people); MCP [`listDirectoryPeople`](https://nextmail.sh/docs/mcp/tools/workspace#listDirectoryPeople).

### `PUT /directory-sync/default-role`

Choose the default role of directory sync

Chooses the role directory sync gives somebody that none of their groups gives a role. `null` goes back to the built-in Member role. The owner role cannot be given out this way (422 `invalid_parameter`), a role that is not in the workspace is a 422 `role_not_found`, and anybody but the owner can only give a role that holds nothing they lack themselves (403 `insufficient_authority`). It answers 409 `directory_sync_not_connected` while directory sync is off. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`. A key limited to particular addresses or domains is refused with 422 `capability_unsupported`, because the people it changes reach beyond them.

Requires the `members:write` scope.

- Scopes: `members:write`.
- Asks an OAuth access token for a verification code.

**Request body**

- `roleId` (`string`, required, nullable, 1 to 128 characters): The id of a role in this workspace, as the roles list names it, or null to give no role from the directory. The owner role can never be given out this way, and anybody but the owner can only give a role that holds nothing they lack themselves.

**Returns**

- `200` `DirectorySync`: Where directory sync stands now.

**Errors**

- `403`: The key lacks the scope, or may not send as that address. `step_up_required`: the call was made with an OAuth access token that has not been verified in the last 60 minutes. Ask for a code with `POST /security/step-up`, send it to `POST /security/step-up/verify`, then repeat the call. The person can also choose Allow changes for 60 minutes on the app in Account settings, Connected apps, on the Nextmail website. An API key is never asked for a code.
- `409`: `directory_sync_not_connected`: directory sync is off.
- The errors every operation can return: `400`, `401`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`members.setDirectoryDefaultRole()`](https://nextmail.sh/docs/sdk/reference/members#setDirectoryDefaultRole); Python [`members.set_directory_default_role()`](https://nextmail.sh/docs/python/reference/members#setDirectoryDefaultRole); Ruby [`members.set_directory_default_role`](https://nextmail.sh/docs/ruby/reference/members#setDirectoryDefaultRole); PHP [`members->setDirectoryDefaultRole`](https://nextmail.sh/docs/php/reference/members#setDirectoryDefaultRole); Go [`Members.SetDirectoryDefaultRole`](https://nextmail.sh/docs/go/reference/members#setDirectoryDefaultRole); Java [`members().setDirectoryDefaultRole`](https://nextmail.sh/docs/java/reference/members#setDirectoryDefaultRole); C# [`Members.SetDirectoryDefaultRoleAsync`](https://nextmail.sh/docs/csharp/reference/members#setDirectoryDefaultRole); CLI [`nextmail members set-directory-default-role`](https://nextmail.sh/docs/cli/reference/members#members-set-directory-default-role); MCP [`setDirectoryDefaultRole`](https://nextmail.sh/docs/mcp/tools/workspace#setDirectoryDefaultRole).

### `PUT /directory-sync/groups/{groupId}/role`

Choose the role of a directory group

Chooses the role a group the directory synced gives its people, and moves everyone in it to that role now. `null` stops the group giving a role, so its people fall back to the default role. The same rules about roles apply as for the default role, a group that is not in the workspace is a 404, and it answers 409 `directory_sync_not_connected` while directory sync is off. Only the workspace owner, or a member whose role holds `workspace:manage`, may call it: an access token acting for anybody else is refused with 403 `insufficient_authority`. A key limited to particular addresses or domains is refused with 422 `capability_unsupported`, because the people it changes reach beyond them.

Requires the `members:write` scope.

- Scopes: `members:write`.
- Asks an OAuth access token for a verification code.

**Path parameters**

- `groupId` (`string`, required): A group the directory synced, as `GET /directory-sync` lists it in `groups`.

**Request body**

- `roleId` (`string`, required, nullable, 1 to 128 characters): The id of a role in this workspace, as the roles list names it, or null to give no role from the directory. The owner role can never be given out this way, and anybody but the owner can only give a role that holds nothing they lack themselves.

**Returns**

- `200` `DirectorySync`: Where directory sync stands now.

**Errors**

- `403`: The key lacks the scope, or may not send as that address. `step_up_required`: the call was made with an OAuth access token that has not been verified in the last 60 minutes. Ask for a code with `POST /security/step-up`, send it to `POST /security/step-up/verify`, then repeat the call. The person can also choose Allow changes for 60 minutes on the app in Account settings, Connected apps, on the Nextmail website. An API key is never asked for a code.
- `409`: `directory_sync_not_connected`: directory sync is off.
- The errors every operation can return: `400`, `401`, `404`, `422`, `429`, `500`, described in the [error catalog](https://nextmail.sh/docs/api/errors).

Also available in: TypeScript [`members.setDirectoryGroupRole()`](https://nextmail.sh/docs/sdk/reference/members#setDirectoryGroupRole); Python [`members.set_directory_group_role()`](https://nextmail.sh/docs/python/reference/members#setDirectoryGroupRole); Ruby [`members.set_directory_group_role`](https://nextmail.sh/docs/ruby/reference/members#setDirectoryGroupRole); PHP [`members->setDirectoryGroupRole`](https://nextmail.sh/docs/php/reference/members#setDirectoryGroupRole); Go [`Members.SetDirectoryGroupRole`](https://nextmail.sh/docs/go/reference/members#setDirectoryGroupRole); Java [`members().setDirectoryGroupRole`](https://nextmail.sh/docs/java/reference/members#setDirectoryGroupRole); C# [`Members.SetDirectoryGroupRoleAsync`](https://nextmail.sh/docs/csharp/reference/members#setDirectoryGroupRole); CLI [`nextmail members set-directory-group-role`](https://nextmail.sh/docs/cli/reference/members#members-set-directory-group-role); MCP [`setDirectoryGroupRole`](https://nextmail.sh/docs/mcp/tools/workspace#setDirectoryGroupRole).

### Objects

#### `DirectoryPerson`

`object`

- `object` (`string`, required, one of `"directory_person"`)
- `id` (`string`, required): The person as the directory sync knows them.
- `userName` (`string`, required): The user name the directory sent.
- `email` (`string`, required, nullable): The email address the directory sent, or null when it sent none.
- `name` (`string`, required, nullable): The name the directory sent, or null when it sent none.
- `state` (`string`, required, one of `"member"`, `"invited"`, `"waiting"`, `"inactive"`): `member` once they joined, `invited` while an invitation waits, `waiting` before one is sent, `inactive` when the directory switched them off.
- `groups` (`string[]`, required): The names of the groups they are in.
- `updatedAt` (`string`, required, format `date-time`): When the directory last changed them.

#### `DirectoryPersonList`

`object`

- `object` (`string`, required, one of `"list"`)
- `data` (`DirectoryPerson[]`, required)
- `total` (`integer`, required): How many people the directory synced in all.
- `startIndex` (`integer`, required): Where this page starts, counted from 1.

#### `DirectorySync`

`object`

- `object` (`string`, required, one of `"directory_sync"`)
- `connected` (`boolean`, required): Whether directory sync is on.
- `available` (`boolean`, required): Whether the plan of the workspace includes directory sync.
- `baseUrl` (`string`, required): The SCIM base URL to give the directory, the same for every workspace.
- `tokenLast4` (`string`, required, nullable): The last four characters of the token, to tell it apart. Null while it is off.
- `tokenRotatedAt` (`string`, required, nullable, format `date-time`): When the token in use was made. Null while it is off.
- `lastSeenAt` (`string`, required, nullable, format `date-time`): When the directory last called with the token, to the minute, or null if it never has.
- `connectedAt` (`string`, required, nullable, format `date-time`): When directory sync was turned on. Null while it is off.
- `defaultRoleId` (`string`, required, nullable): The role given to somebody that none of their groups gives a role, or null for the built-in Member role.
- `users` (`object`, required): How many people the directory synced, and where they stand.
  - `total` (`integer`, required): Everybody the directory synced.
  - `members` (`integer`, required): Those who joined the workspace.
  - `invited` (`integer`, required): Those with an invitation waiting.
  - `waiting` (`integer`, required): Those not invited yet.
  - `inactive` (`integer`, required): Those the directory switched off.
- `groups` (`object[]`, required): The groups the directory synced, oldest first.
  - `id` (`string`, required): The group, as the role of a group is changed by.
  - `displayName` (`string`, required): Its name in the directory.
  - `members` (`integer`, required): How many synced people are in it.
  - `roleId` (`string`, required, nullable): The role it gives its people, or null when it gives none.

#### `DirectorySyncDisconnected`

`object`

- `object` (`string`, required, one of `"directory_sync"`)
- `disconnected` (`boolean`, required): True when directory sync was on and is off now, false when it was already off.

#### `DirectorySyncToken`

`object`

- `object` (`string`, required, one of `"directory_sync_token"`)
- `token` (`string`, required): The bearer token the directory sends. It is shown here only and can never be read again.
- `status` (`DirectorySync`, required)
