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

# Role-Based Access Control (RBAC)

> Manage who can access what in TextQL using roles and permissions

Role-based access control (RBAC) is the method TextQL uses to manage what users can see and do within your organization. Instead of configuring permissions for each person individually, you define roles that reflect job functions, assign permissions to those roles, and then assign users to the appropriate role.

## What is Role-Based Access Control?

RBAC is an access control model built around three concepts:

1. **Roles** — Named groupings that represent a job function or authority level (e.g., admin, member)
2. **Permissions** — Specific actions a role is allowed to perform (e.g., create connectors, manage members, view billing)
3. **Assignments** — Users are placed into a role; they inherit all of that role's permissions automatically

When an employee joins, changes teams, or leaves, you update their role rather than hunting down individual permission settings.

## Benefits of RBAC

* **Less per-user configuration** — Set permissions once on a role; every user assigned to it inherits them automatically. Role changes (e.g., someone moving teams) are a single update, not a sweep across individual accounts.
* **Least-privilege by default** — Access is scoped to what each role needs. Sensitive areas like billing, SCIM, and org settings stay out of reach for general members without any extra effort.
* **Consistent onboarding** — New users get the right access on day one by assigning a role, with no risk of missing a permission or over-granting access.
* **Easier compliance** — Access is tied to named roles rather than individuals, making it straightforward to show auditors who can do what. See [Audit Log](/core/admin/audit-log) for a record of actions taken.

## Limitations of RBAC

* **No record-level control** — Permissions apply to a whole resource type, not individual records. You cannot restrict a member to only their team's data within the same connector.
* **Write bundles creation and editing** — Granting `write` on a resource lets users both create their own and edit any publicly shared version of it. These cannot currently be separated.
* **Two roles may not fit every org** — The built-in admin/member split covers most cases, but organizations with many distinct access tiers may find the options limiting.
* **Roles need periodic review** — Access that was appropriate when someone joined may not be correct six months later. Stale role assignments accumulate without active maintenance.

## When to Use RBAC

TextQL's role system works well for organizations that have a clear split between users who manage the platform (admins) and users who use it to run analyses (members). Most deployments follow this pattern.

Where it requires more thought is when different groups of members need meaningfully different levels of access — for example, a team that should be able to publish connectors versus one that should only be able to run chats. In those cases, configure the member role to the most restrictive common baseline and use [SCIM group mappings](/core/admin/scim) if your IdP can enforce the distinction at provisioning time.

## How to Configure RBAC in TextQL

TextQL has two system roles. Each user in your organization is assigned exactly one.

| Role | Description |
| - | - |
| **admin** | Full read and write access to all permissions across the organization |
| **member** | Scoped access — can create and use core features, but cannot manage org-level settings, billing, SSO/SCIM, or other members |

Roles are configured from **Settings → Roles**. Click **Manage Permissions** on any role to view or edit its permission set.

### Creating a Role

1. Navigate to **Settings → Roles**
2. Click **Create Role** and give it a name
3. Click **Manage Permissions** on the new role to configure its permission set
4. Toggle the permissions you want this role to have across each resource category

### Importing and Exporting Roles

In **Settings → Roles**, choose **Import / Export → Import roles** to prepare multiple roles and their permissions together. You need both `role:write` and `role_permission:write`. Uploading opens an editable draft: review the role names, permissions, and model settings before approving creation. Nothing is created until you approve. Download the example from the import dialog, or prepare a UTF-8 CSV or XLSX worksheet with this layout:

```csv theme={null}
Permission,Analyst,Reviewer
chat:read,yes,yes
chat:write,yes,
dashboard:read,yes,yes
Allow model switching,Yes,No
Default model,Org Default,Org Default
Allowed models,All,All
```

Select your file and click **Preview draft**. Rename roles, toggle grants, edit model settings, or remove roles and rows in the matrix. You can also add permission or model-setting rows. Click **Approve and create** when the draft is ready. If the server rejects a value or a name collision, your edits stay in the dialog so you can correct and retry. Files are parsed on the backend; edits remain in the open dialog; canceling or replacing the file discards them.

The first column must be `Permission`; every other column names a new role. Permission rows use `resource:action` names from the exported matrix. A cell containing `✓`, `true`, `yes`, `1`, or `x` grants the permission. Blank cells, `false`, `no`, and `0` do not grant it. Omitted permission rows do not add grants, but the normal permission hierarchy still applies: for example, a write grant can imply read access. A header-only CSV creates roles with no permissions.

The three model-setting rows are optional. `Default model` accepts `Org Default`, `System Default`, or a model identifier such as `sonnet-5`. `Allowed models` accepts `All` or semicolon-separated identifiers such as `sonnet-5;haiku-4-5`; a blank cell is rejected when this row is present. Organization and deployment model policies still apply.

To reuse an XLSX export, remove columns for roles you do not want to create and rename the remaining roles. The parser reads the **Role Permissions** worksheet, or the first worksheet if that name is absent. Model lists in exported cell notes are preserved. Replace formulas with values before uploading. If you save the workbook as CSV, replace `N selected` cells with explicit model identifiers because CSV cannot preserve notes. Legacy XLS files are not supported.

Files may contain up to 100 roles and be at most 1 MiB. Existing role or group names, reserved names, duplicate rows, and invalid values reject the entire import. No existing roles are changed and no partial batch is saved. Imports do not assign members or copy object-specific sharing.

Choose **Import / Export → Export roles** to download a CSV containing all current roles and every permission in the backend catalog, including permissions no role grants yet. New permissions appear automatically. Export requires `role:read` and `role_permission:read`. CSV model lists use semicolon-separated names.

The Python SDK exposes the same flow. Given an authenticated `client` and a
presigned **download** URL:

```python theme={null}
preview = client.rbac.parse_role_import(file_url=presigned_url)
draft = preview.draft
assert draft is not None

# Review or edit before creating any roles.
draft.names[0] = "New analysts"
created = client.rbac.import_roles(draft=draft)
```

To upload a local file first, request the upload URL and PUT the original bytes:

```python theme={null}
from pathlib import Path
from urllib.parse import urlparse
import httpx

file = Path("roles.csv")  # .xlsx is also accepted
upload = client.rbac.create_import_upload(
    file_name=file.name,
    size_bytes=file.stat().st_size,
)
headers = {"Content-Type": upload.content_type}
if urlparse(upload.upload_url).hostname.endswith(".blob.core.windows.net"):
    headers["x-ms-blob-type"] = "BlockBlob"
httpx.put(upload.upload_url, content=file.read_bytes(), headers=headers).raise_for_status()
presigned_url = upload.file_url
```

Export all roles as CSV without specifying a format:

```python theme={null}
import base64
from pathlib import Path

exported = client.rbac.export_roles()
Path(exported.file_name).write_bytes(base64.b64decode(exported.data))
```

The `data` field is base64-encoded in the JSON API. For an Excel export, call
`export_roles(format_="ROLE_PERMISSIONS_EXPORT_FORMAT_XLSX")` explicitly.
The SDK method names come from the public API's Speakeasy configuration and are
available in SDK releases generated from this contract.

### Assigning a Role

1. Navigate to **Settings → Members**
2. Find the user and click the role dropdown next to their name
3. Select **admin** or **member** and confirm

Role changes apply to new sign-ins immediately, but an active session carries its roles in a token that lives for 15 minutes, so an already signed-in user may not see the change until that token renews. Signing out and back in applies it at once. Existing API keys are not affected at all — their roles are fixed when the key is created, so a promoted user needs a new key. See [API Keys](/core/admin/api-keys).

### Permission Reference

Each role is composed of granular permissions grouped by resource. Permissions follow a consistent pattern: `read` (public), `read_private` (private/org-wide), `write` (create/edit public), and `write_private` (create/edit private). Below is a full reference of every configurable permission and what it controls.

<AccordionGroup>
  <Accordion title="API Access Key">
    Controls access to API keys used to authenticate programmatic requests to TextQL.

    | Permission | What it does |
    | - | - |
    | `read` | View and use public API access keys and any private ones explicitly shared with you |
    | `read_private` | View and use all private API access keys, even those not shared with you |
    | `write` | Create and edit public API access keys |
    | `write_private` | Create and edit private API access keys |
    | `delegate` | Create API keys that act as another member (delegated keys), scoped to that member's roles |

    `delegate` lets a role mint API keys that act as any human member of the organization, so grant it only to trusted service accounts. A human target requires `delegate`; `organization:write` alone is not enough. A service-account target continues to require `organization:write`. See [Delegated Keys](/core/admin/api-keys#delegated-keys).

    *Default member permissions: `read` only*
  </Accordion>

  <Accordion title="Audit Log">
    Controls who can view your organization's audit log. See [Audit Log](/core/admin/audit-log).

    | Permission | What it does |
    | - | - |
    | `read` | View the organization audit log |

    Reading the audit log previously required organization `write`, which meant an auditor's role also carried destructive powers — export-sink configuration, SSO and OIDC changes, deleting the organization. It is now grantable on its own. Configuring audit log *exports* still requires organization `write`.

    Existing roles keep what they had: every role that held organization `write` was granted `audit_log:read` automatically, so nothing was revoked. Narrow those roles yourself if you want audit access separated from org administration.

    This permission cannot be granted to an API access key or OAuth token — the audit log is readable only by a signed-in member.

    *Default member permissions: none*
  </Accordion>

  <Accordion title="Billing">
    Controls visibility into your organization's usage and billing data.

    | Permission | What it does |
    | - | - |
    | `read` | View usage across every workspace in your billing tenant, through the `/usage` API and a read-only Usage view in the billing console |

    *Default member permissions: none — billing is admin-only*
  </Accordion>

  <Accordion title="Chat">
    Controls access to conversation threads with Ana.

    | Permission | What it does |
    | - | - |
    | `read` | View public chats and any private chats explicitly shared with you |
    | `read_private` | View all private chat conversations, even those not shared with you |
    | `write` | Create and edit public chats |
    | `write_private` | Create and edit private chats |

    *Default member permissions: `read`, `write`*
  </Accordion>

  <Accordion title="Connector">
    Controls access to data source connections. See [Connectors](/core/datasources/the-connectors-page) for setup details.

    | Permission | What it does |
    | - | - |
    | `read` | View public connectors and any private connectors explicitly shared with you |
    | `read_private` | View all private connectors, even those not shared with you |
    | `write` | Create and edit public connectors |
    | `write_private` | Create and edit private connectors |

    *Default member permissions: `write` (public connectors only). Anyone who can edit a connector can also share it with roles, groups, and members, so a power user with `write` can assign roles to connectors without `role:write`. Selecting a role in the share dialog still requires `role:read`, which the default member role includes.*
  </Accordion>

  <Accordion title="Context">
    Controls access to organization-level context prompts that Ana uses to give business-specific answers. See [Ontology](/core/ontology/overview).

    | Permission | What it does |
    | - | - |
    | `read` | View organization context prompts |
    | `write` | Create, update, and delete organization context prompts |

    *Default member permissions: none*
  </Accordion>

  <Accordion title="Context Policy">
    Controls the **Ontology → Rules and owners** screen: the approval rules that decide which ontology changes need review, and the auto-approve rules that let some through without it. See [Ontology Access Control](/core/ontology/ontology-rbac).

    | Permission | What it does |
    | - | - |
    | `read` | View ontology rules and code owners |
    | `write` | Create, edit, and delete ontology rules and code owners |

    `write` includes `read`. These rules used to sit under the Context permission, so anyone who could edit context prompts could also rewrite the approval policy governing them. Splitting them lets you grant one without the other.

    <Warning>
      Unlike most permission changes, this one does not carry over: a custom role that held Context `write` **loses** access to Rules and owners until you grant it Context Policy `write` explicitly. Only the built-in admin role received it automatically. Check your custom roles after upgrading.
    </Warning>

    *Default member permissions: none*
  </Accordion>

  <Accordion title="Dashboard">
    Controls access to persistent dashboards built from Ana's outputs. See [Dashboards](/core/how-it-works/dashboards).

    | Permission | What it does |
    | - | - |
    | `read` | View public dashboards and any private dashboards explicitly shared with you; also enables chatting with a dashboard (requires `chat:write`) |
    | `read_private` | View all dashboards in the org, even those not shared with you |
    | `write` | Create your own dashboards **and** edit any public dashboard |
    | `write_private` | Edit **any** dashboard in the org, including restricted-access dashboards not shared with you |

    *Default member permissions: `write` (public dashboards)*
  </Accordion>

  <Accordion title="Dataset">
    Controls access to datasets — structured data files that can be uploaded or generated by Ana.

    | Permission | What it does |
    | - | - |
    | `read` | View public datasets |
    | `read_private` | View private datasets |
    | `write` | Create and edit public datasets |
    | `write_private` | Create and edit private datasets |

    *Default member permissions: `write` (public datasets only)*
  </Accordion>

  <Accordion title="Feed">
    Controls access to the feed — posts, comments, and agent activity visible across the organization.

    | Permission | What it does |
    | - | - |
    | `read` | View feed posts and comments |
    | `read_private` | View private feed channels and their posts, even when not explicitly invited |
    | `write` | Create posts, comments, and manage agents |
    | `write_private` | Manage private feed channels and their posts, even when not explicitly invited |

    *Default member permissions: `write`*

    **Chat-level access inheritance**

    Feed post visibility is also gated by chat permissions. A member with feed `read` access will only see a post if they also have `read` access to every chat referenced by that post. Posts that have no chat reference (e.g. plain text posts) are always visible to anyone with feed `read` access. Admins bypass this check and see all posts.
  </Accordion>

  <Accordion title="MCP">
    Controls access to MCP (Model Context Protocol) server configurations used to extend Ana with external tools and data sources.

    | Permission | What it does |
    | - | - |
    | `read` | Read MCP server configurations |
    | `write` | Write/modify MCP server configurations |

    *Default member permissions: `read`*
  </Accordion>

  <Accordion title="Member">
    Controls visibility and management of users in your organization.

    | Permission | What it does |
    | - | - |
    | `read` | View organization members |
    | `write` | Manage organization members (invite, remove) |

    *Default member permissions: `read`*
  </Accordion>

  <Accordion title="Notifications">
    Controls access to notification preferences and in-app alerts.

    | Permission | What it does |
    | - | - |
    | `read` | View own notifications and preferences |
    | `write` | Mark notifications read, update preferences |

    *Default member permissions: `write`*
  </Accordion>

  <Accordion title="Ontology">
    Controls access to the ontology — the structured map of your business data. See [What is Ontology](/core/ontology/overview).

    | Permission | What it does |
    | - | - |
    | `read` | View public ontology schema and any private ontologies explicitly shared with you |
    | `read_private` | View all private ontologies, even those not shared with you |
    | `write` | Create, update, and delete ontology schema, objects, attributes, metrics, and relations |
    | `write_private` | Create and edit private ontologies |

    *Default member permissions: `read`*
  </Accordion>

  <Accordion title="Organization">
    Controls access to top-level organization settings such as name, appearance, and global configuration.

    | Permission | What it does |
    | - | - |
    | `read` | View organization settings |
    | `write` | Manage organization settings |

    *Default member permissions: none — org settings are admin-only*
  </Accordion>

  <Accordion title="Packages">
    Controls access to installable packages that extend TextQL's functionality.

    | Permission | What it does |
    | - | - |
    | `read` | View org packages |
    | `write` | Install/remove org packages |

    *Default member permissions: `read`*
  </Accordion>

  <Accordion title="Playbook">
    Controls access to automated analysis workflows. See [Playbooks](/core/how-it-works/playbooks).

    | Permission | What it does |
    | - | - |
    | `read` | View public playbooks and any private playbooks explicitly shared with you |
    | `read_private` | View all private playbooks, even those not shared with you |
    | `write` | Create and edit public playbooks |
    | `write_private` | Create and edit private playbooks |

    *Default member permissions: `write` (public playbooks only)*
  </Accordion>

  <Accordion title="Role">
    Controls whether a user can view or modify role configurations themselves.

    | Permission | What it does |
    | - | - |
    | `read` | View role settings |
    | `write` | Manage role settings |

    *Default member permissions: `read`*
  </Accordion>

  <Accordion title="SCIM">
    Controls access to SCIM provisioning tokens and OAuth clients used for automated user lifecycle management. See [SCIM](/core/admin/scim).

    | Permission | What it does |
    | - | - |
    | `read` | View SCIM tokens and OAuth clients |
    | `write` | Create and revoke SCIM tokens and OAuth clients |

    *Default member permissions: none — SCIM is admin-only*
  </Accordion>

  <Accordion title="Usage">
    Controls visibility into organization-level usage metrics and ACU consumption data.

    | Permission | What it does |
    | - | - |
    | `read` | View organization usage metrics and ACU data |

    *Default member permissions: none — usage data is admin-only*
  </Accordion>

  <Accordion title="Model Access">
    Controls which AI models members can use and whether they can override the org default. See [Model Management](/core/admin/models) for full model management options.

    > **Important:** Model access is **not** a `resource:action` permission — it is a set of fields on the role itself. `allow_model_switching` is a UI label, not a permission string: there is no `model` (or `model_access`) resource and no `allow_model_switching` permission. Do **not** add it to a role's permission list or to a provisioning permission array (SCIM/OIDC group mapping, JSON role definition, etc.) — it will not resolve and grants nothing. Configure these from **Settings → Models → Role Access**, or set them with the role update API, which is separate from permission assignment.

    | Setting | Role field | What it does |
    | - | - | - |
    | Allow model switching | `allow_model_choice` | Users in this role can override the default model in chat |
    | Default Model | `default_model_id` | The model used when this role starts a new chat (defaults to Org Default) |
    | Allowed Models | `allowed_model_ids` | Which models this role can select from |

    To set these programmatically, call the role update API (`RBACService/UpdateRole`) with the role fields — for example, to enable model switching:

    ```json theme={null}
    { "roleId": "<role-id>", "allowModelChoice": true }
    ```

    `defaultModelId` and `allowedModelIds` take `LlmModel` enum numbers. An empty `allowedModelIds` array is treated as "no change"; send `clearAllowedModelIds: true` to reset the allowed list back to all models.

    *Default member settings: model switching disabled; allowed models and default set by admin*
  </Accordion>
</AccordionGroup>

### Provisioning Roles at Scale with SCIM

If your organization uses an identity provider like Okta or Azure AD, you can provision and deprovision users automatically and map IdP groups to TextQL roles using [SCIM](/core/admin/scim). This is the recommended approach for organizations with more than \~20 users, as it keeps roles in sync with your IdP without manual intervention.

## RBAC Best Practices

**Default new users to member**
When unsure which role to assign, start with member and escalate to admin only when explicitly needed. Admin access should be limited to people who manage the organization's settings and security configuration.

**Base roles on job function, not individuals**
If you find yourself needing highly customized permissions for a single person, it is usually a sign that the permission boundary is better handled at the connector or context level rather than a new role.

**Review role assignments periodically**
Run a quarterly review of your member list and their assigned roles. Look for users who have moved teams or changed responsibilities. The [Audit Log](/core/admin/audit-log) can help surface accounts that have not been active recently.

**Pair with SSO and SCIM for lifecycle management**
Manual role assignment works for small teams, but human error accumulates. Connecting TextQL to your IdP via [SSO](/core/admin/sso) and [SCIM](/core/admin/scim) ensures that when someone leaves the company or changes teams, their TextQL access updates automatically.

**Restrict private resource access carefully**
`read_private` and `write_private` permissions grant org-wide visibility into resources that individual users have intentionally kept private. Grant these sparingly and only to roles that genuinely need cross-user visibility.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.