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

# SDKs & clients

> You do not need an Ocean-specific SDK to use the One API. The models plane is OpenAI-wire, so every OpenAI-compatible client works with a base-URL swap; the execution planes are plain HTTP+JSON and have official SDKs in TypeScript, Python, Go and Rust. This chapter lists the options, shortest …


You do not need an Ocean-specific SDK to use the One API. The models plane is OpenAI-wire, so
every OpenAI-compatible client works with a base-URL swap; the execution planes are plain
HTTP+JSON and have official SDKs in TypeScript, Python, Go and Rust. This chapter lists the
options, shortest path first.

Key throughout: `$PLUNGE_API_KEY` = `ozk_`, the one key for every plane. See
[Authentication & keys](/one-api/authentication).

## OpenAI SDK (models plane)

Point the official OpenAI SDK at `https://api.plungeai.com/v1` with your `ozk_` key.
This covers `/v1/chat/completions`, `/v1/embeddings`, and `/v1/models`.

Python:

```python
from openai import OpenAI

client = OpenAI(
    api_key="<your ozk_ key>",
    base_url="https://api.plungeai.com/v1",
    default_headers={"x-trace-id": "00000000-0000-4000-8000-000000000006"},  # optional UUID
)
resp = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Reply with exactly the word: pong"}],
    max_tokens=16,
)
```

TypeScript:

```ts
import OpenAI from 'openai'

const client = new OpenAI({
  apiKey: process.env.PLUNGE_API_KEY,
  baseURL: 'https://api.plungeai.com/v1',
  defaultHeaders: { 'x-trace-id': crypto.randomUUID() }, // optional
})
```

Ocean-only fields (`models[]`, `sort`, `@preset/<slug>`) pass through as extra body params —
use the SDK's `extra_body` (Python) or just add them to the request object (TypeScript). See
[Models plane](/one-api/models).

## Official SDKs (execution planes)

Four packages, one shape: a client that reads the key from `PLUNGEAI_API_KEY`, one method per
operation (`client.agents.execute(…)`), typed errors by status, retries (2, with backoff, on
connection errors, 408, 429 and ≥ 500 — never on 409 or 424, which are semantic answers), a
10-minute timeout, server-sent-event streams, offset pages that iterate across pages, raw-response
access and `get/post/…` escape hatches. The curl examples in this guide use `$PLUNGE_API_KEY`; the
SDKs' variable is `PLUNGEAI_API_KEY`, the CLI's too. All four target One API 2.5.0 (package version
1.0.0); their resource layers are generated from this document, so every operation listed here has
a method. Source: `sdk/` in the platform repository.

| Language | Package | Install |
|---|---|---|
| TypeScript | `@plungeai/one-api` (npm) | `npm install @plungeai/one-api` |
| Python | `plungeai` (PyPI; sync + async over httpx) | `pip install plungeai` |
| Go | `github.com/PlungeAI/plungeai-go` (stdlib only) | `go get github.com/PlungeAI/plungeai-go` |
| Rust | `plungeai` (crates.io; async over reqwest) | `cargo add plungeai` |

Release status: `npm view @plungeai/one-api version`, `pip index versions plungeai`,
`go list -m -versions github.com/PlungeAI/plungeai-go`, `cargo search plungeai`; until a registry
answers, build the package from its directory under `sdk/`.

## TypeScript

```ts
import PlungeAI from '@plungeai/one-api'

const client = new PlungeAI()

const me = await client.whoami()
const run = await client.agents.execute('one-agent', { prompt: 'Reply with exactly the word: pong', max_tokens: 32 })
const hits = await client.discovery.search({ q: 'web search', kind: 'agents', limit: 3 })

const stream = await client.workflows.executeInlineStream({ workflow }) // workflow: CNL YAML string
for await (const event of stream) console.log(event.event, event.data)

for await (const w of client.workflows.list()) console.log(w) // walks every page

try {
  await client.agents.execute('no-such-agent', { prompt: 'hi' })
} catch (err) {
  if (err instanceof PlungeAI.NotFoundError) console.log(err.status, err.code, err.requestID)
  else throw err
}
```

Each status has its own error class (`BadRequestError` … `FailedDependencyError` for 424,
`InternalServerError` for ≥ 500), all exported on `PlungeAI`; a per-request `idempotencyKey` is
sent as `x-trace-id`, so a replayed run answers 409 `duplicate_execution_id` instead of starting
twice. Zero runtime dependencies, ESM and CJS.

## Python

```python
from plungeai import PlungeAI, NotFoundError

client = PlungeAI()  # or AsyncPlungeAI() — same methods, awaited

me = client.whoami()
run = client.agents.execute("one-agent", prompt="Reply with exactly the word: pong", max_tokens=32)
hits = client.discovery.search(q="web search", kind="agents", limit=3)

stream = client.workflows.execute_inline_stream(workflow=workflow)  # workflow: CNL YAML string
for event in stream:
    print(event.event, event.data)

for w in client.workflows.list():  # walks every page
    print(w)

try:
    client.agents.execute("no-such-agent", prompt="hi")
except NotFoundError as err:
    print(err.status_code, err.code, err.request_id)
```

Method names are `snake_case` (`execute_inline_stream`); request fields are keyword arguments
typed as `TypedDict`s; responses are plain `dict`s typed the same way. `stream=True` on
`agents.execute` and `chat.completions.create` returns a `Stream`. Per-call options:
`extra_headers`, `extra_query`, `timeout`, `max_retries`, `idempotency_key`;
`client.agents.with_raw_response.execute(...)` gives the status, headers and request id. One
runtime dependency: `httpx`. Python ≥ 3.10.

## Go

```go
import (
    "context"
    "errors"
    "fmt"

    "github.com/PlungeAI/plungeai-go"
)

client := plungeai.NewClient() // plungeai.WithAPIKey(…) to override PLUNGEAI_API_KEY
ctx := context.Background()

me, err := client.Whoami(ctx)
run, err := client.Agents.Execute(ctx, "one-agent", plungeai.AgentExecuteParams{Prompt: "Reply with exactly the word: pong", MaxTokens: plungeai.Int(32)})
hits, err := client.Discovery.Search(ctx, plungeai.DiscoverySearchParams{Q: "web search", Kind: "agents", Limit: plungeai.Int(3)})

stream, err := client.Workflows.ExecuteInlineStream(ctx, plungeai.WorkflowExecuteInlineStreamParams{Workflow: workflow})
for stream.Next() {
    ev := stream.Current()
    fmt.Println(ev.Event, ev.Data)
}
err = stream.Err()

pager := client.Workflows.ListAutoPaging(ctx, plungeai.WorkflowListParams{}) // walks every page
for pager.Next() {
    fmt.Println(pager.Current())
}

_, err = client.Agents.Execute(ctx, "no-such-agent", plungeai.AgentExecuteParams{Prompt: "hi"})
var apiErr *plungeai.Error
if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
    fmt.Println(apiErr.StatusCode, apiErr.Code, apiErr.RequestID)
}
```

One package, no sub-packages: options are `plungeai.WithAPIKey`, `WithBaseURL`, `WithMaxRetries`,
`WithRequestTimeout`, `WithHeader`, `WithQuery`, `WithIdempotencyKey`, `WithResponseInto`, usable
on `NewClient` and on every call. Streaming variants of the `stream`-flag operations are
`Agents.ExecuteStreaming` and `Chat.Completions.CreateStreaming`; list operations have a
`…AutoPaging` twin. Errors are one type, `*plungeai.Error`, classified by `StatusCode`. Standard
library only. Go ≥ 1.24.

## Rust

```rust
use futures_util::StreamExt;
use plungeai::{types::*, Client};

let client = Client::new()?; // or Client::builder().api_key(…).build()?

let me = client.whoami().await?;
let run = client
    .agents()
    .execute("one-agent", &AgentExecuteParams { prompt: Some("Reply with exactly the word: pong".into()), max_tokens: Some(32), ..Default::default() })
    .await?;
let hits = client.discovery().search(&DiscoverySearchParams { q: Some("web search".into()), kind: Some(DiscoverySearchParamsKind::Agents), limit: Some(3), ..Default::default() }).await?;

let mut stream = client.workflows().execute_inline_stream(&WorkflowExecuteInlineStreamParams { workflow: workflow.into(), ..Default::default() }).await?;
while let Some(event) = stream.next().await {
    let event = event?;
    println!("{:?} {}", event.event, event.data);
}

let mut items = client.workflows().list(&Default::default()).await?.into_stream(); // walks every page
while let Some(w) = items.next().await {
    println!("{:?}", w?);
}

match client.agents().execute("no-such-agent", &AgentExecuteParams { prompt: Some("hi".into()), ..Default::default() }).await {
    Err(e) if e.is_not_found() => println!("{:?} {:?}", e.status(), e.request_id()),
    other => { other?; }
}
```

Params and responses are `serde` structs under `plungeai::types` (optional fields `Option<T>`,
response enums carry an `Unknown` variant so a new server value never breaks a client). Errors are
one enum, `plungeai::Error`, with `status()` and `is_not_found()`-style predicates; streaming
variants are `execute_stream`; pages expose `has_next_page()`, `next_page()` and `into_stream()`, carry
`request_id` and `headers`, and have a `list_raw` form.
Async over `reqwest` (rustls); `tokio` runtime. Rust ≥ 1.85.

## OpenAI-compatible frameworks (models plane)

Any framework that accepts an OpenAI base URL works. Set base URL
`https://api.plungeai.com/v1` and your `ozk_` key.

LiteLLM:

```python
import litellm
resp = litellm.completion(
    model="openai/anthropic/claude-sonnet-4-6",  # openai/ tells LiteLLM to use the OpenAI wire
    api_base="https://api.plungeai.com/v1",
    api_key="<your ozk_ key>",
    messages=[{"role": "user", "content": "pong?"}],
)
```

Vercel AI SDK:

```ts
import { createOpenAI } from '@ai-sdk/openai'

const ocean = createOpenAI({
  baseURL: 'https://api.plungeai.com/v1',
  apiKey: process.env.PLUNGE_API_KEY,
})
const model = ocean('anthropic/claude-sonnet-4-6')
```

LangChain (Python):

```python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="anthropic/claude-sonnet-4-6",
    base_url="https://api.plungeai.com/v1",
    api_key="<your ozk_ key>",
)
```

## MCP clients (the platform as an MCP server)

The platform is itself an MCP server. Point any MCP client at the inbound Streamable-HTTP
endpoint with your `ozk_` key. There are two equivalent URLs (both live-verified, both
return the same tool table): `https://api.plungeai.com/v1/mcp` (the One API's pass-through,
which forwards your key) and `https://mcp.plungeai.com/v1` (the MCP gateway directly).

Claude Code:

```bash
claude mcp add --transport http plungeai https://api.plungeai.com/v1/mcp \
  --header "Authorization: Bearer $PLUNGE_API_KEY"
```

Claude Desktop / other clients that speak `mcp-remote`:

```json
{
  "mcpServers": {
    "plungeai": {
      "command": "npx",
      "args": ["mcp-remote", "https://api.plungeai.com/v1/mcp",
               "--header", "Authorization: Bearer ${PLUNGE_API_KEY}"]
    }
  }
}
```

You can confirm the tool table over plain curl (JSON-RPC `tools/list`) — live-verified:

```bash
curl -s -X POST https://api.plungeai.com/v1/mcp \
  -H "Authorization: Bearer $PLUNGE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
```

See [MCP plane](/one-api/mcp) for inbound tools and outbound MCP runs, and
[Connecting clients](https://mcp.plungeai.com/docs#3-connecting-clients) in the MCP developer
guide for every supported client's config and how to verify it.

## Ocean CLI (`ocean-cli`)

A non-interactive terminal client for scripts, CI and other agents: `ocean-cli <noun> <verb>`,
`--json` on every verb, stable exit codes. Package `@plungeai/ocean` (not yet on npm; run it from
the repo with `npm run ocean-cli -- --help`). `ocean-cli login` stores your `ozk_` key; the
Studio nouns also need `ocean-cli login --session`. Each noun rides one of three surfaces:

| Surface | Credential | Nouns |
|---|---|---|
| One API (`https://api.plungeai.com/v1`) | `ozk_` key (`X-API-Key`; `Authorization: Bearer` on the models plane) | `search`, `extract`, `answer`, `research`, `findall`, `monitor`, `enrich` (through `POST /v1/tools/parallel-agent/execute`); `agents`; `tools list\|get\|call`; `workflows run` (waiting or `--stream`), `workflows result\|cancel`; `discovery`; `models`; `mcp` (outbound runs); `traces` |
| MCP (`https://mcp.plungeai.com/v1`) | `ozk_` key (`Authorization: Bearer`) | `workflows list\|get\|create\|update\|delete\|versions\|restore\|build`, `workflows run --no-wait`; `executions`; `missions`; `chat`; `schedules`; `memory`; `learn`; `templates`; `tools call --async`; `whoami` |
| Studio session (`https://studio.plungeai.com`, `https://dashboard.plungeai.com`) | `auth-session` cookie | `keys`, `balance`, `usage`, `nodes`, `campaigns`, `connections`, `secrets`, `documents`, `teams`, `projects`, `executions export` |

`ocean-cli doctor` checks all three hosts and both credentials. Full command reference:
`apps/ocean-cli/README.md`.

## curl conventions used in this guide

- **Base URL** is always `https://api.plungeai.com`.
- **Auth** is `-H "Authorization: Bearer $PLUNGE_API_KEY"` on every plane;
  `-H "X-API-Key: $PLUNGE_API_KEY"` also works on the execution planes.
- **JSON bodies** use `-H "Content-Type: application/json"`; workflow YAML can be posted with
  `-H "Content-Type: text/yaml" --data-binary @workflow.yaml`.
- **Streaming** examples add `-N` so curl does not buffer the SSE.
- **Correlation:** add `-H "x-trace-id: <a UUID>"` to tie several calls into one trace.

A plain execution-plane list call (the shape every `ozk_` GET follows) is live-verified:

```bash
curl -s "https://api.plungeai.com/v1/agents?limit=3" \
  -H "Authorization: Bearer $PLUNGE_API_KEY"
```
