# List a user's roles (admin)

`GET /v1/admin/orgs/{slug}/users/{id}/roles`

Returns the roles the user holds in `slug`'s organization, in role
order, and the roles the caller may grant to or revoke from them.
Requires the `roles.read` permission. The id must belong to
`slug`'s organization; one that belongs to another is answered as 404,
exactly like an id that does not exist.

## Authorization

- [Bearer token](https://www.nauthera.io/docs/v1/api#auth-bearerAuth) (`bearerAuth`) and [OAuth 2.0](https://www.nauthera.io/docs/v1/api#auth-oauth2) (`oauth2`) with scope `nauthera.roles:read`

Authorization header: The access token, as `Bearer <token>` (RFC 6750 §2.1) or, for a sender-constrained token, `DPoP <token>` (RFC 9449 §7.1). A token minted with a `cnf.jkt` confirmation is only accepted together with a `DPoP` proof signed by that key, whichever of the two schemes carries it.

Requires the permission [`roles.read`](https://www.nauthera.io/docs/v1/api#permission-roles.read): Read who holds which role.

This endpoint is rate limited; it answers `429` when a limit is hit.

## Path parameters

- `slug` (string, required): The organization slug. It names the TENANT OF RECORD for the operation: the rows it reads and writes, the policy its secrets are derived under, and the organization its audit record is filed against all come from this organization. A token is admitted only through its own slug, except a main-org token, which may be admitted through any — so cross-tenant administration is performed through the TARGET tenant's slug, never by naming its resources through another. A token reaching for a slug it may not be admitted through is refused identically whether that organization exists or not. Constraints: 1–64 characters.
- `id` (string, required): The user id (UUID). Constraints: 1–64 characters.

## Headers

- `DPoP` (string, optional): DPoP proof JWT (RFC 9449 §4). REQUIRED on a request presenting a DPoP-bound access token, and it must be signed by the key named in the token's `cnf.jkt` and cover this request's method and URI. The proof is read from the request itself rather than from this decoded parameter, so every protected operation is covered whether or not it declares it.

## Responses

### 200 OK

The user's roles in the organization.

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`

The roles a user holds in the organization the path names.

- `roles` (array of enums, required): A built-in role id, as stored and as the `roles` claim carries it: `<scope>-<job>`, the scope being the organization the role is held in (`org`) or every organization (`platform`). A platform role counts only when held in the main organization, where it admits its holder to every organization. What each grants is the fixed table in `internal/rbac`. One of: `"org-viewer"`, `"org-auditor"`, `"org-support"`, `"org-user-admin"`, `"org-owner"`, `"platform-viewer"`, `"platform-auditor"`, `"platform-owner"`.
- `assignable` (array of enums, required): The roles the caller may grant to or revoke from this user, in role order: none without `roles.assign` or on the caller's own account, a platform role only in the main organization, and only a role whose permissions and reach the caller's own roles cover. One of: `"org-viewer"`, `"org-auditor"`, `"org-support"`, `"org-user-admin"`, `"org-owner"`, `"platform-viewer"`, `"platform-auditor"`, `"platform-owner"`.

  Each item: A built-in role id, as stored and as the `roles` claim carries it: `<scope>-<job>`, the scope being the organization the role is held in (`org`) or every organization (`platform`). A platform role counts only when held in the main organization, where it admits its holder to every organization. What each grants is the fixed table in `internal/rbac`.

### 401 Unauthorized

Problem details (RFC 9457) with an RFC 6750 / RFC 9449 challenge.

Content type: `application/problem+json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### 403 Forbidden

Problem details (RFC 9457) with an RFC 6750 / RFC 9449 challenge.

Content type: `application/problem+json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### 404 Not Found

Problem details (RFC 9457).

Content type: `application/problem+json`

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### 429 Too Many Requests

Too many requests.

Content type: `application/problem+json`

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### Error

Problem details (RFC 9457) with an RFC 6750 / RFC 9449 challenge.

Content type: `application/problem+json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

## Example request

```bash
curl -g 'https://auth.example.com/v1/admin/orgs/{slug}/users/{id}/roles' \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Example response (200)

```json
{
  "roles": [
    "org-viewer"
  ],
  "assignable": [
    "org-viewer"
  ]
}
```
