JSON-RPC
Chorus exposes a single JSON-RPC 2.0 endpoint, POST /rpc. It frames the same command
catalog as the REST and MCP transports — the wire layer only validates the envelope and
dispatches to a method handler (src/rpc/routes.ts). Auth is member: send your API key
as Authorization: Bearer <key>.
Request shape
Section titled “Request shape”The envelope is standard JSON-RPC 2.0 (JsonRpcRequestSchema, src/rpc/types.ts:21):
{ "jsonrpc": "2.0", "method": "memory.store", "params": { ... }, "id": 1 }jsonrpc must be the literal "2.0", method a non-empty string, params is optional,
and id is a string, number, or null.
Method names: dot-form canonical, slash-form back-compat
Section titled “Method names: dot-form canonical, slash-form back-compat”The method registry carries two naming conventions at once (methodRegistry,
src/rpc/methods.ts:1165-1377 — 198 keys at HEAD). 102 catalog dot-form names
(memory.store, tasks.create, signals.emit.pulse) sit alongside 96 legacy
slash-form names (memory/store, signals/emit, org/create_ring). Many ops are
dual-registered under both spellings and resolve to the same catalog command; the
canon for every naming seam (tools, leases, ids, rings, signal types) lives on
Naming & conventions.
| Use | When |
|---|---|
domain.op (dot) |
Canonical. Post-#416 catalog spelling. Prefer this. |
domain/op (slash) |
Back-compat alias for existing SDK/CLI/MCP clients. |
The dot-form catalog covers the full RFC-291 execution surface (tasks.*, runs.*,
deliveries.*, checkouts.*), plus circles.*, workstreams.*, webhooks.*,
workflow.*, admin.*, and the eight signals.emit.<type> foundation methods. The
authoritative list is the registry itself.
Single, batch, and notification
Section titled “Single, batch, and notification”processRequest runs one request object; the top-level handler dispatches on body shape
(src/rpc/routes.ts:36, :106).
- Single — one object → one response object, HTTP
200. - Batch — a JSON array of requests. Each is processed in order; non-notification
results are collected into a response array, HTTP
200. An empty array isINVALID_REQUEST; a batch of all-notifications returns HTTP204(:141). - Notification — a request with no
idfield (absent, notnull). The method runs but no response is emitted; a single notification returns HTTP204(isNotification,src/rpc/types.ts:75;:164).
[ { "jsonrpc": "2.0", "method": "rings.list", "params": {}, "id": 1 }, { "jsonrpc": "2.0", "method": "signals.emit.pulse", "params": { "content": "hi" } }]The second item above is a notification (no id), so the batch response array contains
only the result for id: 1.
Responses and errors
Section titled “Responses and errors”Success returns { "jsonrpc": "2.0", "result": <any>, "id": ... }. Errors return
{ "jsonrpc": "2.0", "error": { "code", "message", "data"? }, "id": ... }
(jsonRpcResult / jsonRpcError, src/rpc/types.ts:51, :58).
Standard numeric codes (src/rpc/types.ts:10):
| Code | Constant | Meaning |
|---|---|---|
-32700 |
PARSE_ERROR |
Body was not valid JSON |
-32600 |
INVALID_REQUEST |
Envelope failed schema (or empty batch) |
-32601 |
METHOD_NOT_FOUND |
No handler for method |
-32602 |
INVALID_PARAMS |
Handler rejected params |
-32603 |
INTERNAL_ERROR |
Unhandled handler failure |
-32000 |
APP_ERROR |
A ChorusError — app code nested under error.data.code |
A ChorusError maps to APP_ERROR (-32000) with the domain code carried in
error.data.code and error.data.details (toJsonRpcError, src/rpc/types.ts:83). Note
that this differs from the REST envelope: JSON-RPC errors have no flat code string, no
request_id, and no docs_url (gap-4 §1.3).
Related
Section titled “Related”- Signals & SSE — the streaming transport.
- Webhooks — push delivery.
- Limits — rate limits and per-key quotas.