users

13 endpoints.

GET/api/users/me Bearer token

Read the caller's account

The account the presented token resolves to: canonical profile and sign-in methods. Accounts come into existence on first contact, so this always answers for a valid token.

Responses

StatusDescriptionBody
200The caller's account.UserProfile
401The access token is missing or invalid.ErrorResponse
500The account could not be read.ErrorResponse
DELETE/api/users/me/identities/{identity_id} Bearer token

Unlink one of the caller's sign-in methods

Detaches a sign-in method from the caller's own account. The last method cannot be unlinked — that would lock the account out; ask a superuser to detach it deliberately.

Parameters

NameInTypeRequiredDescription
identity_idpathstring (uuid)yesThe identity's id, from the account's identity listing.

Responses

StatusDescriptionBody
200The unlinked sign-in method.UserIdentity
400The identity is the account's last sign-in method.ErrorResponse
401The access token is missing or invalid.ErrorResponse
404The identity does not exist, or belongs to another account.ErrorResponse
500The identity could not be detached.ErrorResponse
POST/api/users Bearer token

Create an account known only by email

Creates a user whose OIDC subject is not yet known, with a pending email link at the named provider: the first sign-in there whose `email` claim matches (subject to the provider's email verification strictness) attaches its identity to this account instead of creating a new one. Refused when the address already has a pending link or is the profile email of a live account — attach the known identity or merge instead. Requires a superuser's access token.

Request body

application/jsonrequiredCreateUserRequest
FieldTypeRequiredDescription
display_namestring | nullno

Starting display name; the first sign-in's name claim refreshes it.

1–200 characters

emailstringyes

The address the person will sign in with; compared case-insensitively.

3–300 characters

provider_idstringyes

Sign-in provider UUID id or slug the person is expected to sign in with.

at least 1 character

Responses

StatusDescriptionBody
201The new account and its pending email link.CreatedUser
400The request body failed validation, or the email is not an address.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No sign-in provider has that id or slug.ErrorResponse
409That address already has a pending link or belongs to a live account.ErrorResponse
500The account could not be created.ErrorResponse
POST/api/users/{user_id}/email-links Bearer token

Add a pending email link to an account

Names an address whose first sign-in at the given provider should attach its identity to this account (subject to the provider's email verification strictness). Refused when the address already has a pending link or is the profile email of a *different* live account. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Request body

application/jsonrequiredCreateEmailLinkRequest
FieldTypeRequiredDescription
emailstringyes

The address the person will sign in with; compared case-insensitively.

3–300 characters

provider_idstringyes

Sign-in provider UUID id or slug the person is expected to sign in with.

at least 1 character

Responses

StatusDescriptionBody
201The pending email link.UserEmailLink
400The request body failed validation, or the email is not an address.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No user or provider has that id.ErrorResponse
409That address already has a pending link or belongs to another account.ErrorResponse
500The email link could not be created.ErrorResponse
PATCH/api/users/{user_id} Bearer token

Update an account's profile or standing

Sets the account's display name (null clears it) and/or switches it off and on with `disabled`; omitted fields keep their values. The name is the canonical profile the app shows everywhere, and a later sign-in whose ID token carries a name claim refreshes it again. Disabling is the reversible way to keep someone out: sign-in and tokens are refused and open sessions end, while everything the account owns stays put until it is enabled again (permanent deletion lives at /users/{user_id}/permanent-deletion). A merged (tombstoned) account cannot be edited — its survivor holds the profile — and a caller cannot disable their own account. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Request body

application/jsonrequiredUpdateUserRequest
FieldTypeRequiredDescription
display_namestring | nullno

The name shown for the account everywhere; null clears it.

1–200 characters

disabledbooleanno

true switches the account off: its sessions end on their next page load, its tokens stop resolving, and sign-in is refused, while everything it owns stays. false switches it back on. A caller cannot disable their own account.

Responses

StatusDescriptionBody
200The updated account.UserAccount
400The request body failed validation, the account was merged away, or the caller tried to disable their own account.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No user has that id.ErrorResponse
500The account could not be updated.ErrorResponse
GET/api/users/{user_id}/identities Bearer token

List an account's sign-in methods

Every identity attached to the account. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Responses

StatusDescriptionBody
200The account's sign-in methods, oldest first.array of UserIdentity
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No user has that id.ErrorResponse
500The identities could not be read.ErrorResponse
POST/api/users/{user_id}/identities Bearer token

Attach a sign-in method to an account

Links a (provider, subject) pair to the account — the administrative side of identity linking, for provider migrations and rescues. An identity already attached to a *different* account is refused: merge or detach it first. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Request body

application/jsonrequiredAttachIdentityRequest
FieldTypeRequiredDescription
provider_idstringyes

Sign-in provider UUID id or slug.

at least 1 character

subjectstringyes

The `sub` claim the account's owner presents at that provider.

1–255 characters

Responses

StatusDescriptionBody
201The attached sign-in method.UserIdentity
400The request body failed validation.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No user or provider has that id.ErrorResponse
409That identity already belongs to a different account.ErrorResponse
500The identity could not be attached.ErrorResponse
DELETE/api/users/{user_id}/identities/{identity_id} Bearer token

Detach a sign-in method from an account

Removes one identity from the account. Detaching the *last* one locks the account out of signing in (its history remains) and is refused unless `force=true` — the deliberate offboarding or compromised-credential case. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).
identity_idpathstring (uuid)yesThe identity's id, from the account's identity listing.
forcequerystringnoSet true to detach the account's last sign-in method.

Responses

StatusDescriptionBody
200The detached sign-in method.UserIdentity
400The identity is the account's last sign-in method and force is not set.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404The user or the identity does not exist.ErrorResponse
500The identity could not be detached.ErrorResponse
POST/api/users/{user_id}/merge Bearer token

Merge an account into another

Absorbs the account at {user_id} into into_user_id: identities move over, every reference is repointed at the survivor (redundant memberships, grants and duplicates resolve in the survivor's favour), and the absorbed account is tombstoned. The fix for the duplicate a first sign-in creates before its identity is linked. One transaction, irreversible. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Request body

application/jsonrequiredMergeUsersRequest
FieldTypeRequiredDescription
into_user_idstring (uuid)yes

The surviving account everything is repointed at.

Responses

StatusDescriptionBody
200The merge's outcome.MergeUsersResponse
400The merge is refused: a self-merge, or a tombstoned participant.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404One of the accounts does not exist.ErrorResponse
500The merge failed; nothing was changed.ErrorResponse
GET/api/users/{user_id}/permanent-deletion Bearer token

Preview permanently deleting an account

Counts everything DELETE on this path would remove: sign-in methods, memberships and role grants, the learning records, certification awards, sign-offs, attempts and roster entries that are the person's, and how many rows created or approved by them stay behind without attribution. Nothing is changed. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Responses

StatusDescriptionBody
200What the deletion would take with it.DeletionImpact
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No user has that id.ErrorResponse
500The database could not be read.ErrorResponse
DELETE/api/users/{user_id}/permanent-deletion Bearer token

Permanently delete an account

Hard-deletes the account and everything that is the person's — sign-in methods, memberships, role grants, learning records with their evidence files, certification awards, sign-offs given and received, quiz and SCORM attempts, event roster entries, imported grades, job aid views and pending email links — and drops their name from rows they created or approved for others. Duplicates previously merged into the account go with it. Irreversible; disabling the account (PATCH /users/{user_id}) is the reversible alternative. Preview the cost with GET first. A caller cannot delete their own account. Requires a superuser's access token.

Parameters

NameInTypeRequiredDescription
user_idpathstring (uuid)yesThe account's id (`users.id`).

Responses

StatusDescriptionBody
204The account and everything it owned are gone.—
400The caller tried to delete their own account.ErrorResponse
401The access token is missing or invalid.ErrorResponse
403The access token's subject is not a superuser.ErrorResponse
404No user has that id.ErrorResponse
500The database could not be reached, or the write failed.ErrorResponse