One API
Tools plane
A tool agent is any active registry agent with a typed invocation contract. The tools plane is the shortest path from "I want Brave search" to a result: three routes, one security check, one execution path. Every call becomes a one-task workflow on the engine, so it lands in your execution history and carries an x-request-id you can trace (Traces).
Discover ──► GET /v1/tools who is callable (the active catalog)
Inspect ──► GET /v1/tools/:id the contract: operations, params, examples
Execute ──► POST /v1/tools/:id/execute contract check → trust fence → engine → resultAuth: Authorization: Bearer $PLUNGE_API_KEY (an ozk_ key — see Authentication & keys).
6.1 List the catalog — GET /v1/tools
Returns the active registry agents as summary cards. Paging: limit (default 50, max 100) and offset. The response count is the size of the page you received, not the catalog total — page until a short page comes back.
curl -s "https://api.plungeai.com/v1/tools?limit=100" \
-H "Authorization: Bearer $PLUNGE_API_KEY"Response shape: { "tools": [ { "id", "type": "agent", "name", "description", "category", "tags": [...], "status": "active", "credential": {...}, ... } ], "count" }. credential says what the agent runs on: { "tier": "platform" } (our keys — nothing to set up) or { "tier": "connection", "provider", "method": "oauth" | "api_key" | "wallet" } (the caller's own account at that provider — see Connectors). Tags worth reading: gated:<operation> marks operations the trust fence will refuse unattended (see 6.4), status:active is the only status this route lists.
On 2026-09-18 the catalog had 92 active tool agents in 14 categories (GET /v1/agents/categories gives the live breakdown; the same query backs GET /v1/agents, so the two lists are identical — the difference is how you call them).
6.2 Inspect the contract — GET /v1/tools/:id
curl -s "https://api.plungeai.com/v1/tools/brave-agent" \
-H "Authorization: Bearer $PLUNGE_API_KEY"| Field | Meaning |
|---|---|
agent_id, name, description | the card identity |
operations[] | { name, description?, required_params? } — the verbs the agent accepts as operation; an agent with no operations takes a prompt (or its soleRequiredParam) |
inputSchema | JSON Schema (type: object, properties, required, additionalProperties) for params; per-operation requireds live on operations[].required_params |
examples[] | { title, cnl } — copy-paste CNL task snippets from the card's demonstrations |
output_type | prose description of what comes back |
credential | { tier: "platform" } or { tier: "connection", provider, method } — the same value the list carries; GET /v1/tools/stripe-agent answers { "tier": "connection", "provider": "stripe", "method": "api_key" } |
soleRequiredParam | present when exactly one string param is required — a bare prompt is mapped onto it |
credentials | filled by surfaces that know the user's connections; absent here |
A connection-tier agent says so in the same card:
curl -s "https://api.plungeai.com/v1/tools/stripe-agent" \
-H "Authorization: Bearer $PLUNGE_API_KEY"Unknown id → 404 unknown_tool (also in the X-Error-Code header):
curl -s -o /dev/null -w '%{http_code} %header{x-error-code}\n' \
"https://api.plungeai.com/v1/tools/no-such-agent" \
-H "Authorization: Bearer $PLUNGE_API_KEY"6.3 Execute — POST /v1/tools/:id/execute
Body (JSON by default; the same fields as YAML with Content-Type: text/yaml). The tool's fields may be
sent bare at the root, exactly as the agent's README documents them ({"expression": "7*6"}); params:
is still accepted and wins on a name clash.
| Field | Type | Notes |
|---|---|---|
operation | string | one of operations[].name; omit for prompt-only agents |
| the tool's fields | per the contract | bare at the root (query, count, expression, …); validated against the contract |
params | object | the same fields wrapped; merged over the bare ones. Names that collide with task-envelope fields (agent, id, type, …) are reserved and rejected either way |
prompt | string | free-text input; satisfies missing requireds on prompt-capable agents |
format | json · yaml · markdown · text | response negotiation; Accept and the request Content-Type also count (default JSON, YAML in → YAML out) |
Order of evaluation, from orchestration/api-gateway/routes/tools.ts:
- Contract validation.
reservednames, or missing requireds on a prompt-less call →422 invalid_paramswith{ missing, reserved, warnings, contract }— the contract is echoed so you can fix the call without a second request. - The trust fence (
checkAgentCall, modeunattended) →403 refusedor409 approval_required— see 6.4. Nothing is executed and no execution row is written. - Execution. A
workflow_executionsrow opens under your user (execution_id= the request's trace id), the engine runs the one-task wrapper, the row closes with the result.
Success (200):
{ "ok": true, "content": "…markdown or text…", "outcome": "ok", "execution_id": "…", "request_id": "…" }curl -s -X POST "https://api.plungeai.com/v1/tools/brave-agent/execute" \
-H "Authorization: Bearer $PLUNGE_API_KEY" -H "Content-Type: application/json" \
-d '{"prompt":"cloudflare workers pricing","count":3}'Bare fields, YAML in → YAML out (the Content-Type mirror):
curl -s -X POST "https://api.plungeai.com/v1/tools/calculator-tool-agent/execute" \
-H "Authorization: Bearer $PLUNGE_API_KEY" -H 'Content-Type: text/yaml' --data-binary $'expression: "7*6"\n'Operation form (the weather agent takes location; here the contract says what is missing):
curl -s -w '\n%{http_code}\n' -X POST "https://api.plungeai.com/v1/tools/weather-agent/execute" \
-H "Authorization: Bearer $PLUNGE_API_KEY" -H "Content-Type: application/json" \
-d '{"operation":"current","params":{}}'6.4 Outcomes and the trust fence
An agent that cannot do the job answers with a structured outcome, not prose (core/core-base/agent-outcome.ts). The tools plane maps outcomes onto HTTP (routes/_util.ts outcomeStatus):
outcome | HTTP | error.code | What to do |
|---|---|---|---|
ok | 200 | — | read content |
needs_input | 422 | invalid_params | fix params — error.detail and the echoed contract say how |
needs_connection | 424 | connection_required | the account behind the call has not connected this provider — error.connect names the way in, see Connectors |
needs_api_key | 424 | credential_required | the agent needs a key the account has not stored — error.connect names the way in |
unavailable | 503 | agent_unavailable | upstream/provider down — retry later or pick another agent |
error | 502 | execution_failed | the agent failed; error.message carries the agent's own text |
The error body is { "error": { "code", "message", "outcome", "detail"?, "content"?, "contract"?, "connect"? } } plus an X-Error-Code header. On a 424, connect is { "provider", "method", "link": "POST /v1/connections/<provider>/link" }; the Markdown, YAML and text wires print it as a connect: line under the execution footer.
The trust fence (core/core-base/call-agent.ts) runs before every execution and cannot be turned off from the API:
- Always-gated operations —
buy,purchase,fetch_paid,send,transfer,pay,send_payment,withdraw,shop— plus anygated:<op>tag on the card. On this unattended surface they return403 refused(a human-in-the-loop surface such as Studio or a harness mission with an explicit allow-list returns409 approval_requiredinstead). - Guarded categories — agents in the
paymentandblockchaincategories are refused unattended unless a mission'sallowed_agentsnames them. - Not active — a card that is not
status:activeis403 agent_not_active; an unknown id is404 unknown_tool.
curl -s -w '\n%{http_code}\n' -X POST "https://api.plungeai.com/v1/tools/resend/execute" \
-H "Authorization: Bearer $PLUNGE_API_KEY" -H "Content-Type: application/json" \
-d '{"operation":"send","params":{"from":"hello@example.com","to":"nobody@example.com","subject":"x","text":"x"}}'A refusal writes no execution row — a denial is not a run.
6.5 The catalog by category (live 2026-09-18)
Ids are exact; GET /v1/tools/<id> is the source of truth for operations and for credential. Credential is that field: platform runs on our keys; connection needs the caller's own account at the named provider, connected once over the API (chapter 7). A card whose agent also needs an account that is not yet in the machine-readable table (core/core-base/credential-tiers.ts) reads platform; those are named in the row.
| Category | Agents | Credential |
|---|---|---|
| search (14) | brave-agent, exa-agent, firecrawl-agent, tavily-agent, serper-agent, serpapi-agent, crawl4ai-agent, browserbase, arxiv-search, wikipedia-search, newsapi-get-articles, nyt-api, product-hunt-agent, branding-agent | platform |
| tools (23) | calculator-tool-agent, markitdown, weather-agent, wolfram-alpha-full-results, data-analyst, code-reviewer, technical-writer, research-analyst, research-team, learn, pdf-worker, pptx-worker, xlsx-worker, ms-office-worker, report-worker, render-worker, cloud-browser-agent, gemini-file-search, google-docs-export-worker, n8n-converter, concierge, agentic-agent, universal-agent | platform (google-docs-export-worker: connection · google) |
| ai (3) | llm-agent, vision-agent, skill-agent | platform |
| google-workspace (25) | google-gmail, google-calendar, google-drive, google-docs, google-sheets, google-slides, google-forms, google-tasks, google-contacts, google-chat, google-meet, google-keep, google-classroom, google-admin, google-vault, google-analytics, google-search-console, google-ads, google-merchant-center, google-business-profile, google-books, google-youtube, google-translate, cloud-natural-language, google-workspace-assistant | connection · google · OAuth (google-books, google-translate, cloud-natural-language: platform) |
| microsoft-365 (12) | microsoft-outlook-mail, microsoft-outlook-calendar, microsoft-outlook-contacts, microsoft-onedrive, microsoft-sharepoint, microsoft-teams, microsoft-todo, microsoft-planner, microsoft-onenote, microsoft-excel, microsoft-word, microsoft-powerpoint | connection · azure · OAuth |
| coding-agents (1) | gitlab-agent | connection · gitlab · OAuth (a personal access token works too) |
| communication (2) | resend, cloudflare-email-agent | platform — send/send_batch gated |
| media (2) | image-gen-agent, video-gen-agent (generate_video gated) | platform |
| financials (1) | sec-agent | platform |
| entertainment (2) | sports-schedule-agent, tv-listings-agent | platform |
| ecommerce-agents (2) | amazon-shopping-agent, shopping-zinc (buy gated) | platform — amazon-shopping-agent also needs an Amazon retailer login, made in Studio |
| payment (2) | x402-wallet-agent (fetch_paid gated), crossmint-agent | x402-wallet-agent: connection · x402-wallet · wallet; crossmint-agent: platform — guarded category |
| blockchain (2) | circle-agent, circle-wallet-agent | platform — the Circle wallet is connected in Studio; guarded category |
| shopping (1) | x402-broker (execute gated) | platform — needs a wallet connected in Studio |
6.6 Using tools from your own LLM loop
The tools plane is designed to sit behind any function-calling model — no SDK required:
GET /v1/tools/:idfor the agents you want; turn each contract into a function definition (name= agent id,parameters=inputSchema, one function peroperations[].nameif you want the model to pick operations).- Send the definitions to your model as
tools(any provider — the models plane works too). - When the model returns a tool call,
POST /v1/tools/<id>/executewith{ "operation", "params" }. - Feed
contentback as the tool result. On422feed backerror.message+error.detail— the model fixes its own params. On403 refusedtell the user a human has to approve that action.
Every hop carries x-request-id; keep it in your logs and GET /v1/traces/:id shows the run. For a server-side loop that already does all of this (discover → inspect → execute with an approval gate), call the concierge agent on the agents plane.