plungeai-api-setup
Bots, executions, schedules, connections and the Agent Call door — over REST
One API 2.3.0 puts everything a saved bot needs on REST, under the same ozk key (Authorization: Bearer ozkYOURKEY).
One API 2.3.0 puts everything a saved bot needs on REST, under the same ozk_ key (Authorization: Bearer ozk_YOUR_KEY). The live contract is GET https://api.plungeai.com/v1/openapi.json (tags
Workflows, Executions, Schedules, Triggers, Bots, Memory, Skills, Templates,
Connections, Agent Call) — regenerate clients from it, never copy routes from this page.
Rules on every route here: your rows only (another account's id answers 404 <thing>_not_found,
same as a missing one) · JSON by default, YAML in (Content-Type: text/yaml) and out on the same
routes, format= or Accept picks json | yaml | markdown | text · errors are
{"error":{"code","message"}} · a storage or upstream failure is a generic 502, never its text.
Saved workflows and bots
| Route | Does |
|---|---|
POST /v1/workflows | save {name, kind, yaml, folder?, campaign_config?}; kind is workflow, agent, bot or campaign (campaign_config with kind: campaign: the ledger and its schedule) |
GET /v1/workflows, GET|PATCH|DELETE /v1/workflows/{id} | list (kind, search, folder, limit ≤ 200, offset), read, change, delete (a bot also loses its memory and jobs) |
GET|POST /v1/workflows/{id}/versions, POST …/versions/{versionId}/restore | list versions, save one {label?}, restore one |
POST /v1/workflows/{id}/execute | run a saved workflow or bot by id (level 2) |
The YAML is validated and linted for its kind before anything is written: a bot is ONE
type: harness task on agent: one-agent; a refusal is 422 invalid_workflow with errors.
Bot policy, workspace, inbox, chats, groups (owner only)
| Route | Does |
|---|---|
GET|PUT /v1/workflows/{id}/permissions | {classes: {send|write|pay|delete: allow|ask|deny|hand_off}, locks: [...], budget_usd_run, budget_usd_month}; fields you send replace, null clears a cap. locks need a team admin (else 403 lock_admin_required, nothing written) |
GET …/workspace-files, GET …/workspace-files/download?path= | the files the bot keeps (workspace: bot); raw bytes, Content-Disposition: attachment, 20 MB cap |
GET …/inbox | queued bot-to-bot messages (read-only) |
GET|POST …/bindings, DELETE …/bindings/{channel}/{sender_id} | which paired chat talks to this bot: {channel, sender_id}; a chat talks to ONE bot |
GET /v1/channels, POST /v1/channels/pair {channel} | your paired chats; a 10-minute pairing code (telegram, whatsapp, discord, teams) |
GET|POST /v1/bot-groups, PUT|DELETE /v1/bot-groups/{id}, GET …/{id}/thread | named sets of 2–6 of your bots (name 1–40 chars) for message_bot to=group:<name>; the thread is read-only |
The engine loads the stored policy once per run, so every door (REST, MCP, CLI, Studio, schedules, triggers, chat) stops at the same caps and holds. A run that reaches its cap says so in its result.
Executions (every run, whichever door started it)
| Route | Does |
|---|---|
GET /v1/executions, GET|DELETE /v1/executions/{id} | list (source, status, workflow_id), poll, delete |
GET …/{id}/output, GET …/{id}/conversation, GET …/{id}/files | final result, turns added by continue/followup, Office/PDF files the run produced |
POST …/{id}/continue {answer | approve, allow_scope?} | answer a paused run, or approve its held action; allow_scope: once, run, always (409 not_paused on a finished run: use followup) |
POST …/{id}/followup {text} | queued (202) while running, a new turn (200) once finished |
POST …/{id}/steer {text} | queue a message for the live loop (202); a finished run is 409 execution_finished |
POST …/{id}/cancel, GET …/{id}/stream | cooperative stop; SSE re-attach (status, progress, done) |
Schedules, triggers, hooks
| Route | Does |
|---|---|
POST /v1/schedules | {name, schedule, workflow_id, job_type?: workflow|heartbeat, timezone?, run_at?, ends_at?, parameters?}; schedule: "@once" needs run_at (ISO-8601 WITH a UTC offset, in the future: 400 run_at_required, 400 run_at_past) |
GET /v1/schedules, GET|PATCH|DELETE /v1/schedules/{id} | list, read (with latest runs), change, delete (history stays) |
POST …/{id}/pause, …/resume, …/run {input?}, …/wake {note?, in?} | state; run now (run_id, execution_id); fire the workflow once, optionally later (a pending wake is re-timed, never stacked) |
GET /v1/schedules/{id}/runs | run history; execution_id is what the execution routes take |
GET|POST /v1/triggers/webhook, DELETE …/{token}, POST …/{token}/rotate | an HTTPS URL that runs the schedule; scheme: generic, github, gitlab, standard-webhooks; prompt_template, filters |
GET|POST /v1/triggers/email, DELETE …/{token}, POST …/{token}/rotate | an address (bot+<token>) that runs it; allowed_from filter |
GET|POST /v1/triggers/hooks, DELETE …/{id} | signed run events to your https URL: run.started, run.finished, run.failed, run.needs_approval; verify X-Ocean-Signature |
Every trigger call names the schedule with schedule_id; a secret is shown once, on mint.
Connections — your keys, or your customer's
| Route | Does |
|---|---|
GET /v1/connections | status only, never a credential; ?on_behalf_of=<customer id> lists one customer's |
POST /v1/connections/{provider} | bring a key {api_key, api_secret?, on_behalf_of?}; checked live, stored encrypted |
DELETE /v1/connections/{provider} | disconnect (204); ?on_behalf_of= for a customer's |
POST /v1/connections/{provider}/link | a one-use, 15-minute hosted connect URL to send a customer |
A connection-tier agent with no connection answers 424 connection_required with a connect
pointer. on_behalf_of (1–128 chars of A-Za-z0-9_.-, no ..) is accepted on POST /v1/execute among
the execute routes: the run reads the credential connected under that customer id.
Other errors you will meet
503 not_enabled (bindings, bot groups, channel pairing before their migration or deploy) and
503 unavailable (outbound hooks) · 409 binding_limit, 422 not_paired (bind a chat you
did not pair) · 409 name_taken, 422 invalid_name / invalid_members (bot groups) ·
400 invalid_path, 404 file_not_found, 413 file_too_large (workspace download) ·
404 trigger_not_found · 410 result_expired · 409 execution_failed · 502 upstream_error. The live spec lists each per operation.
Memory, skills, templates
GET /v1/memory/recall, POST /v1/memory/remember, POST /v1/memory/forget,
GET /v1/memory/runs, GET /v1/memory/runs/{id} · GET /v1/skills, POST /v1/skills/learn,
DELETE /v1/skills/{id} · GET /v1/templates, GET /v1/templates/{id},
POST /v1/templates/{id}/use. Detail: plungeai-memory, plungeai-skills-plugins,
plungeai-discovery.
The Agent Call door — POST /v1/execute (level 3)
The body (JSON or YAML) is the program, and the call always runs through the engine, so a saved bot keeps its stored policy:
| Body | Runs |
|---|---|
agent: <id> + input | your saved agent or bot, from its live YAML |
agent: <id>@3 + input | snapshot 3 of it (a pinned version) |
agent: <building block id> + that agent's own fields (or a bare agent: one-agent + prompt) | a registry agent as a one-task workflow |
workflow: (object or YAML string), or root name + tasks: | an inline workflow |
Root keys that steer the call and never reach an agent: format, input, inputs,
user_context, stream (SSE), dry_run (200 {dry_run, kind, agent, tasks, warnings}, no run,
no row, no spend), on_behalf_of. Answers carry x-ocean-agent, x-ocean-tasks,
x-ocean-version (<n>, live or inline).
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}'| Status and code | When |
|---|---|
404 agent_not_found | no saved agent of yours (or snapshot) and no building block with that id |
400 ambiguous_body / invalid_body / missing_workflow | agent: next to workflow:; a malformed agent ref or a non-boolean dry_run/stream; none of agent, workflow, tasks |
403 agent_not_active | a named agent card is parked |
424 connection_required / credential_required | a connection-tier agent and no connected account (connect in the error) |
The per-resource execute routes (/v1/workflows/{id}/execute, /v1/agents/{id}/execute) answer
exactly as before: the three levels all stay. Start with level 2; use the door when the body is
the program.