Documentation menu
These are pre-release docs from the main branch.

API reference

19 operations in org-users. Raw spec: openapi.json.

GET/api/v1/directory-groups

GET /api/directory-groups — every group in the org (manager+).

Responses

StatusDescription
200
403requires manage_users, manage_roles, directory_groups:read or directory_groups:write

Security: bearer

POST/api/v1/directory-groups

POST /api/directory-groups — create a group. The group grants nothing until a role is mapped onto it.

Request body

CreateDirectoryGroupRequest

Responses

StatusDescription
200
409name or external id already in use, or idempotency key reused
422validation failure

Security: bearer

GET/api/v1/directory-groups/{id}

GET /api/directory-groups/{id} — the group with its members and role mappings (manager+).

Parameters

NameInTypeRequiredDescription
idpathstringYes

Responses

StatusDescription
200
404group not visible in scope

Security: bearer

PATCH/api/v1/directory-groups/{id}

PATCH /api/directory-groups/{id} — rename, re-describe, or re-point the external id. Absent fields keep their values.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

UpdateDirectoryGroupRequest

Responses

StatusDescription
200
404group not visible in scope
409name or external id already in use, version conflict, or idempotency key reused
422validation failure

Security: bearer

DELETE/api/v1/directory-groups/{id}

DELETE /api/directory-groups/{id} — remove the group. Its memberships and role mappings go with it, so every member loses exactly what this group granted.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Responses

StatusDescription
200
404group not visible in scope

Security: bearer

PUT/api/v1/directory-groups/{id}/members/{userId}

PUT /api/directory-groups/{id}/members/{userId} — put a user in the group. Idempotent; a repeated call reports the same membership and emits nothing.

The ceiling applies to the insert and not to the no-op: adding a person is what turns the roles this group already carries into that person's grants, so every mapped role must be inside the caller's own set. A repeat of a membership that already exists confers nothing new, which is why it stays a 200 — refusing it would break the documented idempotency a sync depends on while closing no hole. The 403 the ceiling raises is not added to the `utoipa` response list here: the OpenAPI pins are a reviewed artifact, and this lane changes behaviour rather than the documented contract. The next sanctioned re-pin should add it to this operation and to `map_role`.

Parameters

NameInTypeRequiredDescription
idpathstringYes
userIdpathstringYes

Responses

StatusDescription
200
404group or user not visible in scope

Security: bearer

DELETE/api/v1/directory-groups/{id}/members/{userId}

DELETE /api/directory-groups/{id}/members/{userId} — take a user out of the group. The roles that group carried for them are gone; every other grant survives.

Parameters

NameInTypeRequiredDescription
idpathstringYes
userIdpathstringYes

Responses

StatusDescription
200
404group or membership not visible in scope

Security: bearer

PUT/api/v1/directory-groups/{id}/roles/{roleId}

PUT /api/directory-groups/{id}/roles/{roleId} — map a role onto the group. Every current and future member holds it from now on (additive union with their directly assigned roles). Idempotent.

The ceiling applies to the mapping write itself: the role's resolved permission set must be inside the caller's own, or the mapping is a typed 403 and no row is written. A no-op repeat of an existing mapping confers nothing new, so it stays a 200 for the same reason a repeated member add does. The 403 stays out of the `utoipa` response list for the reason given on `add_member`: the pins are a reviewed artifact and this lane does not re-pin them.

Parameters

NameInTypeRequiredDescription
idpathstringYes
roleIdpathstringYes

Responses

StatusDescription
200
404group or role not visible in scope

Security: bearer

DELETE/api/v1/directory-groups/{id}/roles/{roleId}

DELETE /api/directory-groups/{id}/roles/{roleId} — drop a role from the group. Members lose exactly that role where the group was its only source.

Parameters

NameInTypeRequiredDescription
idpathstringYes
roleIdpathstringYes

Responses

StatusDescription
200
404group or mapping not visible in scope

Security: bearer

GET/api/v1/org/users

GET /api/org/users — the staff directory (manager+).

Parameters

NameInTypeRequiredDescription
limitquery—NoPage size (default 50, max 200).
offsetquery—NoOffset paging.

Responses

StatusDescription
200
403requires manager role

Security: bearer

POST/api/v1/org/users

POST /api/org/users — invite (create) a staff user. The row lands INACTIVE with an unusable password; the response carries a one-time setup token the invitee redeems at `POST /org/users/accept-invite`.

Request body

InviteUserRequest

Responses

StatusDescription
200
409email already in org, or idempotency key reused

Security: bearer

POST/api/v1/org/users/accept-invite

POST /api/org/users/accept-invite — public invite redemption: set the first password and activate the account; returns a normal token pair. The token is single-use and expiring; an unknown, used, or expired token is an indistinguishable 404.

Request body

AcceptInviteRequest

Responses

StatusDescription
200
404invite unknown, already used, or expired
422validation error
POST/api/v1/org/users/{id}/email-verified

POST /api/org/users/{id}/email-verified — the admin OVERRIDE for the email-verification gate (migration 0088). Audited both ways.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

SetEmailVerifiedRequest

Responses

StatusDescription
200
403
404

Security: bearer

PATCH/api/v1/org/users/{id}/role

PATCH /api/org/users/{id}/role — assign a builtin org role. Each change is audited; the last active owner cannot be demoted away from owner.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

SetUserRoleRequest

Responses

StatusDescription
200
409last active owner, version conflict, or idempotency key reused

Security: bearer

PATCH/api/v1/org/users/{id}/status

PATCH /api/org/users/{id}/status — activate or deactivate. Deactivation revokes every live session and blocks login in the same commit.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

SetUserStatusRequest

Responses

StatusDescription
200
409last active owner, version conflict, or idempotency key reused

Security: bearer

GET/api/v1/roles

GET /api/roles — the org's roles plus the canonical preset + portal tier rosters (manager+).

Responses

StatusDescription
200
403requires manage_users or manage_roles

Security: bearer

PUT/api/v1/roles/{id}

PUT /api/roles/{id} — the custom-roles EDITOR (Wave 22): updates a custom row's name and/or permission set. Builtin presets are typed refusals (rb1) — restore is the ONLY preset-matrix mutation.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

UpdateRoleRequest

Responses

StatusDescription
200
403builtin_role_immutable
404
422validation failure

Security: bearer

POST/api/v1/roles/{id}/fork

POST /api/roles/{id}/fork — duplicate a role (typically a preset) into a mutable custom copy on the same matrix. The origin row is untouched.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

ForkRoleRequest

Responses

StatusDescription
200
404role not visible in scope
409role name taken or idempotency key reused
422validation failure

Security: bearer

POST/api/v1/roles/{id}/restore

POST /api/roles/{id}/restore — reset a preset row's permission array to the canonical shipped set (RB1: the presets stay canonical). Custom roles and forks are org-owned data and are never restorable.

Parameters

NameInTypeRequiredDescription
idpathstringYes

Request body

RestoreRoleRequest

Responses

StatusDescription
200
404role not visible in scope, or not a preset
409version conflict or idempotency key reused

Security: bearer