> ## 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.

# API Keys

> Issue, scope, and revoke keys for programmatic access to TextQL

API keys authenticate programmatic requests to TextQL — the SDKs, the public RPC API, and any integration that cannot sign in as a person. Keys are managed from **Settings → Developers**.

Every key has two independent properties, and most confusion comes from mixing them up:

* **Owner** — the member the key authenticates *as*. This decides whose personal shares, connector access and ownership the key inherits.
* **Assumed roles** — the roles the key is allowed to use. This decides which permissions apply.

Roles cannot substitute for ownership. A large part of TextQL's access model is keyed to a member ID rather than a role: objects shared directly with a person, connector grants, the creator bypass on Data Apps and dashboards, and audit attribution. A key with the `admin` role but the wrong owner will still be denied a Data App that was shared with a specific colleague.

## Key Types

| Type                | Acts as                       | Required permission     |
| ------------------- | ----------------------------- | ----------------------- |
| **Personal**        | You                           | None — always available |
| **Service Account** | A non-human member you create | `organization:write`    |

Service accounts are the right choice for integrations. Because the key belongs to a member that is not a person, its access does not disappear when someone changes teams or leaves, and you grant it roles directly.

## Assumed Roles

When you create a key you may select which of the owner's roles it can use. Leaving the picker empty inherits every role the owner holds at that moment.

<Warning>
  **Assumed roles are a snapshot taken when the key is created. They never update.**

  Granting the owner a new role later does **not** widen an existing key. If you make a key while you are a `member` and are promoted to `admin` an hour later, that key still behaves as a `member` and will be denied admin-only endpoints. Issue a new key after the role change.
</Warning>

The snapshot is intersected with the owner's live roles on every request, so the guarantee runs one way: a key can lose access when a role is removed, but never gain access when one is added.

You can only select roles the owner already has.

A key showing **All roles** in the list is a legacy key created before role snapshots existed. Those fall back to the owner's live roles and *do* track role changes.

## Keep Access to the Owner's Own Items

This option controls whether the key behaves like the person or like the role.

* **Off** — the key resolves access through its assumed roles alone. Items shared directly with the owner are invisible, as though the key were the role rather than the person. This is the tighter setting and the right default for a narrowly scoped integration.
* **On** — items shared directly with the owner remain reachable, in addition to whatever the roles grant. Permissions are still capped by the assumed roles.

## Visibility and Revocation

| Capability                        | Required permission  |
| --------------------------------- | -------------------- |
| Create a personal key             | None                 |
| List your own keys                | None                 |
| List every key in the org         | `organization:read`  |
| Revoke or rotate any member's key | `organization:write` |

Organization readers can review keys belonging to other human members from the read-only **Other Members** tab. Organization writers can also revoke or rotate them there.

Keys belonging to TextQL staff accounts are filtered out of org-wide lists for anyone who is not a superadmin, so a list may show fewer keys than exist.

A key's secret is shown exactly once, at creation. TextQL stores only a hash and cannot reissue it. If a secret is lost, revoke the key and create a new one.

## Using a Key

Send the key in the `tql_api_key` header. The public RPC surface is served under `/rpc/public`.

```bash theme={null}
curl -X POST https://app.textql.com/rpc/public/textql.rpc.public.app.AppService/GetApp \
  -H "Content-Type: application/json" \
  -H "Connect-Protocol-Version: 1" \
  -H "tql_api_key: $TEXTQL_API_KEY" \
  -d '{"appId": "..."}'
```

The SDKs read the key from the `TEXTQL_API_KEY` environment variable and append the `/rpc/public` prefix themselves — pass only the host when overriding the base URL.

## Troubleshooting

| Response                                           | What it usually means                                                                                                                                       |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 invalid API key: API key not found`           | The key belongs to a different environment, or it was revoked. Keys are per-deployment.                                                                     |
| `401 invalid API key format`                       | The value is not the base64 string issued at creation. Do not decode or reformat it.                                                                        |
| `403 admin role required`                          | The key's **assumed roles** do not include `admin`, even if the owner does. Snapshots do not update — issue a new key.                                      |
| `403 permission denied` on an object               | The key's owner has not been granted that object. Roles do not convey individually shared items; check the object's sharing settings.                       |
| `404 not found` on an object you can see in the UI | Often a permission difference rather than a missing record. Agents, for example, are hidden from callers who lack access to every connector the agent uses. |

<Note>
  Role changes do not apply instantly to an active browser session. Roles are embedded in a session token that lives for 15 minutes, so a newly granted role can take up to that long to take effect in the UI. Signing out and back in applies it immediately. The same lag applies to revoked roles.
</Note>

## Best Practices

**Prefer service accounts for anything long-lived**
A personal key inherits one person's access and dies with their account. A service account is owned by the org and its roles are explicit.

**Scope keys to the narrowest role that works**
Leaving assumed roles empty inherits everything the owner has, which is rarely what an integration needs. Select the specific role instead.

**Set an expiry**
Keys default to a 90 day expiry. Long-lived keys should be the exception and should be rotated on a schedule.

**Re-issue keys after a role change**
Because the role snapshot is frozen, promoting someone does not upgrade their existing keys, and the failure surfaces as a confusing permission error rather than an obvious one.

**Audit keys periodically**
Every create, revoke and rotate is recorded in the [Audit Log](/core/admin/audit-log). Review the org-wide key list for keys belonging to people who have left or integrations that were retired.
