Start here
plungeai-api-setup
Setup for the PlungeAI One API (https://api.plungeai.com, v2.3.0) — a self-service ozk_ key, the Bearer/X-API-Key header, your first curl call, the live OpenAPI 3.1 contract at /v1/openapi.json, typed-client codegen, the three API levels (compatible wire, standard REST incl. bots, schedules and executions, the Agent Call door POST /v1/execute), and shared error/rate-limit basics. Use whenever writing or GENERATING code that calls PlungeAI, or answering "how do I authenticate", "where's the OpenAPI spec", "how do I generate a typed client" or "can I run a bot over REST". Triggers: "PlungeAI API", "api.plungeai.com", "One API", "call plungeai from code", "plungeai openapi", "plungeai curl", "plungeai SDK". Route detail: the capability skills (plungeai-agents, plungeai-workflows, plungeai-scheduling, plungeai-results-traces, …). MCP → plungeai-mcp-setup; terminal → plungeai-cli-setup; unsure which door → choose-your-plungeai-door.
Download zip (opens in a new tab) · View raw SKILL.md (opens in a new tab)
One base URL for the whole platform: https://api.plungeai.com. Plain JSON over
HTTPS — no SDK required. The models ("money") plane is OpenAI-compatible. Machine-readable
contract: GET /v1/openapi.json (OpenAPI 3.1). This skill gets you authenticated and making
calls; the route-by-route manual for each capability (what params, what the response looks
like, error handling per route) lives in that capability's own skill — load it next.
Prerequisites — get a key
Self-service ozk_ key: Dashboard → One API → Keys (https://dashboard.plungeai.com) →
create → name it, pick an expiry (never, or 7–365 days) → copy it once, it is shown exactly
once and stored hashed thereafter. Ask the account owner only for a shared/team key minted
under someone else's account. Keys inherit your account's tier (free by default).
The same ozk_ key opens the models plane too (/v1/chat/completions, /v1/embeddings,
/v1/models, see plungeai-models) — one key for every plane. The legacy sk-ocean- / sk-conn- keys are retired:
no new ones can be minted, and existing ones sit read-only under Dashboard → One API → Keys → Legacy keys until revoked.
Discovery first — hard rule
The catalog is live. Never hardcode agent ids, tool lists, model slugs, or route shapes from memory:
GET /v1/openapi.json— the live route contract, no auth required. This is the one authority for what routes exist; regenerate any typed client from it, never patch by hand.GET /v1/agents/GET /v1/discovery/search?q=<capability>— what's callable right now (full capability detail:plungeai-discovery,plungeai-agents).
Quick start — first call
curl -X POST https://api.plungeai.com/v1/agents/one-agent/execute \
-H "Authorization: Bearer ozk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Say exactly: hello ocean", "sync": true}'{
"content": "hello ocean",
"workflow_id": "00000000-0000-4000-8000-000000000001",
"task_id": "t1",
"request_id": "00000000-0000-4000-8000-000000000004"
}That is the whole integration pattern: bearer key, JSON body, JSON result. Every other plane (tools, workflows, models, MCP pass-through, discovery, traces) works the same shape — the per-plane request/response detail is in that plane's capability skill.
Three levels — all supported, none deprecated
| Level | What | Use it when |
|---|---|---|
| 1 Compatible wire | /v1/chat/completions, /v1/models, /api/v1/* (models only) | an OpenAI or OpenRouter client should just work |
| 2 Standard REST | one route per resource, JSON or YAML in and out: /v1/workflows (saved workflows, agents, bots, campaigns, versions), /v1/executions, /v1/schedules, /v1/triggers, /v1/bot-groups, /v1/channels, /v1/connections, /v1/memory, /v1/skills, /v1/templates | you manage a resource or call it by id — start here |
| 3 Agent Call door | POST /v1/execute: the body is the program — a bare task, a workflow:, or agent: <saved id>@3 + input | the body is the program; it always runs through the engine, so a saved bot keeps its stored policy |
# a saved bot, pinned to snapshot 3, planned but not run
curl -s -X POST https://api.plungeai.com/v1/execute \
-H "Authorization: Bearer ozk_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"agent":"<saved bot id>@3","input":"Check the pricing page","dry_run":true}'Bots over REST: set a bot's permission classes, locks and spend caps (PUT /v1/workflows/{id}/permissions),
read its workspace files and inbox, bind a paired chat (/bindings), group bots (/v1/bot-groups), steer a
running execution (POST /v1/executions/{id}/steer), schedule it once (@once + run_at) or wake it, and
mint webhook, email or outbound-hook triggers. Connections (/v1/connections, on_behalf_of for your
customers) are there too. Every route, body field and status of those: references/bots-and-door.md; every other
operation (discovery, tools, agents, MCP, traces, models): references/route-map.md. The saved-bot authoring rules are
plungeai-bot-agent's; MCP and CLI spellings are in the verb tables of plungeai-workflows and plungeai-scheduling.
Save a workflow, run it, read the result
A saved workflow is CNL YAML in the yaml field of POST /v1/workflows (kind is workflow, agent, bot or
campaign). The smallest valid body, as YAML (a workflow: wrapper, tasks: list, one type: task):
workflow:
name: Hello
tasks:
- type: task
id: t1
agent: one-agent
prompt: "Say exactly: {input}"KEY=ozk_YOUR_KEY; API=https://api.plungeai.com
# save → 201 {"workflow":{"id":"<id>",…},"warnings":[]} (a refusal is 422 invalid_workflow with errors[])
curl -s -X POST $API/v1/workflows -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"name":"Hello","kind":"workflow","yaml":"workflow:\n name: Hello\n tasks:\n - type: task\n id: t1\n agent: one-agent\n prompt: \"Say exactly: {input}\"\n"}'
# or save a YAML file as the body (kind and name ride the query; workflow.name is the name when ?name is left out)
curl -s -X POST "$API/v1/workflows?kind=bot&name=Hello" -H "Authorization: Bearer $KEY" -H 'Content-Type: text/yaml' --data-binary @bot.yaml
# run it by id → {"success":true,"workflow_id":"<execution id>","final_task_id":"t1","request_id":"…","content":"…"}
curl -s -X POST $API/v1/workflows/<id>/execute -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"input":"hello"}'
# read it back: the execution id is the `workflow_id` of the run answer (not the saved workflow's id)
curl -s $API/v1/executions/<execution id>/output -H "Authorization: Bearer $KEY"Agent ids in agent: come from discovery, never memory: GET /v1/discovery/search?q=<capability> returns skill
and other cards first, so add &kind=agents when you want an agent id (GET /v1/agents lists them too).
Other routes you will want next: GET /v1/executions/{id} (poll status), GET /v1/executions/{id}/output,
GET /v1/executions/{id}/files, POST /v1/schedules — all in references/bots-and-door.md.
Authentication — header and error basics
| Key | Planes | How to send |
|---|---|---|
ozk_YOUR_KEY | Execution planes: /v1/discovery, /v1/tools, /v1/agents, /v1/workflows, /v1/executions, /v1/schedules, /v1/triggers, /v1/bot-groups, /v1/channels, /v1/connections, /v1/memory, /v1/skills, /v1/templates, /v1/execute, /v1/mcp, /v1/traces | Authorization: Bearer ozk_YOUR_KEY (or X-API-Key: ozk_YOUR_KEY) |
ozk_YOUR_KEY (the same key) | Models plane: /v1/chat/completions, /v1/embeddings, /v1/models | Authorization: Bearer ozk_YOUR_KEY only (no X-API-Key here) |
- Never hardcode keys in generated code — read the one
ozk_key from an env var (PLUNGEAI_API_KEY) for every plane. - Execution-plane errors use the envelope
{"error":{"code","message",…}}and always carryX-Error-Code: <code>— branch on the header. The body follows the negotiatedformat(?format=or a body field,Accept:jsondefault,yaml,markdown,text); an unknown value is400 invalid_format. A 424 addserror.connect({provider, method, link}); an error on a call that created an execution carriesX-Execution-Id./v1/discovery/recommendand/v1/discovery/cards/:type/:idnormalize the registry's own flat{"error":"<string>"}into the envelope (codeisnot_foundon a 404,invalid_requestotherwise). The models plane uses the OpenAI error shape ({"error":{"message","type","code","request_id"}}); the/api/v1drop-in the OpenRouter one. - Every response carries a server-minted
x-request-idheader. To correlate your own calls, sendx-trace-idand keep your own copy — it is not echoed back, but it is threaded across every internal hop and queryable atGET /v1/traces/<your id>(plungeai-results-traces).
Error semantics — the ones every caller needs
| Status | Code | Meaning | What your code does |
|---|---|---|---|
| 400 | run_at_required / run_at_past | A @once schedule with no run_at, or one not in the future | Send an ISO-8601 time WITH a UTC offset, in the future |
| 400 | invalid_json / invalid_yaml / invalid_body / invalid_format / missing_workflow / ambiguous_body | An unreadable body, an unknown format, none of workflow / agent / tasks, or agent: next to workflow: | Fix the body; the message names the problem |
| 401 | unauthorized | Missing/invalid key, or the wrong key prefix for this plane | Fix the key. Never retry in a loop |
| 403 | refused | Trust fence. The agent/tool refused the action outright — a gated/money verb, unattended surface. A policy verdict, not a lifecycle state | FINAL. Surface verbatim. NEVER retry, rephrase, or route around |
| 403 | agent_not_active | A named agent (in a workflow or a single-agent execute) is parked/inactive — a lifecycle state, distinct from refused above | Re-discover a fresh id (GET /v1/discovery/search); don't retry the same id |
| 403 | lock_admin_required | A bot's permission locks can be set only by a team admin; nothing in that body was written | Ask an admin; resend without locks |
| 404 | unknown_agent / unknown_tool / agent_not_found (the POST /v1/execute door) / <thing>_not_found (workflow, execution, schedule, trigger, group, binding, version, template, skill, memory, connection, …) | No such id, or another account's (the two answer alike) | Re-discover; don't retry the same id |
| 404 | not_ready | Async result not landed yet | Poll again with backoff (2s+) |
| 409 | approval_required | Trust fence. A human must approve before the action runs — implemented and produced today, not a placeholder. Three shapes, three answers (below) | Never approve on your own |
| 409 | duplicate_execution_id | The x-trace-id you sent was already used by an earlier run | Send a fresh UUID, or omit it |
| 413 | payload_too_large | Body over 1 MiB | Shrink the body |
| 422 | invalid_workflow | A saved workflow, agent or bot fails CNL validation or its kind's lint (errors and warnings listed); nothing is written | Fix exactly the listed fields and resend |
| 422 | invalid_params | Outcome needs_input — body fails the tool contract (the response echoes missing and the contract), or a field on a resource route is missing or malformed | Self-correct from the echoed contract or message, then retry once |
| 424 | connection_required / credential_required | Outcome needs_connection / needs_api_key — no connected account, or no API key for a connector; error.connect names the provider and where to connect it | POST /v1/connections/{provider} (a key) or …/link (a hosted URL for a customer), or Ocean Studio → Connectors, then retry |
| 429 | rate_limited | Execution planes — per-tier RPM/RPD cap, or failed-auth metering | Honour the Retry-After header; back off |
| 429 | rate_limit_exceeded / spend_cap_exceeded / insufficient_quota | Models plane only — separate codes, separate plane; rate, spend-cap, or quota limit | Back off; caps/quotas are policy, not transients |
| 5xx | engine_error / upstream_error / storage_error / scheduler_error / internal_error (503 agent_unavailable / not_enabled) | Upstream or router failure, or a feature not switched on yet | One retry with backoff is reasonable |
409 approval_required — pick the row that matches what you hold:
| You have | Approve with | Then |
|---|---|---|
A 409 from POST /v1/tools/{id}/execute (the dispatch fence; no execution exists to continue) | Studio, by a human | re-issue the identical request |
A paused agent turn on /v1/agents/{id}/execute (the reply carries an action_token) | re-send the same call with approved: true + that action_token (both, or 400 invalid_params) | nothing else |
A run whose GET /v1/executions/{id} reads needs_approval | POST /v1/executions/{id}/continue {"approve": true, "allow_scope": "once"|"run"|"always"} (MCP plungeai_continue) — the held action runs inside that call | do NOT re-issue the original request (it would act twice) |
Canonical code list (all planes, every status): developer guide §13 at GET /llms-full.txt — §13.2
(execution-plane codes), §13.3 (outcome→HTTP mapping), §13.5 (the two trust-fence statuses).
403 refused and 409 approval_required are the fence; everything else is caller-fixable.
Sync vs async and per-route detail are in each capability's own skill.
Rate limits
Execution-plane tiers: free 30/min · 1,000/day, pro 100/min · 10,000/day, enterprise
300/min · 100,000/day (a key with no tier counts as free; a per-key rate_limit override applies only on
https://mcp.plungeai.com/v1, not here). Enforced per-key on POST calls only (GETs are free); a hit
answers 429 rate_limited with a Retry-After header — honour it. Failed authentications are metered per client IP. These numbers are the defaults: the operator sets the limits, and
when they are switched off there is no 429 except failed-auth metering. The models plane has its
own, separate limiter, default 600 req/min per key, answering 429 rate_limit_exceeded with
no Retry-After header — back off on a short fixed interval (~1s) instead. Don't hammer
result-polling endpoints; use 2s+ intervals.
Self-documenting endpoints (no auth), SDK and codegen
GET /v1/openapi.json (OpenAPI 3.1) · GET /llms.txt (index) · GET /llms-full.txt (full guide and route
reference as one markdown payload) · GET /docs (human page) · GET /health. AI agents at runtime can skip HTTP:
connect an MCP client to https://mcp.plungeai.com/v1 with an ozk_ key (plungeai-mcp-setup).
Typed clients: four official SDKs, generated from the live spec — TypeScript @plungeai/one-api, Python plungeai,
Go github.com/PlungeAI/plungeai-go, Rust plungeai (setup and first call: plungeai-sdk-setup). For any other
language, generate a client from GET /v1/openapi.json. Snippets (codegen, the SDK table, the curl cookbook, a
paste-ready AGENTS.md block): references/sdk-and-codegen.md.
Verify
# 1. Liveness (no auth) — expect {"status":"ok","service":"one-api-router",...}
curl -s https://api.plungeai.com/health
# 2. Authenticated GET — expect {"tools":[...],"count":N}
curl -s "https://api.plungeai.com/v1/tools?limit=1" \
-H "Authorization: Bearer ozk_YOUR_KEY"
# 3. A 401 here means the key is wrong; a 200 with cards means you're in:
curl -s "https://api.plungeai.com/v1/discovery/search?q=web%20search&limit=1" \
-H "Authorization: Bearer ozk_YOUR_KEY"
# 4. Who the key is — expect {"user_id":"…","via":"key","tier":…,"key_label":…}
curl -s https://api.plungeai.com/v1/whoami -H "Authorization: Bearer ozk_YOUR_KEY"For the models plane, verify with the same ozk_ key:
curl -s https://api.plungeai.com/v1/models -H "Authorization: Bearer ozk_YOUR_KEY".
References
references/bots-and-door.md— bots, executions, schedules, triggers, connections, memory, skills, templates and the Agent Call door over REST: every route, body field, limit and status (One API 2.3.0).references/route-map.md— every other operation, one line each: service, discovery, tools, agents, workflow execution, MCP, traces, models plane and the/api/v1drop-in.references/sdk-and-codegen.md— the four official SDKs, typed-client generation for other languages, curl cookbook, self-documenting endpoints, the paste-readyAGENTS.mdblock for generated apps.
Related skills
plungeai-models— the OpenAI-compatible plane in depth: routing, presets, guardrails, caching, pricing.plungeai-agents,plungeai-tools-connectors— execute a registry agent or a structured tool; contracts, sync/async, 422 handling.plungeai-workflows— CNL YAML authoring and the workflow execution routes (inline + saved, SSE streaming).plungeai-discovery— catalog search, recommendations, cards.plungeai-scheduling—/v1/schedulesand/v1/triggers: cron, one-timerun_at, wake, webhook/email/hook triggers.plungeai-bot-agent— the saved bot's YAML and keys (what the REST bot routes govern).plungeai-results-traces—/v1/executions(status, output, continue, followup, steer, files), traces, MCP-as-server routes.plungeai-mcp-setup— the same platform over MCP instead of REST.plungeai-sdk-setup— the official TypeScript, Python, Go and Rust SDKs.plungeai-cli-setup— operate PlungeAI from a terminal.
Reference pages
One API 2.3.0 puts everything a saved bot needs on REST, under the same ozk key (Authorization: Bearer ozkYOURKEY).
The One API 2.3.0 has 114 operations.
The supported integration stance, in order of preference: