---
title: "Authentication and scopes"
description: "Authenticate to the Lookio API with an API key or an OAuth token, and choose the scopes that decide what a credential may do."
canonical: "https://lookio.app/docs/developers/authentication"
updated: "2026-11-08"
---

# Authentication and scopes

Send a credential in the `Authorization` header of every request.

```bash
curl https://api.lookio.app/v1/account \
  -H "Authorization: Bearer $LOOKIO_API_KEY"
```

A key looks like `sk_live_<id>_<secret>_<checksum>`. Never put it in a URL or in front-end code. The API sends no CORS headers on `/v1`, so a browser cannot call it.

## Two kinds of credential

|                      | API key                                                                                                                                           | OAuth connection                                                                |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| **Created by**       | An admin, in **Developer tools**.                                                                                                                 | A person, who approves it in the app. Used by the CLI login and by MCP clients. |
| **Belongs to**       | The workspace.                                                                                                                                    | The person who approved it.                                                     |
| **Permissions**      | Its scopes, and optionally a list of assistants.                                                                                                  | Its scopes, narrowed by the person's own Lookio permissions at every call.      |
| **Conversations**    | Keeps the conversations it starts. Continue them with `conversation_id`. Name your users with `end_user`, or send `store: false` to keep nothing. | Keeps the person's own chats. Continue them with `conversation_id`.             |
| **Members and tags** | Never.                                                                                                                                            | Admins only, with the `admin` scope.                                            |
| **Lifetime**         | Until it expires or is revoked.                                                                                                                   | Access token 30 days, renewed by a refresh token valid 90 days.                 |

Use an API key for a job or a backend. Use a connection for a person's CLI or AI client. See [API keys](/docs/guides/api-keys).

## Scopes

A scope is a permission on a credential. If the credential does not have the scope of an endpoint, the request fails with `insufficient_scope`. Each endpoint page names its scope.

| Scope               | Lets the credential                                                                                                     |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `account:read`      | See your workspace, plan, and credit balance.                                                                           |
| `usage:read`        | See credit usage over time, and the answer log: when, which assistant, which channel, what it cost. Not the questions.  |
| `resources:read`    | See resources, their indexed text, their status, the pages of a site, and the indexing log.                             |
| `resources:write`   | Add, edit and re-index resources, and sync sites. Free of credits.                                                      |
| `resources:delete`  | Delete resources and their indexed passages.                                                                            |
| `assistants:read`   | See assistants and their settings.                                                                                      |
| `assistants:write`  | Create and edit assistants and widgets, choose what they read, and read their conversations in the logs.                |
| `assistants:delete` | Delete assistants and widgets. Their resources stay.                                                                    |
| `query`             | Ask questions, at the mode's price, and search passages, at 1 credit. Rename and delete conversations, and send thumbs. |
| `admin`             | Manage members' permissions and assistants, and read the activity log. Admins only.                                     |
| `webhooks:write`    | Manage the webhooks that tell your systems when an import ends. Not available yet: no endpoint uses it.                 |

`query` is the only scope that spends credits. Deleting has its own scopes, so a key can create and edit without the right to delete. Widgets use the assistant scopes.

`GET /v1/account` (MCP: `get_account`) returns `credential.scope_descriptions`. It gives one sentence for each scope of the credential.

### What a credential can really do

`GET /v1/account` returns the scopes that a credential can really use. They are its granted scopes, minus:

- for a connection, what the admin does not let members give to AI clients;
- for a connection, what the person's own permissions refuse, checked at each call;
- for an API key, `admin`. A key never manages people.

On the Pro and Custom plans, a person also needs the `api` permission for the REST API and the CLI, and `mcp` for an MCP client. If not, the call fails with `permission_denied`.

## OAuth

An MCP client or the CLI gets a token through OAuth 2.1. You do not need this to use an API key.

```mermaid title="A personal connection, with PKCE"
sequenceDiagram
  participant C as CLI or MCP client
  participant B as Your browser
  participant L as Lookio
  C->>B: Open /authorize (code_challenge S256)
  B->>L: Sign in, choose the workspace, approve scopes
  L-->>C: Authorization code (redirect)
  C->>L: POST /oauth/token (code + code_verifier)
  L-->>C: Access token and refresh token
  C->>L: API or MCP calls with the token
```

- **Authorization code with PKCE.** PKCE with `S256` is required. `plain` is refused.
- **Device code** (RFC 8628) for machines without a browser. Poll every 5 seconds. The code is valid for 15 minutes.
- **Refresh tokens** renew access tokens.
- **Client registration** by Client ID Metadata Document (an `https` client id) or by dynamic registration (RFC 7591). All clients are public: there is no client secret.

The endpoints are at `/.well-known/oauth-authorization-server`, on `api.lookio.app` and on `mcp.lookio.app`.

| Endpoint             | Path              |
| -------------------- | ----------------- |
| Authorization        | `/authorize`      |
| Token                | `/oauth/token`    |
| Device authorization | `/oauth/device`   |
| Registration         | `/oauth/register` |
| Revocation           | `/oauth/revoke`   |

You can request a token for one resource (RFC 8707): `https://api.lookio.app/v1`, `https://mcp.lookio.app/mcp` or `https://mcp.lookio.app/mcp/code`. Such a token works only there. A token without `resource` works everywhere.

See [Connect an AI client](/docs/developers/mcp/connect) for the MCP flow.

## Failures

| Code                      | Meaning                                                                    |
| ------------------------- | -------------------------------------------------------------------------- |
| `authentication_required` | No `Authorization: Bearer` header.                                         |
| `invalid_api_key`         | The credential is malformed or unknown.                                    |
| `api_key_expired`         | The credential passed its expiry.                                          |
| `api_key_revoked`         | An admin revoked it, or the person behind a connection left the workspace. |
| `insufficient_scope`      | Valid credential, missing scope.                                           |
| `permission_denied`       | The person does not have a Lookio permission.                              |

Lookio limits repeated failed sign-ins from one address. See [Rate limits](/docs/developers/rate-limits).
