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.

Point your OpenAI or OpenRouter client at https://api.plungeai.com/v1, swap the key for your ozk_… platform key, and POST /v1/chat/completions works with the same provider/model slugs and the same models[] fallback array — Ocean is an OpenAI-compatible router with OpenRouter-style multi-provider addressing. What it is not is a drop-in for OpenRouter's cost-accounting fields, its error shape, or its provider-routing request object. This chapter is the developer-facing matrix; the exhaustive live/static diff (every field probed, request ids logged) is the full comparison log at orchestration/api-gateway/live-test-reports/2026-09-18-openrouter-compat/OPENROUTER-COMPAT-LOG.md.

5.0 Drop-in mode

Point an OpenRouter client at https://api.plungeai.com/api/v1 and use your ozk_ account key: /api/v1/* is the same models plane as /v1/* — same routing, guardrails, response cache, billing and metering — in OpenRouter's wire shape. Only the shape differs:

/api/v1 (OpenRouter shape)/v1 (OpenAI shape)
Error envelope{"error":{"code":401,"message":"…","metadata":{"error_type":"authentication","ocean_code":"invalid_api_key","ocean_type":"authentication_error","request_id":"…"}}} — numeric code, provider detail in metadata{"error":{"message":"…","type":"authentication_error","code":"invalid_api_key","request_id":"…"}}
Completion idgen-…, the generation id GET /generation?id= takes; also the x-generation-id headerchatcmpl-…
system_fingerprintalways present (null when the provider sent none)as the provider sent it
Stream: OPENROUTER PROCESSING keepalive until the first byte; the usage chunk is always sent and repeats finish_reasonusage chunk only with stream_options.include_usage
GET /modelspublic; OpenRouter's item keys, pricing = USD-per-token strings, plus links and total_countneeds a key; Ocean's item shape (3.8)
curl -s -X POST "$API_BASE/api/v1/chat/completions" -H "content-type: application/json" \
  -d '{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}'

The drop-in base lists models without a key (live-verified 2026-10-04):

curl -s -o /dev/null -w '%{http_code}\n' "$API_BASE/api/v1/models"

5.1 Migration from OpenRouter

ConcernOpenRouterOceanChange needed
Base URLhttps://openrouter.ai/api/v1https://api.plungeai.com/v1change base URL
Keysk-or-…ozk_…new key (Authentication & keys)
Auth headerAuthorization: BearerAuthorization: Bearernone
Model slugprovider/modelprovider/modelnone (bare aliases also resolve, 3.7)
Fallback listmodels: [...]models: [...]none — same field, same semantics (3.3)
App attributionHTTP-Referer, X-Title headersignored (no effect, no error)drop them or leave them — they do nothing
Cost in responseusage.cost, cost_detailsusage.cost, cost_details, is_byok with usage:{"include":true} (3.5)none
Provider routingprovider:{...} request objectnot interpreted (see below)use sort / models / presets

5.2 Endpoints

OpenRouter pathOceanNotes
POST /chat/completionssamefield caveats in 5.3
GET /modelssame on /api/v1 · differs on /v1public, OpenRouter's item shape under /api/v1; key-gated Ocean shape under /v1 (5.6)
POST /embeddingsdiffersopenai, mistral, google and cohere models; no /embeddings/models list route (3.9)
POST /completions (legacy text)sameprompt must be one string; served on /v1 and /api/v1 (3.1)
GET /generation (cost lookup)sameGET /generation?id=<gen-… id> (3.15); traces stay at GET /v1/traces/:id (Traces)
GET /credits, /keysame(3.15); /auth/key answers too. Key management (/keys, POST /auth/keys) stays in the dashboard
GET /models/{author}/{slug}/endpointssamepublic under /api/v1 (3.15)
/images, /audio/*missingnot offered
/v1/agents, /v1/workflows, /v1/tools, /v1/mcp, /v1/discovery, /v1/tracesOcean-onlythe execution planes (5.9)

5.3 Request fields

Full per-provider-shape table is in 3.2. Relative to OpenRouter:

FieldOpenRouterOcean
model, models, messages, stream, stream_options, temperature, top_p, max_tokens, stop, tools, tool_choice, response_format, seed, penalties, logit_bias, logprobs, top_k, min_p, top_a, reasoning, reasoning_effortsupportedopenai-shape: forwarded after one field split (OpenAI itself never receives top_k, min_p, top_a; reasoning.effort is sent as reasoning_effort). On anthropic/google/cohere some are dropped in translation and named in x-ocean-dropped-params — see 3.2.
sortnot a request fieldOcean-only — price/latency/throughput
provider (routing object)supportedignored — accepted and removed before the provider; use models / sort
route:"fallback"supportedignored — accepted and removed; models[] always fails over in order
transforms, pluginssupportedignored — accepted and removed before the provider
prompt_cache_key, prompt_cache_options, cache_controlsupportedopenai-shape passthrough; anthropic honours a cache_control marker on a system text part (3.5), otherwise ignored
usage:{include:true}opt-in cost accountingsupported — adds usage.cost, cost_details, is_byok (3.5); Ocean meters either way

5.4 Response fields

FieldOpenRouterOcean
idgen-…gen-… under /api/v1; chatcmpl-… under /v1
object, created, model, choices, usagepresentpresent (model is the honest served slug, 3.3)
provider (top-level)presentpresent on every response and stream chunk
openrouter_metadata, system_fingerprint, service_tierpresentopenrouter_metadata, service_tier absent; system_fingerprint always under /api/v1 (null when the provider sent none), as the provider sent it under /v1
choices[].native_finish_reasonpresentpresent on every choice
usage.cost, usage.cost_details, usage.is_byokpresentpresent with usage:{"include":true} (3.5); the non-stream price is also the x-ocean-cost-usd header
usage.prompt_tokens_details.cached_tokenspresentpresent; Ocean also adds cache_write_tokens (Ocean-only)
error-in-choice (mid-stream)presentmatched on the agent plane (error frame, no [DONE])

5.5 Streaming

AspectOpenRouterOcean
Wirechat.completion.chunk + [DONE]same (4)
Keepalive comment: OPENROUTER PROCESSINGsame on /api/v1 (until the first byte); none on /v1; the agents plane sends : OCEAN PROCESSING
Final usage chunkincluded (with cost)/api/v1: always, repeating finish_reason; /v1: anthropic/google/cohere always, openai-shape only with stream_options.include_usage
Mid-stream errorerror object, close without [DONE]matched on the agents plane; money-plane openai passthrough relays the upstream's own bytes

5.6 Models list

OpenRouter GET /models returns data[].{id, canonical_slug, name, created, description, context_length, architecture, pricing:{prompt,completion}, top_provider, supported_parameters, …} where pricing is USD-per-token strings. Ocean returns data[].{id, object, created, owned_by, context_length, max_output, pricing:{in,out}, capabilities:{streaming,tools,vision,embedding,fim}} where pricing.in/.out are USD-per-1M numbers (3.8). /v1/models items also carry name, canonical_slug, architecture, top_provider, supported_parameters, per_request_limits and the per-token price as pricing_per_token; only pricing itself keeps the per-1M numbers.

Under /api/v1/models the list is OpenRouter's: public (no key), data[] items carry id, canonical_slug, name, created, description, context_length, architecture, pricing (USD-per-token strings), top_provider, per_request_limits, supported_parameters, default_parameters, links, supported_voices, and the envelope adds links and total_count.

5.7 Errors

OpenRouter's envelope is {"error":{"message":"…","code":401,"metadata":{…}}} — a numeric code, an optional metadata, no type. Ocean's money-plane envelope is {"error":{"message":"…","type":"authentication_error","code":"invalid_api_key","request_id":"…"}} — a string code, a type, a request_id, no metadata. The execution planes use a third shape ({"error":{"code":"…","message":"…"}} + X-Error-Code header). Full tables: Errors, limits & headers.

curl -s "$API_BASE/v1/chat/completions" -H "content-type: application/json" \
  -d '{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"hi"}],"max_tokens":8}'

The "code" above is the string invalid_api_key, not the integer 401 an OpenRouter SDK expects. Under /api/v1 the same request answers "code":401 with metadata.error_type in OpenRouter's vocabulary (authentication, payment_required, rate_limit_exceeded, …) and the Ocean code in metadata.ocean_code (5.0).

5.8 What breaks an OpenRouter SDK user today

Every item below describes /v1. Under /api/v1 (5.0) the error envelope, gen- ids, keepalive, usage chunk and /models behave as OpenRouter's.

  • error.code is a string, not a number — code that branches on err.error.code === 401 never matches (compare err.status / the HTTP status instead).
  • Cost fields are opt-in — usage.cost, usage.cost_details, usage.is_byok appear with usage:{"include":true} (3.5); without it, read x-ocean-cost-usd or GET /v1/generation (3.15).
  • No openrouter_metadata in the response; provider and native_finish_reason are present.
  • OpenRouter-only request fields are not honoured — provider, route, transforms, plugins are accepted and removed before the provider, with no effect. Replace with models + sort + presets.
  • No : OPENROUTER PROCESSING keepalive on the money-plane stream; openai-shape streams carry a final usage chunk only if you pass stream_options.include_usage.
  • /models shape differs — needs a key, and pricing is per-1M numbers (the per-token strings are in pricing_per_token).
  • HTTP-Referer / X-Title are ignored — no app rankings / attribution.
  • No /images, /audio, /responses, /messages, /providers endpoints; /generation, /credits, /key (3.15) and /completions (3.1) are served.
  • BYOK is dashboard-configured, not a per-request provider object (3.12).

5.9 Ocean-only capabilities you gain

  • Execution planes an OpenRouter key cannot reach: Tools, Agents, Workflows, MCP, Discovery.
  • sort — route candidates by price, latency, or throughput (3.3).
  • @preset/<slug> — server-stored routing + param bundles (3.4).
  • Guardrails — org/key spend caps, model/provider allow-lists, content filter, require_byok (3.10).
  • Exact-match response cache — $0 hits with an x-cache header (3.11).
  • Traces & honest billing — GET /v1/traces/:id, honest served-model reporting after failover (Traces).

Planned: TI-34

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