> For AI agents: the documentation index is at https://platform.detectif.ai/llms.txt. Every page is also available as Markdown by adding `.md` to its path.

# Authentication for Noesis

Send your API key as a bearer token, on HTTP requests and on the WebSocket upgrade. Use a stream token for clients that cannot set headers.

Noesis uses your DetectifAI API keys. A key must be allowed to use Noesis:

| Permission | Allows |
|---|---|
| `noesis:detect` | Live calls, stream tokens and recorded calls |
| `noesis:read` | Sessions and usage |

Choose the permissions when you create a key under **API keys** in the dashboard. A key without them gets `403` with `insufficient_scope`.

## HTTP requests

Send the key as a bearer token:

```bash
curl https://api.noesis.detectif.ai/v1/detect/usage \
  -H "Authorization: Bearer $DETECTIF_API_KEY"
```

## The WebSocket

Send the same header on the WebSocket upgrade request:

```python
import os

import websockets

async def open_stream():
    return await websockets.connect(
        "wss://api.noesis.detectif.ai/v1/detect/stream?encoding=mulaw&sample_rate=8000",
        additional_headers={"Authorization": f"Bearer {os.environ['DETECTIF_API_KEY']}"},
    )
```

```javascript
import WebSocket from "ws";

const ws = new WebSocket("wss://api.noesis.detectif.ai/v1/detect/stream?encoding=mulaw&sample_rate=8000", {
  headers: { Authorization: `Bearer ${process.env.DETECTIF_API_KEY}` },
});
```

## Stream tokens

Some clients cannot set headers on a WebSocket: Twilio `<Stream>`, a browser, or a telephony provider where you only enter a URL. For these, your server creates a stream token for each call and the client passes it instead of the key:

- In the URL: `wss://api.noesis.detectif.ai/v1/detect/stream?token=ntk_...`
- On Twilio: `<Parameter name="token" value="ntk_..."/>` inside `<Stream>`. See [Twilio](https://platform.detectif.ai/docs/noesis/twilio/).

```bash
curl -X POST https://api.noesis.detectif.ai/v1/detect/stream-tokens \
  -H "Authorization: Bearer $DETECTIF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"record": true}'
```

```python
import os

import requests

resp = requests.post(
    "https://api.noesis.detectif.ai/v1/detect/stream-tokens",
    headers={"Authorization": f"Bearer {os.environ['DETECTIF_API_KEY']}"},
    json={"record": True},
    timeout=10,
)
token = resp.json()["token"]
```

```javascript
const res = await fetch("https://api.noesis.detectif.ai/v1/detect/stream-tokens", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.DETECTIF_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ record: true }),
});
const { token } = await res.json();
```

- A token opens one call. It expires 5 minutes after it is created if it has not been used.
- It belongs to the key and account that created it, and carries the key's Noesis permission.
- `record` (optional, default `true`) sets whether the call is recorded. See [Data and recording](https://platform.detectif.ai/docs/noesis/data-and-recording/).
- Create tokens on your server, right before the call. Never create them in browser or app code, because that needs the API key.

## Keep keys out of URLs

Never put an API key in a URL. A request with `?api_key=` is refused with `api_key_in_url`; rotate a key that was sent that way. Never put an API key in browser or mobile app code: give those clients a stream token from your server.

## When a key or token is refused

| HTTP | WebSocket close code | Meaning |
|---|---|---|
| `401` | `4401` | The key is missing, mistyped or revoked, or the token is invalid, expired or already used |
| `403` | `4403` | The key is not allowed to use Noesis, or the account's email address is not verified |

See [Errors](https://platform.detectif.ai/docs/noesis/errors/).
