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

# Control a bot over REST, MCP and the CLI

> Every bot operation on each door. The One API routes, the MCP actions and the Ocean CLI verbs side by side, with the same key, the same rows and the same stored policy.


A bot is the same row whichever door you use. The One API, the MCP server and the Ocean CLI read and write the rows Studio shows, take the same `ozk_` key, and stop at the same stored policy. Pick the door that fits the caller: REST for code, MCP for an agent, the CLI for a terminal.

| Door | Address | Auth |
|---|---|---|
| One API 2.3.0 | `https://api.plungeai.com` | `Authorization: Bearer $PLUNGE_API_KEY` |
| MCP server 2.6.0 | `https://mcp.plungeai.com/v1` | The same key; tools are named `plungeai_*` |
| Ocean CLI 2.1.0 | `ocean-cli` | `ocean-cli login`, or `OCEAN_API_KEY` |

Another account's id answers the same `404 <resource>_not_found` as an id that does not exist. A storage or upstream failure is a generic `502`; the message never carries database text. Every error is `{"error": {"code", "message"}}` with an `X-Error-Code` header. The response format is yours to pick ([four wires](/api-reference/four-wires)).

## Operations by door

| Do this | REST | MCP | CLI |
|---|---|---|---|
| Save a bot | `POST /v1/workflows` with `kind: bot` | `plungeai_workflow` `create` | `workflows create NAME FILE.yaml --kind bot` |
| Check the YAML | The save runs the lint and answers `422 invalid_workflow` | The same, on `create` and `update` | `workflows validate FILE.yaml --kind bot` (local) |
| Change it, version it | `PATCH /v1/workflows/{id}`, `POST .../versions` | `update`, `save_version`, `restore_version` | `workflows update`, `save-version`, `restore` |
| Copy a template | `POST /v1/templates/{id}/use` | `plungeai_templates` | `templates` |
| Read or set permissions and caps | `GET`, `PUT /v1/workflows/{id}/permissions` | `plungeai_workflow` `permissions_get`, `permissions_set` | `workflows permissions get`, `set` |
| Run it | `POST /v1/workflows/{id}/execute` | `plungeai_execute_workflow` | `workflows run ID` |
| Answer a pause or approve | `POST /v1/executions/{id}/continue` | `plungeai_continue` | `executions continue ID` |
| Steer a running bot | `POST /v1/executions/{id}/steer` | `plungeai_executions` `steer` | `executions steer ID TEXT` |
| List a run's files | `GET /v1/executions/{id}/files` | `plungeai_executions` `files` | `executions files ID` |
| Schedule it | `POST /v1/schedules` (`run_at`, `ends_at`) | `plungeai_schedule` `create` | `schedules create` (`--cron @once --at`, `--ends-at`) |
| Wake it | `POST /v1/schedules/{id}/wake` | `plungeai_schedule` `wake` | `schedules wake ID --note TEXT` |
| Add a trigger or hook | `POST /v1/triggers/{webhook,email,hooks}` | `plungeai_schedule` `webhook_mint`, `email_mint`, `hooks_mint` (and `_list`, `_delete`) | `schedules triggers webhook\|email\|hooks mint\|list\|delete` |
| Read its workspace | `GET /v1/workflows/{id}/workspace-files`, `.../download?path=` | `plungeai_workflow` `files`, `file` | `workflows files ID [--download PATH --out FILE]` |
| Read its inbox | `GET /v1/workflows/{id}/inbox` | `plungeai_workflow` `inbox` | `workflows inbox ID` |
| Bind a chat | `POST /v1/workflows/{id}/bindings`, `DELETE .../{channel}/{sender_id}` | `plungeai_workflow` `bindings`, `bind`, `unbind` | `workflows bindings list\|bind\|unbind` |
| Pair a chat | `POST /v1/channels/pair`, `GET /v1/channels`; then send the code from the chat | Studio or REST | Studio or REST |
| Manage bot groups | `/v1/bot-groups`, `.../{id}/thread` | `plungeai_workflow` `groups`, `group_set`, `group_delete`, `group_thread` | `bot-groups list\|create\|update\|delete\|thread` |
| Give it an A2A door | No REST route; Studio (Share, A2A) | `plungeai_workflow` `a2a_door` | Studio (Share, A2A) |
| Search its runs, its memory | `GET /v1/memory/runs`, `/v1/memory/recall` | `plungeai_memory` | `memory` |

The operation pages are in the API reference: [Bots](/api-reference/bots/get-bot-permissions), [Schedules](/api-reference/schedules/create-schedule), [Triggers](/api-reference/triggers/create-webhook-trigger) and [Executions](/api-reference/executions/steer-execution).

## The agent call door

`POST /v1/execute` takes the program as the body and always runs through the engine, so a saved bot keeps its stored policy. The body is JSON or YAML:

| Body | Runs |
|---|---|
| `agent: <id>` and `input` | Your saved agent or bot, from its live YAML. |
| `agent: <id>@3` and `input` | Snapshot 3 of it: a pinned version. |
| `agent: <building block id>` and that agent's own fields | A registry agent as a one-task workflow. |
| `workflow:`, or a root `name` and `tasks:` | An inline workflow. |

`dry_run: true` validates and reports what would run without running it, spending anything or writing an execution row. `stream: true` answers as server-sent events. See [Agent call](/api-reference/agent-call/agent-call).

## MCP

- Every tool takes `user_request` (the user's words) and most take `format`: `markdown` (default) or `json`. A tool that runs something also takes `user_context` (time zone, locale, place), merged over the zone the network request implies.
- `plungeai_get_result` with `format: json` returns the structured result (`status`, `result`, `tasks`), not the rendered thread.
- `plungeai_workflow` `create` takes `kind`: `workflow`, `agent`, `bot` or `campaign`.
- `plungeai_schedule` `create` with `schedule: "@once"` needs `run_at`: without it the answer is `run_at required`.
- `permissions_set` takes `classes`, `locks`, `budget_usd_run` and `budget_usd_month`; `locks` needs a team admin.
- `file` returns a workspace file as base64, up to 5 MiB; a bigger file is refused with the REST download route to use instead.

## CLI

The verbs below are in Ocean CLI 2.1.0. Run it from the repository today with `npm run ocean-cli -- <verb>`; once it is published it installs with `npm install -g @plungeai/ocean` and runs as `ocean-cli`.

- Every verb takes `--json` (one JSON document on stdout) and, when it creates, runs or pays, `--dry-run` (prints the exact request and sends nothing).
- Destructive verbs (delete, unbind, restore, forget) need `--yes`.
- `workflows permissions set` reads the stored classes and locks first and sends only what you name, so one `--class` keeps the others.
- Exit codes: 2 bad input, 3 auth, 4 API error or a run that needs input or approval, 5 timeout.

```bash
ocean-cli workflows permissions set <bot id> --class pay=ask --lock pay --budget-run 0.5 --budget-month 20
ocean-cli schedules create "Reminder" --type workflow --target <bot id> --cron @once --at 2026-10-06T09:00:00Z
ocean-cli bot-groups create --name research-crew --members <bot id>,<bot id>
ocean-cli executions steer <execution id> "also check the pricing page"
```
