Reference
HTTP contract
This is the only hard artifact. There is no SDK to keep in sync and no UI package to import — if your product speaks these routes, it works, and it keeps working when the internals move.
kernel/harness/src/server.ts is authoritative and contract.ts holds the types. Where this page and that code disagree, the code wins; tell us and we will fix the page.
Start with four routes
Most products never need more than this. Build these before you read the rest of the page.
POST /v1/tasks # start work
GET /v1/tasks/:id/events # watch it (SSE, resumable)
GET /v1/approvals?status=pending
POST /v1/approvals/:id/approve # { edited? }Three planes, three credentials
Getting a 401 usually means you are on the wrong plane, not that the token is wrong.
| Plane | Credential | Who holds it |
|---|---|---|
Founder — /v1/* | Authorization: Bearer msk_… | msess_… | Your server, and the portal |
Client — /v1/portal/* | Bearer mcli_… from a magic link | Your customer's browser, via your app |
Sandbox — /v1/internal/* | Per-task nonces and a gate token | The agent. Never a human, never your product |
The planes do not overlap. A client token on a founder route is an unrecognised credential, not a narrowed one. GET /health and the five /v1/auth/* routes are the only open endpoints.
Scope: a project key sees exactly its project. A member sees the whole org and must send X-Mycel-Project on writes if they have more than one. Out-of-scope ids return 404, never 403.
Tasks
| Method | Path | Notes |
|---|---|---|
| POST | /v1/tasks | { wedge, task_type, input?, actor?, constraints?, tools?, output_schema? }. Honours Idempotency-Key. 429 over 120/min |
| GET | /v1/tasks | ?status=&wedge=&limit=, newest first |
| GET | /v1/tasks/:id | Use to reconcile after a stream gap |
| GET | /v1/tasks/:id/events | SSE. Last-Event-ID header or ?lastEventId= |
| POST | /v1/tasks/:id/cancel | Only reliable on the replica running the task |
| POST | /v1/tasks/:id/feedback | { rating?, correction?, note? } → becomes knowledge |
| GET | /v1/artifacts/:id | Raw body with the artifact's content type |
constraints are max_runtime_s, max_cost_usd and approval_required, all clamped down to the server ceilings — a client cannot raise them.
Approvals
GET /v1/approvals?status=pending|auto_approved|approved|rejected|expired
POST /v1/approvals/:id/approve { "edited": { … } } → { ok, decision }
POST /v1/approvals/:id/reject → 409 if already decidedThe identifier field on an Approval is approval_id, not id.
Connections, channels, clients
| Method | Path | Notes |
|---|---|---|
| POST | /v1/connections | { kind, name, owner?|client_id?, config?, secret_ref? } |
| GET | /v1/connections · /v1/connections/:id | Returns has_secret, never the value |
| POST | /v1/connections/:id/secret | { value } — write only |
| POST/GET | /v1/composio/toolkits · /v1/composio/toolkits/:toolkit/connect · /v1/connections/:id/composio/status | 501 without COMPOSIO_API_KEY |
| POST | /v1/channels | { connection_id, address, wedge, task_type } — all required |
| POST | /v1/channels/:id/inbound | Verify the provider signature in your app first. → { task_id, thread_id, client_id } |
| POST/GET | /v1/clients · /v1/clients/:id | { display_name?, handles?, metadata? } |
| POST | /v1/clients/:id/portal-link · /portal-revoke | Raw token returned once; only its hash is stored |
| GET | /v1/threads/:id | Thread plus messages |
Wedges and knowledge
GET /v1/wedges/:wedge # manifest, skills, authored + live knowledge
GET /v1/wedges/:wedge/knowledge
POST /v1/wedges/:wedge/knowledge # { name, content, kind?, source?, metadata? }
PUT /v1/knowledge/:id
DEL /v1/knowledge/:id
GET /v1/wedges/:wedge/intake # question queue + coverage percentage
POST /v1/wedges/:wedge/intake/:question # { answer }
POST /v1/wedges/:wedge/intake/:question/dismissThe service surface
Only reach for these when the work outlives a single task. Cases are a lifecycle, records are a per-wedge key-value store, schedules make work arrive on its own.
GET|POST /v1/cases GET|PUT /v1/cases/:id
POST /v1/cases/:id/tasks # spawn a task on the case; 409 if closed
GET|POST /v1/records GET|DELETE /v1/records/:id
# ?where=<JSON>; upsert on (wedge, collection, key)
GET|POST /v1/schedules GET|PUT|DELETE /v1/schedules/:id
POST /v1/schedules/:id/run # fire now
GET|POST /v1/blueprints… # provision / readiness / activate
GET /v1/analytics?days=30
GET /v1/audit · /v1/audit/verify # tamper-evident chain
GET /v1/tasks/:id/trace # the run as a span tree, folded from the event log
GET /v1/meta # wedges, store, sandbox
GET /health # openSchedule cadences are { kind: "every", seconds }, { kind: "daily", hour, minute } or { kind: "monthly", day, hour, minute }, all UTC.
The client plane
POST /v1/portal/session # { token: "mpl_…" } → mcli_ session. Single use
GET /v1/portal/me
GET /v1/portal/threads · /threads/:id
POST /v1/portal/threads/:id/messages # { body } → message + task_id. 413 over 10,000 chars
GET /v1/portal/tasks/:id/events # filtered stream — no cost, no approvals
GET /v1/portal/casesA customer replying on a thread starts work— the message spawns a task on the thread's channel. That is the shortest path from "customer said something" to "agent is on it".
The sandbox plane
You do not call these. They exist so you can reason about what the agent can reach: /v1/internal/gate (approval for tool calls), /v1/internal/actions/:capability (gated), /v1/internal/reads/:capability (ungated, capped), /v1/internal/llm/* (proxied model calls), /v1/internal/records/*, /v1/internal/case*, /v1/internal/workflows/:name, /v1/internal/knowledge/gap.
Each is authenticated by a per-task nonce that expires with the run. None of them accept a founder key. See The trust boundary.
Errors
| Status | Means |
|---|---|
| 400 | Missing field, unknown wedge or task type, unknown stage, or a member write with no X-Mycel-Project |
| 401 | No credential, or a credential from the wrong plane |
| 403 | Wedge not enabled for the project, or a role that cannot do this |
| 404 | Absent, or out of scope. The two are indistinguishable on purpose |
| 409 | Approval already decided, case closed, or blueprint not ready |
| 429 | Task creation rate limit, or the per-task read cap |
| 501 | Composio route with no API key configured |
Errors are { "error": "lower-case sentence" }. They are written to be shown to a developer, not parsed.
What to read next
Integrating a frontend — the handful of these routes that need care when proxied.