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

# API keys and authentication

> Which key sends data, which key reads it, and how to call the API safely.

Orwel uses two kinds of keys, with different jobs:

| Key | Prefix | Can do | Where it lives |
| - | - | - | - |
| Production API key | `orwel_` followed by the key id (no `sk_` or `dev_`) | **Only send data**: events, sessions, identify, conversions. | In your site's browser bundle. Safe to expose. |
| Development key | `orwel_dev_` | Same as the production API key, but everything is stored as test data. See [Dev mode](https://www.orwel.io/docs/guides/dev-mode.md). | Local and staging builds. |
| API key | `orwel_sk_` | **Read and write workspace data** through the REST API and the MCP server, limited to the scopes you choose. | Your backend, or a browser app on an allowed origin. Never in a public repo. |

> [!WARNING]
>
> The production API key cannot read anything. Calls to `/api/v1` or `/mcp` with the production or development key are rejected. To read data, create an API key.

## Sending data (production API key)

The [SDK](https://www.orwel.io/docs/sdk/introduction.md) sends the production API key in the `X-Orwel-Key` header to the ingest endpoints (`/ingest`, `/ingest/batch`, `/visitor-resolve`). You find it in **Settings, API keys** in the dashboard, and in the install guide at [orwel.io/install](https://orwel.io/install).

```ts theme={null}
orwel.init({ apiKey: process.env.NEXT_PUBLIC_ORWEL_KEY })
```

## Reading data (API keys)

API keys are optional. You only need one to build on Orwel: pull leads into your own dashboard, sync them to a warehouse, or connect an AI agent through [MCP](https://www.orwel.io/docs/agents/mcp.md).

### Create a key

**1. Open Settings, API keys**

In the Orwel dashboard, go to **Settings** and open **API keys**.

**2. Name it and pick scopes**

Give the key a name you will recognize later (for example "Sales agent" or "Warehouse sync") and select only the scopes it needs.

**3. Choose where it can be used**

Add the allowed origins of the browser app that will call the API, or choose **server only**.

**4. Copy it once**

The full key (`orwel_sk_...`) is shown only once. Store it in an environment variable or a secret manager.

### Scopes

| Scope | Grants | Endpoints |
| - | - | - |
| `leads:read` | List and read leads and workspace members. | `GET /leads`, `GET /leads/{id}`, `GET /users`, `GET /users/{id}` |
| `visitors:read` | Read visitors, identities, their activity and personalities, and visitor lists. | `GET /identities` and its sub-routes, `GET /lists` |
| `companies:read` | Read enriched companies. | `GET /companies`, `GET /companies/{id}` |
| `campaigns:read` | Read ad campaigns and their performance. | `GET /campaigns` |
| `conversions:read` | Read conversions. Together with `leads:read` and `visitors:read` it allows reports. | `GET /conversions`, `GET /conversions/{id}`, `POST /reports` (needs all three) |
| `leads:write` | Create and update leads and identities. In MCP, also `create_lead` and `send_lead_to_hyperflow`. | `POST /leads`, `PATCH /leads/{id}`, `POST /identities`, `PATCH /identities/{id}` |
| `lists:write` | Create visitor lists and add members to them, through the MCP `manage_lists` tool. | MCP only |
| `mcp` | Connect to the [MCP server](https://www.orwel.io/docs/agents/mcp.md) and use its read tools. Write tools also need `leads:write` or `lists:write`. | `/mcp` |

A request outside the key's scopes gets `403 insufficient_scope`, and the message names the missing scope. Each page of the [API Reference](https://www.orwel.io/docs/api-reference/introduction.md) says which scope it needs.

### Allowed origins or server only

* **Allowed origins:** the key only answers browser requests whose `Origin` is in the list (for example `https://app.yourcompany.com`). Any other site gets `403`.
* **Server only:** the key has no origins and is rejected from any browser (`403 origin_not_allowed`). Use it from a backend, a cron job or an agent.

Requests without an `Origin` header (a backend, `curl`, an MCP client) work with either kind of key.

> [!NOTE]
>
> Origins stop other websites from using your key in a browser. They do not stop a server or `curl`, because those can send any `Origin`. If a key leaks, rotate it. That is why each key should carry the minimum scopes it needs.

### Send the key

Send the API key as a Bearer token (the `x-api-key` header also works):

```bash cURL theme={null}
curl "https://api.orwel.io/api/v1/leads?limit=10" \
  -H "Authorization: Bearer $ORWEL_API_KEY"
```

```javascript JavaScript theme={null}
const res = await fetch('https://api.orwel.io/api/v1/leads?limit=10', {
  headers: { Authorization: `Bearer ${process.env.ORWEL_API_KEY}` }
})
const { data } = await res.json()
```

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

res = requests.get(
    "https://api.orwel.io/api/v1/leads",
    headers={"Authorization": f"Bearer {os.environ['ORWEL_API_KEY']}"},
    params={"limit": 10},
)
print(res.json())
```

### Rotate or revoke

Each key in **Settings, API keys** shows when it was last used. Revoke a key there and it stops working immediately. To rotate without downtime, create the new key, deploy it, then revoke the old one.

## Errors

### 401 Unauthorized

The key is missing, mistyped, revoked, or it is a public or development key used against the API.

```json theme={null}
{
  "error": "unauthorized",
  "message": "Invalid or missing API key"
}
```

### 403 Forbidden

The key is valid but not allowed for this request:

| `error` | Cause |
| - | - |
| `insufficient_scope` | The key lacks a scope the endpoint needs. |
| `origin_not_allowed` | The request came from a browser and the key is server only, or the origin is not on its list. |
| `plan_not_allowed` | The workspace subscription does not include the feature (MCP). |

```json theme={null}
{
  "error": "insufficient_scope",
  "message": "This API key is missing the required scope: leads:read"
}
```

Keys can also expire: when you create one you can pick 30, 90 or 365 days, or no expiration. An expired key gets `401` with the message `This API key has expired`.

## Best practices

> [accordion] Keep API keys out of public code
>
> `orwel_sk_` keys read customer data. Keep them in environment variables or a secret manager, never in a public repository, and only in a browser app when the key has allowed origins.

> [accordion] One key per use, minimum scopes
>
> A warehouse sync needs `leads:read`, not `leads:write`. An agent that only answers questions does not need `lists:write`. Separate keys also make the "last used" column meaningful.

> [accordion] Rotate on any doubt
>
> If a key may have leaked, revoke it right away. The production API key in your site is not a secret and does not need rotation.

## Next steps

> [card] [API Reference](https://www.orwel.io/docs/api-reference/introduction.md)
>
> Endpoints you can call with an API key.

> [card] [MCP for agents](https://www.orwel.io/docs/agents/mcp.md)
>
> Connect Claude, Cursor or any MCP client to your workspace.


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