For AI agents: a documentation index is available at https://docs.plungeai.com/llms.txt. Append .md to any page URL, or send Accept: text/markdown, to get markdown. Setup instructions for agents are at https://docs.plungeai.com/agents.md. Execution planes take an ozk_ key; the models plane takes an sk-ocean- key.

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

Execute any active platform agent with a plain prompt. The gateway wraps your call in a one-task workflow, so every execution yields a workflow_id + task_id pair — that pair is the handle for async redemption and for traces.

Auth: Authorization: Bearer ozk_YOUR_KEY on every route here.

GET /v1/agents — list active agents

Query params: limit (default 50, max 100), offset (default 0).

curl -s "https://api.plungeai.com/v1/agents?limit=2" \
  -H "Authorization: Bearer ozk_YOUR_KEY"
{
  "agents": [
    {
      "id": "llm-agent",
      "name": "LLM Agent",
      "type": "agent",
      "category": "ai",
      "status": "active",
      "description": "General-purpose LLM completion agent",
      "tags": ["llm", "completion"]
    },
    {
      "id": "brave-agent",
      "name": "Brave Search",
      "type": "agent",
      "category": "search",
      "status": "active",
      "description": "Web search via Brave",
      "tags": ["search", "web"]
    }
  ],
  "count": 2
}

Each entry is a registry Card (summary fields). count is the returned page's length, not the catalog total — page until a short page, never until offset >= count. A failed registry hop returns 502 upstream_error. The catalog is live — take ids verbatim from this listing or from GET /v1/discovery/search; never from memory. For richer capability search (semantic, filtered, quality scores) use the discovery plane — see the plungeai-discovery skill.

GET /v1/agents/categories — agent categories with counts

Agent categories with active-agent counts, aggregated from the registry (the registry has no categories endpoint of its own). Not a cache-stats pass-through — an older version of this route proxied /api/registry/stats and returned cache statistics instead; that shape is gone.

curl -s https://api.plungeai.com/v1/agents/categories \
  -H "Authorization: Bearer ozk_YOUR_KEY"
{
  "categories": [
    { "name": "search", "count": 12 },
    { "name": "documents", "count": 9 }
  ],
  "count": 2
}

categories[] is sorted by count descending, then name. count is the number of distinct categories, not the total agent count. The aggregation queries only the first 100 active agents from the registry (one page, limit=100&offset=0) — on a catalog past 100 active agents this is a representative sample of category names, not a guaranteed-complete count over every active agent. A failed registry hop returns 502 upstream_error, same as GET /v1/agents.

POST /v1/agents/{id}/execute — run an agent

Body fields:

FieldTypeNotes
promptstringThe instruction. Required (or input)
inputstringAlias for prompt
personastringOptional persona overlay
providerstringProvider id (anthropic, openai, gemini, …); required alongside a pinned model
modelstringOptional model override for LLM-driven agents
maxTokensintegerOptional completion cap (max_tokens accepted as an alias — OpenAI habits work)
max_tokensintegerAlias for maxTokens
temperaturenumberSampling temperature, forwarded to the provider
top_pnumberNucleus sampling probability, forwarded to the provider
reasoning_effortstringReasoning-effort hint (e.g. low/medium/high) for reasoning models
thinking_levelstringExtended-thinking level hint for models that support it
systemstringSystem prompt / instructions for the run
messagesarrayPrior chat messages ([{role, content}]) instead of a single prompt
syncbooleanDefault true. false → 202 pointer
streambooleanDefault false. true → an OpenAI-shaped chat.completion.chunk SSE instead of a JSON result (see "Streaming" below). Ignored on the async pointer path
formatstringResponse negotiation: json (default) | yaml | markdown | text — same values as an Accept header (application/json, text/yaml, text/markdown, text/plain) or the request Content-Type mirror

Sync (default) — result inline

curl -s -X POST https://api.plungeai.com/v1/agents/llm-agent/execute \
  -H "Authorization: Bearer ozk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Summarize in one line: Cloudflare Workers are serverless."}'
{
  "content": "Cloudflare Workers run code at the edge without managing servers.",
  "workflow_id": "00000000-0000-4000-8000-000000000001",
  "task_id": "t1",
  "request_id": "00000000-0000-4000-8000-000000000004"
}

Async — 202 + pointer

Use for long-running prompts, or when your caller can't hold a connection.

curl -s -X POST https://api.plungeai.com/v1/agents/llm-agent/execute \
  -H "Authorization: Bearer ozk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Write a 2000-word market analysis of edge computing", "sync": false}'

HTTP 202:

{
  "workflow_id": "00000000-0000-4000-8000-000000000001",
  "task_id": "t1",
  "request_id": "00000000-0000-4000-8000-000000000004"
}

Streaming (stream: true)

Instead of a JSON result, the 200 response is text/event-stream: an OpenAI-shaped chat.completion.chunk per line —

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1788574344,"model":"llm-agent","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1788574344,"model":"llm-agent","choices":[{"index":0,"delta":{"content":"391"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1788574344,"model":"llm-agent","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

One primer chunk (delta.role: "assistant"), then a delta.content chunk per provider token, then a final chunk (delta: {}, finish_reason: "stop" or "length" when truncated at max_tokens), then a literal data: [DONE] line. usage is emitted on the final chunk only when the provider reports the prompt/completion split (often omitted for streamed runs). Comment keepalives (: OCEAN PROCESSING) may appear every ~15s while idle. Response header X-Execution-Id carries the execution id for result redemption / polling regardless of stream. An error before the first byte is a normal JSON error envelope (not SSE); an error after the first byte is a single data: {"error":{…}} frame and the stream closes WITHOUT [DONE].

Errors

StatusCodeMeaning
400missing_prompt / invalid_formatNo prompt/input given, or format isn't one of json/yaml/markdown/text
401unauthorizedBad/missing ozk_ key
403agent_not_activeThe agent card is not status:active (refused pre-dispatch)
404unknown_agentNo such agent id in the registry (refused pre-dispatch)
409duplicate_execution_idThe x-trace-id header was already used by an earlier run — send a fresh UUID
413payload_too_largeBody over 1 MiB (MAX_REQUEST_SIZE)
422invalid_params / unknown_model / empty_completionMissing/invalid input named by the agent; pinned model absent/inactive in the catalog; or the run completed but the model returned no visible content — raise max_tokens ≥ 64 or change the model
424connection_required / credential_requiredThe agent needs a connection or key the user hasn't set up
429rate_limitedRetry-After header
502engine_error / result_unavailableEngine dispatch failed, or sync redemption couldn't fetch the stored result
502upstream_errorA downstream hop threw (catch-all, any route in this plane)
503agent_unavailableAgent temporarily down

A 502 result_unavailable on a sync call does NOT always mean the run failed — the result may land late, and its error body carries workflow_id + task_id. Redeem with those ids at the results route below before re-running.

GET /v1/agents/results/{workflowId}/{taskId} — redeem an async result

curl -s https://api.plungeai.com/v1/agents/results/00000000-0000-4000-8000-000000000005/t1 \
  -H "Authorization: Bearer ozk_YOUR_KEY"

Ready — HTTP 200 (StoredResult):

{
  "content": "# Edge Computing Market Analysis\n\n...",
  "content_type": "text/markdown",
  "workflow_id": "00000000-0000-4000-8000-000000000001",
  "task_id": "t1"
}

Not ready yet — HTTP 404:

{ "error": { "code": "not_ready", "message": "result not available (yet)" } }

Results are tenant-scoped: you can only redeem pointers created by your own key's identity.

Polling pattern

404 not_ready is the normal in-flight state. Poll with backoff, cap the wait:

WF=00000000-0000-4000-8000-000000000005; TASK=t1
for i in 1 2 3 4 5 6 7 8; do
  sleep $((i * 2))
  BODY=$(curl -s -w '\n%{http_code}' \
    "https://api.plungeai.com/v1/agents/results/$WF/$TASK" \
    -H "Authorization: Bearer ozk_YOUR_KEY")
  [ "$(echo "$BODY" | tail -1)" = "200" ] && { echo "$BODY" | sed '$d'; break; }
done
import time, requests

def redeem(wf, task, key, tries=8):
    for i in range(1, tries + 1):
        r = requests.get(
            f"https://api.plungeai.com/v1/agents/results/{wf}/{task}",
            headers={"Authorization": f"Bearer {key}"})
        if r.status_code == 200:
            return r.json()["content"]
        if r.status_code != 404:
            r.raise_for_status()
        time.sleep(2 * i)          # backoff; 404 not_ready is normal
    raise TimeoutError("result not ready")

Choosing sync vs async

  • sync (default): single quick task, interactive callers. Simplest code.
  • async (sync:false): anything that may run long; queue workers; retries survive process restarts because the pointer is durable.
  • Send an x-trace-id header on the execute call and you can watch the run's spans at GET /v1/traces/<your-trace-id> while polling — see the plungeai-results-traces skill.

Planned: TI-33

Search is not available yet. Until it ships, use the page index or browse the sidebar.

Planned: TI-34

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