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

# Authentication and scopes

> Every request carries a workspace API key as a Bearer token, and the key's scopes decide what it may read and change.

Send your API key in the `Authorization` header of every request, as `Bearer` followed by a space and the key. The header is the only place a key is read. There is no query-string form, so a key never ends up in a server log or a browser history by way of a URL.

```bash curl theme={null}
curl https://api.citino.ai/v1/projects -H "Authorization: Bearer $CITINO_API_KEY"
```

```python Python theme={null}
import os
import requests

key = os.environ["CITINO_API_KEY"]
session = requests.Session()
session.headers["Authorization"] = f"Bearer {key}"

response = session.get("https://api.citino.ai/v1/projects")
response.raise_for_status()
```

```javascript JavaScript theme={null}
const headers = { Authorization: `Bearer ${process.env.CITINO_API_KEY}` };
const response = await fetch("https://api.citino.ai/v1/projects", { headers });
if (!response.ok) throw new Error(`Citino answered ${response.status}`);
```

## Making a key

Keys are made on **Workspace settings → API keys**, by a workspace owner or admin. A key cannot be made with another key, so a key that leaks cannot mint its own successor.

When you create one you choose:

* **A name.** Name it after where it will live ("the nightly warehouse job", "Claude Code on my laptop"), so you know which one to revoke.
* **What it may read.** Every read scope is ticked to start with. Untick the ones the job does not need.
* **What it may change.** The two write scopes are off unless you tick them. A key with a write scope acts as you, with your role: see [Writes](https://docs.citino.ai/api/writes.md).
* **An expiry date**, if you want one. The key works through the end of that day (UTC) and stops after it.
* **Which projects**, if your workspace has more than one. Leave them all unticked for every project. A key limited to some projects cannot see the others at all: they are missing from the project list, and asking for one by id answers `404`.

The whole key is shown once, in its own box, the moment it is made. Copy it then. After that, the list shows only its short prefix, its scopes, its projects, its rate limit, its expiry, who made it, and when it was last used.

> [!WARNING]
>
> Keep keys out of source control and out of chat messages. Read the key from an environment variable or your platform's secret store, as every sample on this site does with `CITINO_API_KEY`.

## Revoking a key

Choose **Revoke** beside the key and confirm. It stops working on the next request. A revoked key stays in the list with the time it was revoked, so you can still tell afterwards which key it was. If a key may have leaked, revoke it and make a new one: there is no way to change a key's secret in place.

## Scopes

A route checks one scope. A key without it gets `403 forbidden`, and the message names the scope it needs.

| Scope | What it reads or changes |
| - | - |
| `projects:read` | Your projects, a project's configuration (brand, aliases, competitors, engines, segments), and the list of engines Citino can sample |
| `metrics:read` | Headline figures, the league table, the trend, visibility by engine and by segment, what moved, losing prompts, Prompt detail, the brands report, Objections, Claims, Experiments, Gaps, monthly reports and the metric definitions |
| `prompts:read` | The prompt list |
| `runs:read` | The answers collected, and one answer with its text |
| `sources:read` | Sources and Source detail, the domains and URLs reports, engine searches, Crawlability and Crawler activity |
| `attribution:read` | Attribution |
| `alerts:read` | Alerts |
| `prompts:write` | Add, re-word and archive prompts |
| `projects:write` | Track and stop tracking competitors, add and remove aliases, set claimed segments and engines |

The [API reference](https://docs.citino.ai/api-reference) shows the scope each route needs.

## When a key is refused

Every authentication failure gets the same answer: `401` with the code `unauthorized`. That covers a missing header, a malformed one, a key that does not exist, a revoked or expired key, and a key whose workspace is suspended or cancelled. The answers are identical on purpose, so a guess learns nothing about how close it was.

A key that authenticates but lacks the route's scope gets `403 forbidden`, naming the scope. A project the key may not read, or that does not exist, is `404`, never `403`.

## Signing in instead of a key

Claude, ChatGPT and Claude Code can connect to Citino's MCP server by signing in, with no key to copy. A sign-in connection reads the MCP server only; it cannot call these `/v1` routes. See [Connect Claude or ChatGPT](https://docs.citino.ai/mcp/overview.md).

## Related

> [card] [Rate limits](https://docs.citino.ai/api/rate-limits.md)
>
> How many requests a key and a workspace may make.

> [card] [Writes](https://docs.citino.ai/api/writes.md)
>
> What a write-scoped key can change, and as whom.

> [card] [Errors](https://docs.citino.ai/api/errors.md)
>
> The error envelope and every code.

> [card] [Workspace settings](https://docs.citino.ai/guides/account/workspace-settings.md)
>
> Where keys and connected apps are managed.


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