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.

The platform has two error envelopes. The execution planes (tools, agents, workflows, MCP, discovery, traces) use the router envelope; the models plane (/v1/chat/completions, /v1/embeddings, /v1/models) uses the OpenAI-shaped envelope from the inference gateway. Branch on the HTTP status and, on the execution planes, the X-Error-Code header — not on any field's type.

13.1 The two envelopes

Execution planes (orchestration/api-gateway/lib/respond.ts, lib/format.ts):

{ "error": { "code": "unknown_agent", "message": "No such agent: foo", "…": "extra fields" } }
  • Header X-Error-Code: <code> is always set — clients can branch on it without parsing the body.
  • Header x-request-id echoes the server-minted id.
  • The body is format-negotiated: ?format= / body.format / Accept / a text/yaml content-type mirror select json (default), yaml, markdown, or text. An unknown explicit format → 400 invalid_format (as JSON). code+message survive every format; missing, contract, hint/detail, outcome and the execution id render on markdown (the MCP renderer) and missing on text; every field survives json/yaml.

Models plane (inference/gateway/src/errors.ts) — OpenAI shape, no X-Error-Code:

{ "error": { "message": "Invalid API key", "type": "authentication_error", "code": "invalid_api_key", "request_id": "…" } }
  • code is a string, type groups it, request_id is inside the body. Response headers carry x-request-id (and x-correlation-id when you sent an x-request-id), never X-Error-Code.
  • A known path called with the wrong method (GET /v1/chat/completions, POST /v1/models) answers 405 method_not_allowed with an Allow header naming the one method it serves:
HTTPcodeWhenDo
405method_not_allowedA models-plane path called with the wrong methodUse the method in Allow

13.2 Execution-plane error codes

HTTPcodeWhenDo
400invalid_json / invalid_body / invalid_requestBody is not valid JSON (the message quotes the parser's position) / its root is not an object or mappingFix the body
400invalid_format?format=/body.format is not json|yaml|markdown|textUse a known format
400missing_promptAgent execute without a prompt/input string and without a messages user turnSend a prompt or messages
400missing_workflowWorkflow execute with none of workflow, agent or tasksSend a workflow: document or a bare agent: body (10.2)
400ambiguous_bodyagent: next to workflow:/tasks: in one bodySend one of them
400reserved_fieldA bare-agent field named like an engine task key (id, type, agent, depends_on, mission_ref); the body names themRename the field
400 / 422invalid_workflowEngine rejected the workflow (structural) / a save route refused the YAML: schema or kind lint, with errors and warnings and nothing written (16.1)Fix the CNL
400invalid_yamlA text/yaml body is not valid YAML (every execute door)Fix the YAML syntax (the message quotes js-yaml's reason and position)
401unauthorizedMissing/invalid/revoked ozk_ keySend a valid key (Auth)
401invalid_signatureInternal HMAC identity failedService-to-service only
403refusedTrust fence — agent/tool refused (13.5)Do not retry
404not_foundNo route for method+pathCheck the path
403agent_not_activeThe agent's card is not status:active (refused before dispatch)Pick an active agent (Discovery)
403lock_admin_requiredPUT /v1/workflows/{id}/permissions set locks and the caller is not a team admin over the workflow; nothing was written (16.2)Ask an admin
404unknown_agentNo such agent id on the agents, tools and workflows planes, and for a task inside a workflow bodyDiscover a valid id (Discovery)
404agent_not_foundPOST /v1/execute (16.9): agent: names no saved agent of yours (or snapshot) and no building blockCheck the id, and the @n snapshot
404unknown_toolNo such tool idDiscover a valid id
404workflow_not_foundNo saved workflow with that id for this key (a foreign id answers the same)Check the id/owner
404execution_not_found, schedule_not_found, trigger_not_found, group_not_found, binding_not_found, file_not_found, version_not_found, template_not_found, skill_not_found, memory_not_found, connection_not_found, unknown_providerThe Bots routes (16) and connections (7): no such row for this key. Another account's id answers the sameCheck the id
404run_not_foundMCP run never opened, closed, or expired (30 min idle); on GET /v1/memory/runs/{id}, no past run with that idOpen a new run with POST /v1/mcp/runs; retrying never revives it
404not_readyResult not stored yet (still running)Poll again
409approval_requiredTrust fence — human approval pending (13.5)Approve, then retry
409execution_failedResult route: the run failedRead the message
409duplicate_execution_idReplayed x-trace-id UUID hit the run PKSend a fresh UUID or omit it
409not_paused, execution_finishedPOST /v1/executions/{id}/continue on a run that already completed; steer or stream on a run that is no longer running (16.3)Use followup, or read the output
409memory_conflict, skill_name_taken, name_taken, binding_limitA memory id that is also inside another entry; a skill name held by a platform skill or another account; a bot-group name you already use; the account's binding limit (16.6, 16.7)Forget by text; pick another name; unbind one
410session_expired, result_expiredNo conversation session is left for continue; the stored result of output is gone (16.3)Start a new run
413payload_too_largeBody over the size cap (13.6)Shrink the body
422invalid_paramsOutcome needs_input — agent needs different argsFix the params
422unknown_modelPinned model absent/inactive in the catalogDiscover a model
422not_paired, memory_rejected, invalid_name, invalid_membersA chat you did not pair; a memory write the store refuses (it carries current_entries); a bot-group name or member list that fails its checks (16.6, 16.7)Fix the request
422empty_completionRun completed but produced no visible contentRaise max_tokens (≥64)
424connection_requiredOutcome needs_connection — no connected accountConnect the account (Connectors)
424credential_requiredOutcome needs_api_key — no API key for a connectorAdd the key
429rate_limitedTier RPM/RPD or failed-auth cap (13.4)Honour Retry-After
500config_errorServer misconfigurationReport with x-request-id
500internal_errorUnhandled router errorReport with x-request-id
502engine_errorEngine failed (untagged)Retry; report if persistent
502upstream_error, storage_error, scheduler_error, registry_unavailableUpstream service/storage error; on the Bots routes the storage, scheduler or skill registry failed and no detail is returnedRetry with backoff
502tool_call_failedTool execution failedRead the message
502no_servers_connectedMCP: no server connectedConnect a server
502result_unavailableRun finished but the result could not be retrievedRetry the result route
503not_enabled, unavailable, connections_unavailableA feature is not switched on in this deployment yet: channel bindings, bot groups, outbound hooks, connections (16)Retry later
503agent_unavailableOutcome unavailable — agent temporarily downRetry later
503provider_unconfiguredChosen provider has no key on this platformChoose another provider
503money_plane_unavailableModel routes not staged on this tierRetry later

13.3 Outcome → HTTP mapping

Execute routes classify an agent/tool outcome (routes/_util.ts outcomeStatus). A success: false engine result tagged with an outcome remaps to a caller-actionable status; an untagged failure stays 502 engine_error.

OutcomeHTTPcode
needs_input422invalid_params
needs_connection424connection_required
needs_api_key424credential_required
unavailable503agent_unavailable
error (or untagged)502execution_failed / engine_error
needs_approval409approval_required (13.5)

13.4 Rate limits

Execution planes (orchestration/mcp-gateway/rate-limiter.ts, a Durable Object; enforced in lib/auth.ts). Counted on POST executions only — catalog GETs are free. Per tier:

Tierper minuteper day
free301,000
pro10010,000
enterprise300100,000

A key with no tier is treated as free. A per-key rate_limit override is ignored on these planes (lib/auth.ts does not pass it to checkRateLimit); it applies only on https://mcp.plungeai.com/v1. Failed authentications are metered per client IP (auth-fail: 20/min, 500/day) to bound key brute-force. On a limit hit: 429 rate_limited with a Retry-After header (seconds). A key is never shut out for longer than ten minutes: reaching the day limit pauses the key for 10 minutes (Retry-After ≤ 600), after which its day count starts over (2026-10-05; the minute limit waits out the minute as before). The per-tier numbers are the defaults: the operator can change them or switch the limits off (the failed-authentication meter stays on), see 2.

Money plane (inference/gateway/src/auth/rate-limit.ts). Per key, atomic per-minute window (RateLimiterDO, KV soft-cap fallback): default 600 req/min (RATE_LIMIT_RPM_DEFAULT) → 429 rate_limit_exceeded. Separate from billing caps: 429 insufficient_quota (monthly limit / trial allowance), 402 insufficient_balance (empty wallet), 429 spend_cap_exceeded (guardrail) — see 3.10, 3.13.

13.5 Trust fence

Two statuses gate risky actions:

  • 403 refused — the agent/tool refused the action outright (policy). Do not retry unchanged.
  • 409 approval_required — a human must approve before the action runs. Approve out-of-band, then re-issue the request.

13.6 Limits: body caps, timeouts

  • Body cap (execution planes): MAX_REQUEST_SIZE, default 1 MiB (routes/_util.ts — 1_048_576 bytes). Enforced against Content-Length first, then streamed; over → 413 payload_too_large. The models plane relies on the provider/runtime body limits.
  • Timeouts: there is no gateway-imposed request timeout beyond the Cloudflare Workers runtime and each upstream's own. Streams have no idle timeout on the money/workflows plane; the agents plane sends : OCEAN PROCESSING keepalives (4.3).
  • Storage errors are scrubbed: a D1/SQLite failure surfaces as 502 upstream_error "storage error" (the raw error is logged, never leaked); internal service-binding names are redacted from messages.

13.7 Retry guidance

  • 429 — back off and honour Retry-After. Distinguish rate_limited/rate_limit_exceeded (throttle) from insufficient_quota/spend_cap_exceeded (billing — retrying won't help until the window resets or billing changes).
  • 5xx (502/503) — retry with exponential backoff; the models plane already retries 429/5xx once per candidate and fails over (3.3).
  • 4xx other than 429 — do not retry unchanged; fix the request. needs_* outcomes (422/424) are yours to resolve (params, connection, key).
  • Always include the x-request-id when reporting an error.

13.8 Response headers

HeaderPlaneMeaning
x-request-idbothServer-minted request id: the router's on the execution planes, the inference gateway's (= error.request_id) on the models plane. A client-sent x-request-id is never adopted — it comes back as x-correlation-id.
x-ocean-router-idmodelsThe router's own id for a models-plane call (the id x-request-id carried before the two were split).
X-Error-Codeexecution (errors)The error code, for header-only branching.
X-Execution-IdexecutionThe run id on results, streams, and async pointers.
x-correlation-idbothEcho of your x-request-id when you send one. A successful models-plane call without one carries the router's trace id here.
x-generation-idmodels (/api/v1)The gen-… completion id (= the body id), the id GET /generation?id= takes (3.6).
x-ocean-dropped-paramsmodelsRequest fields the routed provider shape could not take, comma-separated (3.2).
x-cachemodelshit / miss on cache-eligible chat requests (3.11).
x-ocean-cost-usdmodelsThe settled price in USD (6 decimals) of a non-stream POST /v1/chat/completions; 0.000000 on a response-cache hit. Absent on streams (use usage.cost) and on the execution planes (priced later).
x-ocean-agent · x-ocean-tasks · x-ocean-versionexecution (POST /v1/execute)What ran: the saved agent or building block (absent for an inline workflow), how many top-level tasks, and the snapshot (<n>, live or inline) (16.9).
Server-Timingexecutiontotal;dur=<ms> timing.
X-Content-Type-Optionsexecutionnosniff on every router response.
Retry-AfterbothSeconds to wait after a 429.
Access-Control-Allow-Origin (+ CORS)both*; both planes answer OPTIONS preflight and advertise the same methods (GET, POST, PUT, PATCH, DELETE, OPTIONS).
Access-Control-Expose-HeadersbothThe headers a browser client may read: x-request-id, x-ocean-router-id, x-execution-id, X-Error-Code, Retry-After, Server-Timing, x-cache, x-ocean-cost-usd, x-generation-id, x-correlation-id, x-ocean-dropped-params, x-ocean-agent, x-ocean-tasks, x-ocean-version, Content-Disposition.
AllowmodelsOn a 405: the one method the path serves, plus OPTIONS.
Content-Typebothapplication/json | text/event-stream | negotiated text/yaml|text/markdown|text/plain.

13.9 Verified examples

Unauthenticated execution-plane request → 401 unauthorized with the x-error-code header:

curl -is "$API_BASE/v1/agents"

Unknown route → 404 not_found, header x-error-code: not_found, plus x-request-id:

curl -is "$API_BASE/v1/nope"

Agent execute without a prompt → 400 missing_prompt:

curl -s "$API_BASE/v1/agents/one-agent/execute" \
  -H "Authorization: Bearer $PLUNGE_API_KEY" \
  -H "Content-Type: application/json" -d '{}'

Unknown agent id → 404 unknown_agent (the trust/fence pre-dispatch check):

curl -s "$API_BASE/v1/agents/zzz-not-an-agent/execute" \
  -H "Authorization: Bearer $PLUNGE_API_KEY" \
  -H "Content-Type: application/json" -d '{"prompt":"hi"}'

Planned: TI-34

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