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

# Authentication & keys

> The three key prefixes, which routes accept which, and how to keep keys safe.


Every route except `GET /health` and `GET /v1/openapi.json` needs a key. Each of the three prefixes opens a different set of routes, and sending the wrong prefix is a `401`, never a silent fallback.

<Note>
These docs use PLUNGE_API_KEY; the PlungeAI skills, the Claude Code plugin and the install page use PLUNGEAI_API_KEY; any name works if your config and shell agree.
</Note>

## The three prefixes

| Prefix | Opens | Variable in these docs |
|---|---|---|
| `ozk_` | The six execution planes (tools, agents, workflows, MCP, discovery, traces) and the MCP server `https://mcp.plungeai.com/v1` | `$PLUNGE_API_KEY` |
| `sk-ocean-` | The models plane only: `/v1/chat/completions`, `/v1/embeddings`, `/v1/models` | `$PLUNGE_MODEL_KEY` |
| `sk-conn-` | The connector proxy on `gateway.plungeai.com` | `$PLUNGE_CONNECTOR_KEY` |

## Which routes accept which key

The execution planes (`/v1/discovery`, `/v1/tools`, `/v1/agents`, `/v1/workflows`, `/v1/mcp`, `/v1/traces`) accept only a bearer that starts with `ozk_`. The models plane accepts only `sk-ocean-`. Both mistakes answer `401`:

```bash
# ozk_ key on the models plane → 401
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://api.plungeai.com/v1/chat/completions \
  -H "Authorization: Bearer $PLUNGE_API_KEY" -H 'Content-Type: application/json' \
  -d '{"model":"anthropic/claude-haiku-4-5","messages":[{"role":"user","content":"hi"}],"max_tokens":1}'
```

The `401` body is the standard error envelope, and `X-Error-Code: unauthorized` carries the same code as a header.

## Header forms

| Header | Accepted on |
|---|---|
| `Authorization: Bearer <key>` | Every plane and the MCP server |
| `X-API-Key: <key>` | The execution planes and the MCP server (`ozk_` keys) |

```bash Authorization
curl -s "https://api.plungeai.com/v1/tools?limit=1" -H "Authorization: Bearer $PLUNGE_API_KEY"
```

```bash X-API-Key
curl -s "https://api.plungeai.com/v1/tools?limit=1" -H "X-API-Key: $PLUNGE_API_KEY"
```

## How to obtain each key

All three are self-service at **Dashboard → One API → Keys** (https://dashboard.plungeai.com/one-api?tab=keys). Name the key, pick an expiry (never, or 7, 30, 90, 180 or 365 days) and copy it: it is shown exactly once and stored hashed. A new key takes your account's tier.

## Expiry, revocation and rotation

- **Expiry** is checked on every request; an expired key answers `401` from that moment.
- **Revoke** a key from the same Keys list. Keys are cached briefly, so a revoked key can keep working for about two minutes before it answers `401`. If a key leaked, revoke it first and allow for that window.
- **Rotate** without downtime: create the new key, update every client, confirm it with `plungeai_whoami`, then revoke the old key.
- A lost key cannot be recovered, because only its hash is stored: create a replacement and revoke the old one.

## Key fences

Key fences are set by PlungeAI on request. Keys you create in the Dashboard carry no fence: every tool, from any address. On `https://mcp.plungeai.com/v1`, a key fenced to some tools does not see the others in `tools/list`, and a call to one answers JSON-RPC `-32602`; a key fenced to some addresses answers `401` from anywhere else.

## Checking yourself: plungeai_whoami

Ask your MCP client to "use plungeai_whoami", or call it directly. It returns the user, the auth type and tier, the key label and the current rate-limit window:

```bash
curl -s https://mcp.plungeai.com/v1 \
  -H "Authorization: Bearer $PLUNGE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"plungeai_whoami","arguments":{"user_request":"who am I?"}}}'
```

The fields are on [Who am I](/account-api/identity/whoami).

## CORS

The One API answers browser requests from any origin, and the models plane exposes `x-request-id`. Browser code works, but a key in client-side code is a key anyone can read: call PlungeAI from your server.

## Keeping the key safe

<Warning>
Treat the key like a password: whoever holds it acts as your account. If a key may have leaked, rotate it now.
</Warning>

- Keep the literal key in a user-level client config or an environment variable. A config file inside a repository only references a variable.
- Before the first commit in a repository you configured, both commands must print nothing:

```bash
git grep -n "ozk_"
grep -rn "ozk_" . --exclude-dir=.git --exclude-dir=node_modules
```

- Never put a key in browser code or a client bundle.
