API reference
The Catentio control plane is a REST API that uses JSON for both requests and responses. It's the API the portal talks to, and these pages document its wire protocol: paths, params, object shapes, error bodies.
Read the reachability section first — there are two APIs here, and they are not the same surface.
Reachability
The public API is live at
https://catent.io/v1/. It authenticates with an API key (Authorization: Bearer cat_pk_...) and is the surface you should build against.It is small and deliberate: nine endpoints, listed below. It is not the same thing as the rest of this reference.
The public API — catent.io/v1/*
Nine endpoints, authenticated with an API key you create in the portal. This is what the SDKs, the CLI, and other Forjio products call.
| Endpoint | What it does |
|---|---|
GET /v1/agents |
List the agents you can invoke (slug + description). |
GET /v1/workflows |
List workflows, with the parameters each one declares as required. |
POST /v1/runs |
Dispatch an agent run. |
GET /v1/runs/{id} |
Status and output of a run. |
POST /v1/projects |
Start a pipeline project from a workflow. |
GET /v1/projects/{id} |
Status of a project. |
POST /v1/webhooks |
Register a URL to be POSTed when runs/projects finish. |
GET /v1/webhooks |
List your endpoints. |
DELETE /v1/webhooks/{id} |
Delete an endpoint. |
Anything not on that list returns 404 on catent.io/v1/. That is the design, not an omission: the public surface is an explicit allowlist, so an internal endpoint cannot be exposed by accident. See Authentication.
The control-plane API — everything else in this reference
The remaining pages document the internal control-plane protocol: the API the portal itself talks to. It is not public and is not reachable with an API key. Those paths are reached from a signed-in browser through the portal's proxy:
/api/v1/cp/<control-plane-path>
It reads your catentio_session cookie, forwards it to the control plane as the x-catentio-session header, and passes the response back. Without the cookie it returns 401 {"error":"auth_required"}. It does not accept Authorization: Bearer.
So GET /v1/runs in the control-plane sense is reached from a signed-in browser as GET /api/v1/cp/v1/runs — which is a different thing from the public GET /v1/runs above, even though the paths look alike. When in doubt: a cat_pk_ key talks to the public API; a browser session talks to the control plane.
Authentication
| Credential | Where it works |
|---|---|
x-catentio-session header (portal cookie, HMAC-signed) |
Every control-plane route. This is the one the portal uses. |
Authorization: Bearer <huudis-jwt> |
Only /v1/billing/*, /v1/chat/* (except /attachments and /call/turn), and /v1/workspaces list/get/create/delete. |
Authorization: Bearer cat_pk_... (API key) |
Verified by the runtime, not by the control plane. See below. |
Most routes accept the session header and nothing else. The Bearer-JWT path exists on a small set of routes for CLI/service callers.
API keys are a runtime credential. Keys minted at /v1/api-keys carry the prefix cat_pk_ and are checked by the agent runtime's auth guard — the control plane never looks at them. Scopes are not implemented: the scopes field is accepted and discarded, and a key grants full access to its customer's data.
A few routes are gated further: PATCH /v1/workspaces/{id}/state, /v1/crm-admin/* and GET /v1/customers are admin-only; /v1/gojo/* is internal-tenant-only.
See Authentication for the full picture.
Response shapes
There is no response envelope. The control plane returns plain FastAPI JSON.
List endpoints return a data array plus a per-route meta:
{
"data": [ /* items */ ],
"meta": { "total": 412, "limit": 50, "offset": 0 }
}
meta differs by route — the runs list carries {total, limit, offset}, the agents list carries {total, builtin, custom}. Detail endpoints return the object directly, with no wrapper.
Pagination is limit / offset. There are no cursors, no nextCursor, no hasMore.
Errors are FastAPI's detail body:
{ "detail": "auth_required" }
When the failure originated in the runtime, the control plane re-raises the runtime's status code and nests its payload:
{ "detail": { "code": "plan_limit_exceeded", "resource": "runs_per_day", "limit": 100, "tier": "free" } }
Validation failures are 422 with FastAPI's standard validation body. There is no error key, no requestId, and no timestamp on any response.
Resources
| Resource | Path prefix | What it does |
|---|---|---|
| Workspaces | /v1/workspaces |
Tenants. |
| Agents | /v1/agents |
The agent roster + CRUD. (invoke is broken — see the page.) |
| Runs | /v1/runs |
Start a run, list history, read events, cancel, retry. |
| Projects | /v1/projects |
Pipeline-driven workflows: lifecycle, gates, backtrack. |
| Workflows | /v1/workflows |
Versioned workflows — full CRUD. |
| Run artifacts | /v1/run-artifacts |
Run outputs: recaps, comments, PIN-protected public sharing. |
| Tools | /v1/tools |
The capability registry. |
| Skills | /v1/skills |
Versioned agent playbooks. |
| Memory | /v1/memory |
RAG entries (OTP-gated mutations). |
| API keys | /v1/api-keys |
Static cat_pk_* credentials. |
| Billing | /v1/billing |
Catalog, summary, subscribe, wallet, top-up. |
| Cost | /v1/cost |
Workspace-level rollups. |
| Integrations | /v1/integrations |
BYO credentials. |
| Webhook gates | /v1/webhooks |
Inbound gates that block a pipeline step. List only. |
| Scheduled jobs | /v1/scheduled-jobs |
Runtime cron. |
| Feature flags | /v1/feature-flags |
Runtime kill switches. |
| Files | /v1/files |
One route: GET /v1/files/download. |
| Output destinations | /v1/output-destinations |
Where agent outputs go. |
| Heartbeats | /v1/heartbeats |
Cron-triggered agent invocations. |
| System | /v1/system |
Health, info, reload. |
| Chat | /v1/chat |
The chat-bubble backend. |
| Events | /v1/events |
Event archive feed. |
Limits
There is no HTTP rate limiting: no per-second caps, no X-RateLimit-* headers, no 429, no Retry-After.
What can reject a request is a plan cap, enforced in the runtime and surfaced as 403:
| Cap | Free | Starter | Pro |
|---|---|---|---|
| Runs per day | 100 | 5,000 | unlimited |
Custom agents, tools and skills carry per-tier count caps too. Over a cap you get 403 with a plan_limit_exceeded detail carrying resource, current, limit and tier.
There is no Idempotency-Key support. Retrying a POST /v1/runs spawns a second run.
Webhooks
There are two webhook features and they point in opposite directions. Do not confuse them.
Outbound — Webhooks (outbound). Register a URL on the public API and Catentio POSTs to it when a run or project finishes. Three events (run.succeeded, run.failed, project.completed), HMAC-signed with X-Catentio-Signature: t=…,v1=…, retried with backoff. This is what you want if you'd rather not poll.
Inbound gates — Webhook gates. A pipeline step declared with a webhook gate blocks until an external system calls back to resolve it. GET /v1/webhooks (control plane) lists those gates.
There is no WebSocket and no SSE stream. The portal polls.
OpenAPI spec
There is no published OpenAPI file. The wire protocol's source of truth is the FastAPI route definitions — the control plane in saas-catentio (control_plane/app/routes/) and the agent runtime in catentio (app/runtime/). These reference pages are written against them.
Next
- API authentication — what each credential actually opens.
- Runs — start here; it's the resource everything else orbits.
- Concepts — the data model the API exposes.