For AI agents: a documentation index is available at https://www.plungeai.com/docs/llms.txt. Append .md to any page URL, or send Accept: text/markdown, to get markdown. Setup instructions for agents are at https://www.plungeai.com/docs/agents.md. One ozk_ key opens every plane, models included.

Documentation Index: fetch the complete documentation index at /llms.txt. Use this file to discover all available pages before exploring further.

Source: orchestration/mcp-gateway/auth-middleware.ts, index.ts, rate-limiter.ts, core/core-api-keys/rate-limit-config.ts, extras.ts (plungeaiWhoami), core/core-api-keys/index.ts, apps/ocean-dashboard/src/worker/routes/platform-keys.routes.ts, orchestration/api-gateway/lib/auth.ts, orchestration/api-gateway/routes/mcp.ts. Unauthenticated 401 responses live-verified 2026-09-24.

Every MCP call needs an ozk_ key. The key is your identity: it resolves to one user account, and every tool reads and writes only what that account owns. There is no OAuth sign-in on the MCP server today; bearer keys are the only way in. This chapter uses https://mcp.plungeai.com/v1; https://api.plungeai.com/v1/mcp accepts the same key, with the differences noted in each section.

2.1 The ozk_ key

PropertyValue
Formatozk_live_ + 48 hex characters
WhereDashboard → One API → Keys (https://dashboard.plungeai.com), self-service while signed in
Namerequired, 1–64 characters
Expirynever, or 7 / 30 / 90 / 180 / 365 days, chosen when you create it
Shownexactly once, at creation. It is stored only as a SHA-256 hash; nobody can show it to you again
Per accountup to 25 active keys; the 26th is refused until you revoke one
Tiercopied from your account's tier when the key is created (free, pro, enterprise; an unknown account tier gives free)
Works onthe MCP server and every One API plane, models included (https://api.plungeai.com/docs#2-authentication--keys)

The Keys list shows each key's name, redacted prefix and last four characters, created and last-used dates, expiry (highlighted when it is less than a week away) and status (active, revoked, expired), with a revoke action on active keys.

Because the tier is copied at creation, a key keeps the tier it was created with. After your account changes tier, create a new key to get the new limits; plungeai_whoami shows the tier of the key you are using (2.7).

A shared team key is simply a key created under someone else's account: whoever uses it acts as that account. Ask the account owner for one; you cannot create a key for another account yourself.

2.2 Sending the key

HeaderExample
Authorization: Bearer <key>-H "Authorization: Bearer $PLUNGE_API_KEY"
X-API-Key: <key>-H "X-API-Key: $PLUNGE_API_KEY"

Both are supported. The server tries X-API-Key first, then Authorization: Bearer. A bearer value that does not start with ozk_ is rejected (401): OAuth tokens and sk-ocean- model keys (legacy) are not credentials here. X-API-Key exists for integrations whose "API key" field does not send a bearer header:

curl -s https://mcp.plungeai.com/v1 \
  -H "X-API-Key: $PLUNGE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

On https://api.plungeai.com/v1/mcp both headers work too, and the value must start with ozk_.

2.3 What an unauthenticated call returns

Any request to /v1 without a valid key gets 401 with a JSON-RPC error body and a WWW-Authenticate challenge. No credentials at all gives a bare Bearer challenge (live-verified 2026-09-24):

curl -s -o /dev/null -w '%{http_code} %header{www-authenticate}\n' \
  -X POST https://mcp.plungeai.com/v1 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

A credential that was sent but is not valid adds error="invalid_token" (live-verified 2026-09-24, with a made-up key):

curl -s -o /dev/null -w '%{http_code} %header{www-authenticate}\n' \
  -X POST https://mcp.plungeai.com/v1 \
  -H 'Authorization: Bearer ozk_live_not_a_real_key' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

The body is the same in both cases:

curl -s -X POST https://mcp.plungeai.com/v1 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
{"jsonrpc":"2.0","error":{"code":-32001,"message":"No valid authentication provided. Use an X-API-Key header or \"Authorization: Bearer <api key>\"."},"id":null}

What each failure looks like:

CauseStatusWWW-Authenticateerror.message
no key401BearerNo valid authentication provided. …
unknown, mistyped, revoked or expired key, or a non-ozk_ bearer401Bearer error="invalid_token"No valid authentication provided. … — the exact reason is logged server-side, not returned
valid key used from an address outside its allowed_ips fence401Bearer error="invalid_token"API key not permitted from <your IP>
more than 20 failed attempts in a minute (or 500 in a day) from one IP429 + Retry-After—Too many failed authentication attempts

The same request on https://api.plungeai.com/v1/mcp answers in the One API envelope instead (live-verified 2026-09-24):

curl -s -X POST https://api.plungeai.com/v1/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

There: no key or a non-ozk_ value → 401 Bearer ozk_ key required; an ozk_ key that is invalid, revoked, expired or outside its IP fence → 401 Invalid or revoked API key; too many failures → 429 rate_limited (too many failed authentications). Every error carries X-Error-Code and x-request-id headers.

The server does not publish OAuth discovery metadata (/.well-known/oauth-protected-resource). Clients that can only connect through OAuth need a bridge — see Connecting clients.

2.4 Expiry, revocation and rotation

  • Expiry is checked on every request. From the moment a key expires it answers 401.
  • Revoke a key from the same Keys list. The record stays (so the list and audit trail keep it) and is marked revoked. Each server instance caches a successfully checked key for up to 60 seconds, and the key store's own edge cache can add up to another 60 seconds, so a revoked key can keep working for up to about two minutes (about 75 seconds measured), then answers 401. If the key leaked, revoke it first and allow for that window before you treat it as dead.
  • Rotate without downtime: create the new key, update every client config, confirm it with plungeai_whoami (2.7), then revoke the old key.
  • Leaked or lost? Create a replacement and revoke the old one yourself. A lost key cannot be recovered: only its hash is stored.

2.5 Key fences

A key can carry two optional fences. Keys you create yourself in the Dashboard have no fences (every tool, from any address). Fenced keys are issued by PlungeAI for you — ask the account owner who gave you the key, or Requests (opens in a new tab).

FenceEffect on https://mcp.plungeai.com/v1
allowed_ipsThe key authenticates only from the listed addresses: exact IPv4 or IPv6 addresses, or IPv4 CIDR blocks (IPv6 ranges are not supported — list exact IPv6 addresses). From anywhere else: 401 API key not permitted from <ip>, counted as a failed attempt.
allowed_toolsOnly the listed tools are registered for this key: the others do not appear in tools/list, and a tools/call to one returns JSON-RPC error -32602 Tool not permitted for this key: <name>.

If a tool this guide documents is missing from your tools/list, or a call is refused this way, the key is fenced: that is policy, not a fault.

2.6 Tiers and rate limits

Default limits per tier, from tier-limits.ts (the operator can change them, see the last point below):

TierTool-call requests per minuteTool-call requests per day
free301,000
pro10010,000
enterprise300100,000

How they are counted:

  • Per account, not per key. The counter is keyed by your user id, so every key on your account shares it. The tier and any per-key override of the key you are using decide the cap.
  • Only tools/call counts on https://mcp.plungeai.com/v1: each HTTP request that carries one counts once (a JSON-RPC batch is one request). initialize, tools/list, resources/* and prompts/* are free. plungeai_whoami is a tool call and counts.
  • On https://api.plungeai.com/v1/mcp the rule is the same: the One API does not count POST /v1/mcp, and this server counts each request that carries a tools/call, with your key's per-key override.
  • Fixed windows. A minute window opens with your first counted call and resets 60 seconds later; a day window lasts 24 hours from its first call, not until midnight.
  • A per-key rate_limit override, when PlungeAI sets one on a key, replaces the per-minute cap.
  • Set by the operator. The numbers above are the defaults. PlungeAI sets the per-tier numbers, or switches the limits off, in Operator › One API › Keys › Rate limits (GET/PUT /admin/rate-limits for internal callers). While they are off, no call is counted and no tool call or One API POST returns 429; the one limit left is the failed-authentication meter (20 invalid-key attempts a minute per client IP), which is always on. A change reaches the gateways within a minute. A per-key rate_limit still applies while limits are on.

When you are over the limit, the call is refused before anything runs:

HTTP/2 429
retry-after: 42

{"jsonrpc":"2.0","error":{"code":-32000,"message":"Rate limit exceeded: Rate limit: 100 requests/minute","data":{"retryAfter":42}},"id":3}

Retry-After (and error.data.retryAfter) is the number of seconds until the window resets. Wait that long, then continue; do not retry in a tight loop. On the One API host the body is {"error":{"code":"rate_limited","message":"rate limit exceeded for tier pro","retry_after":42}}. The full error catalogue is in Errors, feedback & limits.

2.7 Checking yourself — plungeai_whoami

plungeai_whoami shows who the server thinks you are and how much of your window you have used. It is read-only.

Try asking: "Who am I, what tier am I on, and how much of my rate limit have I used?" — or, right after connecting a client, "use plungeai_whoami to confirm my identity".

ParameterTypeRequiredDefaultNotes
user_requeststring (≤ 4,000 chars)no—the user's own words
formatmarkdown · jsonnomarkdownjson returns the fields below as a JSON document plus structuredContent
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":"how much of my rate limit have I used?"}}}'

The Markdown answer:

# Who am I

- **User:** `<your user id>`
- **Auth:** api-key · tier **pro**
- **Key:** my-laptop (`<key id>`)
- **Rate window:** 3/min used · 41/day used
- **Server:** plungeai.com (mcp.plungeai.com)

With format: "json":

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":2,"method":"tools/call","params":{"name":"plungeai_whoami","arguments":{"user_request":"who am I, as JSON","format":"json"}}}'
{
  "user": "<your user id>",
  "auth_type": "api-key",
  "tier": "pro",
  "key_label": "my-laptop",
  "key_id": "<key id>",
  "rate_window": { "minute_used": 3, "day_used": 41 },
  "server": "plungeai.com (mcp.plungeai.com)"
}

What to know:

  • The counts include the plungeai_whoami call itself. Compare them with your tier's caps in 2.6; the card shows usage, not the caps.
  • The rate-window line is best-effort. If the counter cannot be read, the line is absent (rate_window: null); that alone is not an error.
  • There is no permissions field. A tool fence shows up as fewer tools in tools/list, not on this card.
  • Through https://api.plungeai.com/v1/mcp the card is the same: the One API forwards your key and the gateway reads its record.
  • Without an identity the tool answers Authentication required. with isError: true — in practice you get the 401 from 2.3 first.

2.8 Identity and scoping

  • Everything is scoped to the account behind the key: executions, workflows, schedules, conversations, memory. You see what that account owns, the same view it has in Studio — never the whole system.
  • Id-based lookups check ownership before reading anything. Someone else's id, or a mistyped one, answers "not found", never "forbidden" (plungeai_get_result answers with a needs_input outcome naming the id). An id that looks right but is "not found" usually belongs to another account or is mistyped.
  • What you create through MCP carries your user id, so it shows up in Studio, the dashboard and the other PlungeAI apps under your account.
  • Every authenticated request is written to your activity trail with its arguments and answer. You can read your own day at https://mcp.plungeai.com/log?date=YYYY-MM-DD with the same key (Errors, feedback & limits). This is also why user_request matters: it is where your own sentence survives into support diagnostics.

2.9 Keeping the key safe

  • Treat it like a password. Anyone holding it acts as your account.
  • Keep the literal key in user-level config (~/.claude.json, ~/.cursor/mcp.json, the client's secret store) or an environment variable. A config file inside a repo should only reference a variable (Connecting clients shows how per client).
  • Before the first commit in a repo you just configured, both of these must print nothing:
git grep -n "ozk_"                                                 # tracked files
grep -rn "ozk_" . --exclude-dir=.git --exclude-dir=node_modules    # untracked files too
  • Never put the key in browser code or a client bundle. Apps call PlungeAI from a server with the key in a server-side secret.
  • If a key may have leaked, rotate it now (2.4).

Planned: TI-34

The docs assistant is not available yet. You can hand these docs to your own assistant instead.