> ## 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 one key, which routes accept it, and how to keep it safe.


Every route except `GET /health` and `GET /v1/openapi.json` needs a key. One `ozk_` key opens every plane; a legacy `sk-ocean-` or `sk-conn-` key opens only its own plane, and a legacy key sent to an execution plane 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 one key

| Prefix | Opens | Variable in these docs |
|---|---|---|
| `ozk_` | Every plane on `https://api.plungeai.com` (models, tools, agents, workflows, MCP, discovery, traces, connector proxy) and the MCP server `https://mcp.plungeai.com/v1` | `$PLUNGE_API_KEY` |

Your key is the **Ocean account key**. Billing follows the account: every metered call on any of your `ozk_` keys (models, embeddings, the connector proxy) is charged to your account's one wallet.

The older `sk-ocean-` (models plane) and `sk-conn-` (connector proxy) keys are legacy: still accepted on their own plane while you migrate, no longer created in the Dashboard.

## 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 and the connector proxy accept the same `ozk_` key. The one mistake that answers `401` is a legacy `sk-ocean-` key on an execution plane. Your key on the models plane answers `200`:

```bash
# your ozk_ key on the models plane → 200
curl -s -o /dev/null -w '%{http_code}\n' https://api.plungeai.com/v1/models \
  -H "Authorization: Bearer $PLUNGE_API_KEY"
```

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); **not** the models plane (`/v1/chat/completions`, `/v1/embeddings`, `/v1/models`), where it answers `401` |

```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

The key is self-service at **Dashboard → One API → Keys** (https://dashboard.plungeai.com/one-api?tab=keys); legacy `sk-ocean-` and `sk-conn-` keys are listed there read-only, with revoke. 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.

The tab also shows your **Ocean account key** (prefix and last four characters, with **Rotate**): the key the Playground, `ocean-cli` and OceanCode use. The command-line clients fetch it through a browser sign-in, with nothing to paste: `ocean-cli login` and `oceancode login --provider ocean` open `https://dashboard.plungeai.com/api/cli/authorize?port=<port>&state=<state>`; after you sign in, the Dashboard sends a one-time code (valid 120 seconds) to the client listening on `127.0.0.1:<port>`, and the client exchanges it with `POST https://dashboard.plungeai.com/api/cli/token` for the key.

## 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.
