plungeai-discovery
Discovery — /v1/discovery
Discovery is the live catalog of everything the platform can do — agents, workflows, MCP servers, connectors, models — with hybrid semantic + keyword search ranked server-side.
Auth: Authorization: Bearer ozk_YOUR_KEY on every route here.
Why discovery first
Ids, capabilities, and counts change without notice. Any code (especially generated code) that names an agent, tool, MCP server, or model must have taken that id from a live discovery/list response — never from memory, docs, or this skill. Describe the capability in plain words and trust the ranking.
GET /v1/discovery/search
| Param | Values | Notes |
|---|---|---|
q | free text | Natural-language capability query ("convert pdf to markdown") |
kind | agents | skills | digital-twins | personas | experts | backgrounds | workflows | models | providers | connectors | plugins | mcp-servers | Catalog segment (plural) |
type, category, tier | strings | Additional filters |
status | default active | Leave defaulted — active means deployed and callable |
limit, offset | integers | Paging |
mode | hybrid | keyword | vector | Force one search leg (default hybrid). Response searchMethod values are hybrid/vector/text — keyword mode (and any keyword fallback) answers "text", never "keyword" |
fields | list | summary | full | Card detail level |
include | quality | Attach measured eval scores per card |
curl -s "https://api.plungeai.com/v1/discovery/search?q=web%20search&kind=agents&limit=2" \
-H "Authorization: Bearer ozk_YOUR_KEY"{
"cards": [
{ "id": "brave-agent", "name": "Brave Search", "type": "agent",
"category": "search", "status": "active",
"description": "Web search via Brave", "tags": ["search", "web"] },
{ "id": "exa-agent", "name": "Exa Search", "type": "agent",
"category": "search", "status": "active",
"description": "Semantic web search", "tags": ["search"] }
],
"count": 2,
"total": 14,
"searchMethod": "hybrid"
}The live response also carries a query echo object, and extra fields may
appear — parse what you need, don't assert the exact shape. count is the
returned page's length; total is every card the filters match, ignoring
limit/offset. Agent cards also carry credential ({"tier": "platform"} or
{"tier": "connection", "provider", "method"}).
With include=quality, each card gains a quality block —
{"score": 1, "success_rate": 1, "runs": 12} — or null when unmeasured
(no eval runs yet on that card/tier). Treat null as "unknown", not "bad".
401 → missing/invalid ozk_ key.
POST /v1/discovery/recommend
Ask the platform to pick the best card for a task instead of ranking yourself.
Body: {"type": "<card type, e.g. agent>", "task": "<what you need done>"}.
curl -s -X POST https://api.plungeai.com/v1/discovery/recommend \
-H "Authorization: Bearer ozk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "agent", "task": "summarize a long PDF into bullet points"}'{
"recommended": "pdf-agent",
"card": { "id": "pdf-agent", "name": "PDF Agent", "type": "agent",
"category": "documents", "status": "active",
"description": "Convert and extract PDFs" },
"score": 0.91
}recommended (and card) can be null when nothing matches — handle it;
a card does not always come back.
Errors — always the One API envelope ({"error": {"code", "message", ...}}),
but the code differs by where the rejection happened:
- Unparseable body →
400 {"error":{"code":"invalid_json","message":"JSON body required: { type, task }"}}. - A parseable body the registry rejects → normalized to
400 {"error":{"code":"invalid_request","message":"Invalid type: x"}}/{"code":"invalid_request","message":"Task description required"}— the registry's own flat{"error": "<string>"}is rewritten into the envelope before it reaches you (messagecarries the original text verbatim); it never reaches the caller as a bare string. Always safe to readerr.error.code. 429→rate_limited(Retry-Afterheader).5xxfrom the registry never leaks through raw — it becomes a clean502 {"error":{"code":"upstream_error", ...}}.
POST /v1/discovery/resolve
Resolve one plain-language task to ONE tool, its operation and an example call, with a
calibrated confidence — the shortcut when you want a single answer, not a ranked list.
Body: task (required), optional kinds (≤ 8 card types; default every discoverable
kind), category, status (default active).
curl -s -X POST https://api.plungeai.com/v1/discovery/resolve \
-H "Authorization: Bearer ozk_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"task": "convert a PDF to markdown", "kinds": ["agent"]}'Answer: {outcome: "match" | "uncertain" | "none", tool: {id, kind, type, name, description, category, status}, operation: {name, purpose, gated}, call: {required_params, example_cnl}, confidence, alternatives: [{id, kind, name, p}], mode}. confidence is null on the keyword fallback (the decision model was unavailable). Act on
match only; on uncertain or none fall back to GET /v1/discovery/search and read the cards. A body with no
task is 400 invalid_json; 429 rate_limited carries Retry-After.
GET /v1/discovery/cards/{type}/{id}
The full capability card as markdown (text/markdown) — the LLM-friendly
view with operations, parameters, good-at examples, and "Not for → use X
instead" redirects. Fetch it before building on an unfamiliar capability.
{type} is the singular card type (agent, skill, digital_twin, expert,
background, workflow, model, provider, connector, plugin,
mcp_server) — the plural search kind values are accepted aliases.
curl -s https://api.plungeai.com/v1/discovery/cards/agent/brave-agent \
-H "Authorization: Bearer ozk_YOUR_KEY"# Brave Search (brave-agent)
Web search via the Brave Search API.
## Operations
- search — Run a web search (params: query, count?)
...404 when no such card exists — normalized to the One API envelope,
{"error":{"code":"not_found","message":"Card not found"}}; a bad {type}
is 400 {"error":{"code":"invalid_request","message":"Invalid type: …"}}.
The registry's own flat {"error": "<string>"} response is rewritten into
this envelope (the original text lands in message) before it ever reaches
you — same normalization as /v1/discovery/recommend above.
Templates — /v1/templates
Starter workflows, agents and bots (the gallery behind plungeai_templates and Studio's template picker).
curl -s "https://api.plungeai.com/v1/templates?kind=bot&limit=3" -H "Authorization: Bearer ozk_YOUR_KEY"
# 200 {"templates":[{"id","name","kind","category","difficulty","usage_count","description"}],"count":3}
curl -s https://api.plungeai.com/v1/templates/<template id> -H "Authorization: Bearer ozk_YOUR_KEY"
# 200 {"template": {…}, "yaml": "…"}
curl -s -X POST https://api.plungeai.com/v1/templates/<template id>/use \
-H "Authorization: Bearer ozk_YOUR_KEY" -H "Content-Type: application/json" -d '{"name": "My copy"}'
# 201 {"workflow": {…}} a saved workflow of the template's kindkind is workflow, agent or bot (else 422 invalid_params); limit 1–100, default 50; category
filters. Active templates only, featured first. use keeps a bot template's own name and names the others
<template> (copy) unless you pass name (1–512 characters). The copy runs the same save-time lint as
POST /v1/workflows: 422 invalid_workflow (with errors and warnings) creates nothing;
404 template_not_found for an id that is not an active template. CLI: ocean-cli templates list | get | use.
Related
Execution trace lookups (GET /v1/traces/{id}) are a separate observability
surface — see the plungeai-results-traces skill, not this one.