> ## Documentation Index
> Fetch the complete documentation index at: https://www.plungeai.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

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


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](https://skills.plungeai.com/plungeai-api-setup.zip) · [View raw SKILL.md](https://skills.plungeai.com/plungeai-api-setup/SKILL.md)

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

```bash
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}'
```

```json
{
  "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 |

```bash
# 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`](/skills/plungeai-api-setup/references/bots-and-door); every other
operation (discovery, tools, agents, MCP, traces, models): [`references/route-map.md`](/skills/plungeai-api-setup/references/route-map). 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`):

```yaml
workflow:
  name: Hello
  tasks:
    - type: task
      id: t1
      agent: one-agent
      prompt: "Say exactly: {input}"
```

```bash
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`](/skills/plungeai-api-setup/references/bots-and-door).

## 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 carry
  `X-Error-Code: <code>` — branch on the header. The body follows the negotiated `format`
  (`?format=` or a body field, `Accept`: `json` default, `yaml`, `markdown`, `text`); an unknown value is
  `400 invalid_format`. A 424 adds `error.connect` (`{provider, method, link}`); an error on a call that
  created an execution carries `X-Execution-Id`. `/v1/discovery/recommend` and `/v1/discovery/cards/:type/:id`
  normalize the registry's own flat `{"error":"<string>"}` into the envelope (`code` is `not_found` on a 404,
  `invalid_request` otherwise). The models plane uses the OpenAI error shape
  (`{"error":{"message","type","code","request_id"}}`); the `/api/v1` drop-in the OpenRouter one.
- Every response carries a **server-minted** `x-request-id` header. To correlate your own
  calls, send **`x-trace-id`** and keep your own copy — it is not echoed back, but it is
  threaded across every internal hop and queryable at `GET /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 (`GET`s 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`](/skills/plungeai-api-setup/references/sdk-and-codegen).

## Verify

```bash
# 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`](/skills/plungeai-api-setup/references/bots-and-door) — 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`](/skills/plungeai-api-setup/references/route-map) — every other operation, one line each: service, discovery, tools, agents,
  workflow execution, MCP, traces, models plane and the `/api/v1` drop-in.
- [`references/sdk-and-codegen.md`](/skills/plungeai-api-setup/references/sdk-and-codegen) — the four official SDKs, typed-client generation for other
  languages, curl cookbook, self-documenting endpoints, the paste-ready `AGENTS.md` block 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/schedules` and `/v1/triggers`: cron, one-time `run_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

<CardGroup cols={2}>
<Card title="Bots, executions, schedules, connections and the Agent Call door — over REST" icon="file-text" href="/skills/plungeai-api-setup/references/bots-and-door">
One API 2.3.0 puts everything a saved bot needs on REST, under the same ozk key (Authorization: Bearer ozkYOURKEY).
</Card>
<Card title="Route map — the planes that are not bots, one line per operation" icon="file-text" href="/skills/plungeai-api-setup/references/route-map">
The One API 2.3.0 has 114 operations.
</Card>
<Card title="SDK & codegen — typed clients, snippets, and the AGENTS.md block" icon="file-text" href="/skills/plungeai-api-setup/references/sdk-and-codegen">
The supported integration stance, in order of preference:
</Card>
</CardGroup>
