> ## Documentation Index
> Fetch the complete documentation index at: https://arize-ax.mintlify.site/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# RBAC REST API

> Manage custom roles, role bindings, and resource restrictions programmatically using the Arize REST API.

The Arize REST API provides endpoints for managing access control programmatically. Use these endpoints to automate role assignment, manage project restrictions, and integrate RBAC into your workflows.

<Note>
  These endpoints are currently in Alpha. Breaking changes are possible. See [API Version Stages](/docs/ax/rest-reference/overview#api-version-stages) for details.
</Note>

## Authentication

All RBAC endpoints require Bearer token authentication:

```
Authorization: Bearer <api-key>
```

Both [User Keys](/docs/ax/security-and-settings/api-keys) and [Service Keys](/docs/ax/security-and-settings/service-keys) are supported. See [Authentication](/docs/ax/rest-reference/overview#authentication) for details.

## Key Concepts

### Permission hierarchy

Arize AX uses a hierarchical RBAC model: **Account → Organization → Space → Project**. Permissions granted at a higher level flow down to resources below it. When a project or dashboard is [restricted](/docs/ax/security-and-settings/sso-and-rbac/resource-restrictions), that inheritance is cut off — only users with an explicit role binding on that resource can access it.

### Permission strings

Each permission follows the `{RESOURCE}_{ACTION}` pattern, for example `DATASET_CREATE` or `PROJECT_READ`. When building a custom role, you specify which permissions to grant. To discover available permissions on managed roles, call [`GET /v2/roles?is_predefined=true`](/docs/api-reference/roles/list-roles) and inspect the `permissions` arrays.

### Managed vs. custom roles

**Managed roles** are built-in system roles (e.g., Admin, Member, Read-only). They cannot be modified or deleted. **Custom roles** are account-scoped, user-defined roles that can hold any combination of permissions. See [Custom Roles](/docs/ax/security-and-settings/sso-and-rbac/custom-roles) for conceptual details.

### Role binding uniqueness

A user can have at most one role binding per resource. To change a user's role, update the existing binding — do not delete and recreate it.

### Privilege escalation prevention

When creating service keys or assigning roles, you can only assign roles at or below your own privilege level. The same ceiling applies to account invitations: `POST /v2/users` and `POST /v2/users/{user_id}/resend-invitation` only let you assign an account role up to your own — see [Who Can Invite Whom](/docs/ax/security-and-settings/sso-and-rbac#who-can-invite-whom).

## User Management

Invite, list, update, and remove users across your account, organizations, and spaces.

<table>
  <thead>
    <tr>
      <th width="100">Method</th>
      <th width="340">Path</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/users</code></td>
      <td>[Create a user](/docs/api-reference/users/create-a-user)</td>
    </tr>

    <tr>
      <td><code>GET</code></td>
      <td><code>/v2/users</code></td>
      <td>[List users](/docs/api-reference/users/list-users)</td>
    </tr>

    <tr>
      <td><code>GET</code></td>
      <td><code>/v2/users/\{user\_id}</code></td>
      <td>[Get a user](/docs/api-reference/users/get-a-user)</td>
    </tr>

    <tr>
      <td><code>PATCH</code></td>
      <td><code>/v2/users/\{user\_id}</code></td>
      <td>[Update a user](/docs/api-reference/users/update-a-user)</td>
    </tr>

    <tr>
      <td><code>DELETE</code></td>
      <td><code>/v2/users/\{user\_id}</code></td>
      <td>[Delete a user](/docs/api-reference/users/delete-a-user)</td>
    </tr>

    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/users/\{user\_id}/resend-invitation</code></td>
      <td>[Resend a user invitation](/docs/api-reference/users/resend-a-user-invitation)</td>
    </tr>

    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/users/\{user\_id}/reset-password</code></td>
      <td>[Trigger a password-reset email for a user](/docs/api-reference/users/trigger-a-password-reset-email-for-a-user)</td>
    </tr>

    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/organizations/\{org\_id}/users</code></td>
      <td>[Add a user to an organization](/docs/api-reference/organizations/add-a-user-to-an-organization)</td>
    </tr>

    <tr>
      <td><code>DELETE</code></td>
      <td><code>/v2/organizations/\{org\_id}/users/\{user\_id}</code></td>
      <td>[Remove a user from an organization](/docs/api-reference/organizations/remove-a-user-from-an-organization)</td>
    </tr>

    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/spaces/\{space\_id}/users</code></td>
      <td>[Add a user to a space](/docs/api-reference/spaces/add-a-user-to-a-space)</td>
    </tr>

    <tr>
      <td><code>DELETE</code></td>
      <td><code>/v2/spaces/\{space\_id}/users/\{user\_id}</code></td>
      <td>[Remove a user from a space](/docs/api-reference/spaces/remove-a-user-from-a-space)</td>
    </tr>
  </tbody>
</table>

For `invite_mode` options when creating a user, see [Invite a user via API](#invite-a-user-via-api) in the Common Workflows section below.

<Info>
  [developer access](/docs/ax/security-and-settings/sso-and-rbac#developer-access) is derived from the account role you assign. It is included in user responses, and `PATCH /v2/users/{user_id}` accepts it as a manual override of the role-derived value. `POST /v2/users` and `POST /v2/users/{user_id}/resend-invitation` also enforce the account [invitation role ceiling](/docs/ax/security-and-settings/sso-and-rbac#who-can-invite-whom): you can only invite (or resend an invite to) a role at or below your own.
</Info>

## Roles

Manage custom and managed roles for your account.

<table>
  <thead>
    <tr>
      <th width="100">Method</th>
      <th width="250">Path</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>GET</code></td>
      <td><code>/v2/roles</code></td>
      <td>[List roles](/docs/api-reference/roles/list-roles)</td>
    </tr>

    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/roles</code></td>
      <td>[Create a role](/docs/api-reference/roles/create-a-role)</td>
    </tr>

    <tr>
      <td><code>GET</code></td>
      <td><code>/v2/roles/\{role\_id}</code></td>
      <td>[Get a role](/docs/api-reference/roles/get-a-role)</td>
    </tr>

    <tr>
      <td><code>PATCH</code></td>
      <td><code>/v2/roles/\{role\_id}</code></td>
      <td>[Update a role](/docs/api-reference/roles/update-a-role)</td>
    </tr>

    <tr>
      <td><code>DELETE</code></td>
      <td><code>/v2/roles/\{role\_id}</code></td>
      <td>[Delete a role](/docs/api-reference/roles/delete-a-role)</td>
    </tr>
  </tbody>
</table>

<Info>
  Managed roles (e.g., Admin, Member, Read-only) cannot be updated or deleted.
</Info>

### List roles

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl https://api.arize.com/v2/roles \
  -H "Authorization: Bearer <api-key>"
```

You can filter by `is_predefined=true` or `is_predefined=false` to see only built-in or custom roles. Responses are paginated — use `limit` and `cursor` parameters for large role lists.

```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "roles": [
    {
      "id": "Rol001",
      "name": "Dataset Manager",
      "description": "Can manage datasets and run experiments",
      "permissions": [
        "DATASET_READ",
        "DATASET_CREATE",
        "DATASET_UPDATE",
        "DATASET_DELETE",
        "EXPERIMENT_READ",
        "EXPERIMENT_CREATE"
      ],
      "is_predefined": false,
      "created_at": "2024-06-01T10:00:00Z",
      "updated_at": "2024-06-01T10:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}
```

### Get a role

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl https://api.arize.com/v2/roles/<role-id> \
  -H "Authorization: Bearer <api-key>"
```

### Create a custom role

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X POST https://api.arize.com/v2/roles \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Dataset Manager",
    "description": "Can manage datasets and run experiments",
    "permissions": [
      "DATASET_READ",
      "DATASET_CREATE",
      "DATASET_UPDATE",
      "DATASET_DELETE",
      "DATASET_EXAMPLE_READ",
      "DATASET_EXAMPLE_CREATE",
      "EXPERIMENT_READ",
      "EXPERIMENT_CREATE"
    ]
  }'
```

<Tip>
  To discover available permission values on managed roles, call [`GET /v2/roles?is_predefined=true`](/docs/api-reference/roles/list-roles) and inspect the `permissions` arrays.
</Tip>

### Update a custom role

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X PATCH https://api.arize.com/v2/roles/<role-id> \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "permissions": [
      "DATASET_READ",
      "DATASET_CREATE",
      "EXPERIMENT_READ"
    ]
  }'
```

<Warning>
  When updating `permissions`, the provided list **replaces** the entire set — it is not a merge. Include all permissions you want the role to have.
</Warning>

Managed roles cannot be updated. Attempting to do so returns a `403 Forbidden`.

### Delete a custom role

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X DELETE https://api.arize.com/v2/roles/<role-id> \
  -H "Authorization: Bearer <api-key>"
```

Roles are soft-deleted. Managed roles cannot be deleted.

## Role Bindings

Assign roles to users on specific resources (spaces or projects).

<table>
  <thead>
    <tr>
      <th width="100">Method</th>
      <th width="300">Path</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/role-bindings</code></td>
      <td>[Create a role binding](/docs/api-reference/role-bindings/create-a-role-binding)</td>
    </tr>

    <tr>
      <td><code>GET</code></td>
      <td><code>/v2/role-bindings/\{binding\_id}</code></td>
      <td>[Get a role binding](/docs/api-reference/role-bindings/get-a-role-binding)</td>
    </tr>

    <tr>
      <td><code>PATCH</code></td>
      <td><code>/v2/role-bindings/\{binding\_id}</code></td>
      <td>[Update a role binding](/docs/api-reference/role-bindings/update-a-role-binding)</td>
    </tr>

    <tr>
      <td><code>DELETE</code></td>
      <td><code>/v2/role-bindings/\{binding\_id}</code></td>
      <td>[Delete a role binding](/docs/api-reference/role-bindings/delete-a-role-binding)</td>
    </tr>
  </tbody>
</table>

<Info>
  Each user can have one role binding per resource. Attempting to create a duplicate binding returns a `409 Conflict` error.
</Info>

### Assign a role

The `resource_type` field accepts `SPACE` or `PROJECT`.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X POST https://api.arize.com/v2/role-bindings \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "role_id": "<role-id>",
    "user_id": "<user-id>",
    "resource_type": "PROJECT",
    "resource_id": "<project-id>"
  }'
```

```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "id": "Rbd001",
  "role_id": "<role-id>",
  "user_id": "<user-id>",
  "resource_type": "PROJECT",
  "resource_id": "<project-id>",
  "created_at": "2024-06-01T10:00:00Z",
  "updated_at": "2024-06-01T10:00:00Z"
}
```

### Get a role binding

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl https://api.arize.com/v2/role-bindings/<binding-id> \
  -H "Authorization: Bearer <api-key>"
```

### Update a role binding

To change the role assigned to a user, update the binding with a new `role_id`:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X PATCH https://api.arize.com/v2/role-bindings/<binding-id> \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "role_id": "<new-role-id>"
  }'
```

Only the `role_id` can be updated. The user, resource type, and resource cannot be changed — delete and recreate the binding instead.

### Delete a role binding

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X DELETE https://api.arize.com/v2/role-bindings/<binding-id> \
  -H "Authorization: Bearer <api-key>"
```

## Resource Restrictions

Mark projects or dashboards as restricted so only users with explicit role bindings can access them.

<table>
  <thead>
    <tr>
      <th width="100">Method</th>
      <th width="380">Path</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>GET</code></td>
      <td><code>/v2/resource-restrictions</code></td>
      <td>[List resource restrictions](/docs/api-reference/resource-restrictions/list-resource-restrictions-the-caller-is-permitted-to-manage)</td>
    </tr>

    <tr>
      <td><code>POST</code></td>
      <td><code>/v2/resource-restrictions</code></td>
      <td>[Restrict a resource](/docs/api-reference/resource-restrictions/restrict-a-resource)</td>
    </tr>

    <tr>
      <td><code>DELETE</code></td>
      <td><code>/v2/resource-restrictions/\{resource\_id}</code></td>
      <td>[Unrestrict a resource](/docs/api-reference/resource-restrictions/unrestrict-a-resource)</td>
    </tr>
  </tbody>
</table>

Supported resource types are `PROJECT` and `DASHBOARD`.

<Note>
  Restricting a dashboard (marking it opt-in) works today via this endpoint. Granting a user access to it does not yet: `POST /v2/role-bindings` only accepts `SPACE` and `PROJECT` as of this writing, so dashboard role bindings must be assigned in the UI once that flow ships. Restricting a dashboard before that UI is available leaves it accessible only to space/org/account admins.
</Note>

### List resource restrictions

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl "https://api.arize.com/v2/resource-restrictions?resource_type=DASHBOARD" \
  -H "Authorization: Bearer <api-key>"
```

`resource_type` is optional; omit it to list restrictions of all supported types. Use `limit` and `cursor` to page.

<Warning>
  Results are authorization-filtered after a page is read, so a page can contain fewer items than `limit` — or be empty — while `has_more` is still `true`. Keep paging with `cursor` until `has_more` is `false`; do not stop on an empty page.
</Warning>

### Restrict a resource

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
# Restrict a project
curl -X POST https://api.arize.com/v2/resource-restrictions \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{"resource_id": "<project-id>"}'
```

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
# Restrict a dashboard
curl -X POST https://api.arize.com/v2/resource-restrictions \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{"resource_id": "<dashboard-id>"}'
```

The request body takes only `resource_id` — the resource type is inferred from the ID itself, so there's no separate `resource_type` field to set. This operation is idempotent — restricting an already-restricted resource returns successfully.

### Remove a restriction

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X DELETE https://api.arize.com/v2/resource-restrictions/<resource-id> \
  -H "Authorization: Bearer <api-key>"
```

See [Resource Restrictions](/docs/ax/security-and-settings/sso-and-rbac/resource-restrictions) for details on how restrictions affect access.

## Schema Reference

### Role

| Field | Type | Constraints |
| - | - | - |
| `id` | string | Read-only |
| `name` | string | Required, max 255 chars, unique within account |
| `description` | string | Optional, max 1000 chars |
| `permissions` | string\[] | Required on create, min 1 item |
| `is_predefined` | boolean | `true` = system role (immutable) |
| `created_at` | datetime | Read-only |
| `updated_at` | datetime | Read-only |

### RoleBinding

| Field | Type | Constraints |
| - | - | - |
| `id` | string | Read-only |
| `role_id` | string | Required; only mutable field on PATCH |
| `user_id` | string | Required; immutable after creation |
| `resource_type` | string | `SPACE` or `PROJECT`; immutable after creation. Dashboard role bindings aren't yet supported through this endpoint. |
| `resource_id` | string | Required; immutable after creation |
| `created_at` | datetime | Read-only |
| `updated_at` | datetime | Read-only |

### ResourceRestriction

| Field | Type | Constraints |
| - | - | - |
| `resource_type` | string | Read-only; `PROJECT` or `DASHBOARD`, inferred from `resource_id` |
| `resource_id` | string | Required |
| `created_at` | datetime | Read-only |

## Common Workflows

### Invite a user via API

Use `POST /v2/users` to invite a user programmatically. The `invite_mode` field controls how the invited user completes sign-up:

| `invite_mode` | Behavior |
| - | - |
| `email_link` | Sends the user an email with a sign-up link. Default for most accounts. |
| `temporary_password` | Sends the user a temporary password. Useful when email delivery is restricted. |
| `none` | No email is sent. Use only when **Allow Only SAML Logins** is enabled — users log in directly via SSO without a separate sign-up step. |

<Warning>
  Using `invite_mode: none` on an account where **Allow Only SAML Logins** is not enabled will leave the user with no way to complete sign-up.
</Warning>

### Onboard a team member with scoped project access

1. **Restrict the project** (if not already restricted):
   `POST /v2/resource-restrictions` with the project ID.
2. **Create a role binding** for the user:
   `POST /v2/role-bindings` with the user ID, a role ID (e.g., Member), and the project ID.

### Restrict a dashboard

1. **Create a custom role** holding `DASHBOARD_READ` (and `DASHBOARD_UPDATE` / `DASHBOARD_DELETE` if needed):
   `POST /v2/roles` — there are no managed dashboard roles.
2. **Restrict the dashboard**:
   `POST /v2/resource-restrictions` with the dashboard ID.
3. **Grant access** to specific users once dashboard role bindings are available in the UI — `POST /v2/role-bindings` doesn't yet accept a dashboard resource. Until then, only space, organization, and account admins can access a restricted dashboard.

### Create a least-privilege custom role and assign it

1. **Identify the permissions** your use case requires — call [`GET /v2/roles?is_predefined=true`](/docs/api-reference/roles/list-roles) to inspect what permissions managed roles use.
2. **Create a custom role** with only those permissions:
   `POST /v2/roles`
3. **Restrict the target project** so access is opt-in:
   `POST /v2/resource-restrictions`
4. **Bind the role to the user** on that project:
   `POST /v2/role-bindings`

### Rotate a user's role on a project

1. **Find the existing binding ID** (use your own records or `GET /v2/role-bindings/<id>`).
2. **Update the binding** with the new role:
   `PATCH /v2/role-bindings/<binding-id>` with `{ "role_id": "<new-role-id>" }`

### Audit roles and access

1. **List all custom roles**: `GET /v2/roles?is_predefined=false`
2. **List managed roles**: `GET /v2/roles?is_predefined=true`
3. **Inspect a specific binding**: `GET /v2/role-bindings/<binding-id>`

## Full API Reference

For complete request and response schemas, see the API reference:

* [Users](/docs/api-reference/users/list-users)
* [Roles](/docs/api-reference/roles/list-roles)
* [Role Bindings](/docs/api-reference/role-bindings/list-role-bindings)
* [Resource Restrictions](/docs/api-reference/resource-restrictions/restrict-a-resource)

You can also explore and try out all endpoints interactively at [api.arize.com/v2/docs](https://api.arize.com/v2/docs).

<Tip>
  For additional support, join the [Arize Community Slack](https://join.slack.com/t/arize-ai/shared_invite/zt-2w57bhem8-hq24MB6u7yE_ZF_ilOYSBw).
</Tip>
