How directory sync works
Directory sync lets a company directory, such as Okta or Microsoft Entra, add people to the workspace when they join the company and take them out when they leave. The directory talks to Nextmail over SCIM 2.0. The API holds the settings the owner sees on the Privacy tab: it turns the connection on and off, chooses the roles it gives, and lists the people it synced.
The SCIM connection
The directory calls the SCIM endpoints under https://api.nextmail.sh/scim/v2 itself, with a bearer token that belongs to the workspace. POST /directory-sync turns directory sync on and returns that token in token, with status, where directory sync stands now. Give the directory status.baseUrl and the token.
curl -X POST "$API/directory-sync" -H "$AUTH" { "object": "directory_sync_token", "token": "nm_scim_…", "status": { "object": "directory_sync", "connected": true, "available": true, "baseUrl": "https://api.nextmail.sh/scim/v2", "tokenLast4": "q7Xe", "tokenRotatedAt": "2026-10-25T09:00:00.000Z", "lastSeenAt": null, "connectedAt": "2026-10-25T09:00:00.000Z", "defaultRoleId": null, "users": { "total": 0, "members": 0, "invited": 0, "waiting": 0, "inactive": 0 }, "groups": [] }}- The token is in that answer only and can never be read again, so store it in the directory straight away. Every read after that shows its last four characters in
tokenLast4. - Calling
POST /directory-syncagain while directory sync is on makes a new token, and the old one stops working at once. GET /directory-syncreads where it stands: whether it is on, whether the plan includes it inavailable, when the token was made, when the directory last called with it inlastSeenAt, to the minute, how many people it synced inusers, and thegroupsit synced with the role each gives.DELETE /directory-syncturns it off. Its token stops working at once and the people and groups it synced are forgotten, while everyone who already joined stays a member.disconnectedis false when it was already off.- Directory sync comes with the Business and Enterprise plans. Turning it on with any other plan is a 403
plan_required, and while the workspace is on another plan the directory itself is refused, so nobody is added or removed.
Default and group roles
Each person the directory sends is matched on their email address. Somebody who is already a member is linked to their entry. Anybody else is invited by email with a role and no addresses, and joins once they accept. The role comes from their groups, or else from the default role.
PUT /directory-sync/default-rolewith{ "roleId": "…" }chooses the default role, the one a person gets when none of their groups gives one.nullgoes back to the built-in Member role.PUT /directory-sync/groups/{groupId}/rolewith aroleIdmakes a group give that role, and moves everyone in it to that role now.nullstops the group giving a role, so its people fall back to the default role.groupIdcomes fromgroupson the read.- Somebody in several groups that give a role gets the one with the most permissions. A directory never gives the owner role.
- Both answer with where directory sync stands now, in the shape of the read.
- The owner role cannot be given this way (422
invalid_parameter), a role that is not in the workspace is a 422role_not_found, and anybody but the owner can give only a role that holds nothing they lack themselves (403insufficient_authority). A group that is not in the workspace is a 404, and both answer 409directory_sync_not_connectedwhile directory sync is off.
The people list
GET /directory-sync/people lists the people the directory synced, in the order it sent them. Each has id, userName, email, name, the names of their groups, updatedAt, when the directory last changed them, and state, where they stand.
| `state` | What it means |
|---|---|
| member | They joined the workspace. |
| invited | An invitation waits for them to accept it. |
| waiting | No invitation could be sent yet, such as for a person with no email address. They are invited as soon as one can be. |
| inactive | The directory switched them off, and they are out of the workspace. |
- Pages are counted from 1, as SCIM pages are.
counttakes up to 100 and defaults to 100, and the answer carriestotalandstartIndex. For the next page, passstartIndexplus the number of people returned, while that is at mosttotal. userson the read counts the same people bystate, so the totals need no paging.
Who can read and change it
- Reading needs
members:read, and every change needsmembers:write. - Only the workspace owner, or a member whose role holds
workspace:manage, may call these: an access token acting for anybody else is refused with 403insufficient_authority. That permission stays in the console and is never given to an app. - A key limited to particular addresses or domains is refused every change with 422
capability_unsupported, because the people it changes reach beyond them. - An OAuth access token needs a verification code to turn directory sync on or off and to choose a role. Until the app has verified one in the last 60 minutes, the call answers
403step_up_requiredand changes nothing. An API key is never asked. The Authentication page shows how to ask for a code and verify it. - Turning it on or off, a new token and every role change are written to the workspace security log with who made them, and what the directory itself does is logged as By directory sync.
From code, the terminal and agents
The same six calls are the members methods of the SDKs, from getDirectorySync to setDirectoryGroupRole, the members commands of the CLI, and six MCP tools under Workspace and people, which ask in a chat before they change anything.
Google Workspace