JSON-RPC API Reference

Arona exposes a JSON-RPC 2.0 surface at /api/rpc for the management plane: auth, keys, providers, agents, memory, conversations, usage, billing, video, realtime and streaming chat. It complements the OpenAI-compatible REST surface (/v1/*, see OpenAI-compatible REST API); use REST for key-authenticated inference workloads and JSON-RPC for session/account management and streaming control. The Quickstart walks through the first end-to-end turn.

The surface dispatches 39 request methods plus one anonymous WebSocket-only liveness method, system.probe (40 methods total). Every request is a JSON-RPC 2.0 object with jsonrpc: "2.0", a method string, an optional params object and an optional id.

Transport

Ids

Request id values are echoed with type fidelity: nullnull, strings → strings, integers → numbers, and anything else (floats, objects, integers outside the i64 range) → the JSON string rendering. An omitted id is answered with null.

Server → client notifications (SSE sidecar)

Tokens, deploy progress and realtime events are not delivered on the WebSocket socket. Each streaming RPC creates a session id and pushes notifications to GET /api/rpc/events?session=<session_id> as server-sent events. Subscribe to the SSE endpoint before or immediately after the RPC call returns a session id — notifications emitted between the call returning and the SSE subscription being established are dropped (the pre-subscription window). The recommended pattern is to open the SSE stream first, then fire the RPC.

Notification methods: chat.stream (one token per event from chat.send), models.progress (agent model download progress from agents.deploy), realtime.event (server events for an open realtime session), and video.progress / video.done / video.failed (async video jobs). See the full catalog in Events & Notifications.

Error codes

CodeNameMeaning
-32700Parse errorRequest body is not valid JSON.
-32600Invalid requestRequest object is malformed, e.g. a missing method.
-32601Method not foundUnknown method string; the message echoes it.
-32602Invalid paramsparams failed deserialization for the method.
-32603Internal errorUnexpected server failure.
-32000APP_ERRORGeneric application error — e.g. conversation/provider/agent not found, no online agent available for deploy.
-32005AUTH_ERROR"Authentication required" — missing or invalid JWT. Also used by admin-token methods when the bearer token does not match ARONA_ADMIN_TOKEN ("Admin access required").
-32006QUOTA_ERRORMonthly billing quota exceeded for a JWT-gated RPC method (chat.send).
-32007ADMIN_REQUIREDAuthenticated non-admin calling an admin-gated method (agents.*, engine.invoke); the message includes a method-specific hint.

The agents.* and engine.invoke methods are admin-only: they require a JWT whose account has users.is_admin = true. An authenticated non-admin is rejected with -32007 (ADMIN_REQUIRED); an unauthenticated caller gets the standard AUTH_ERROR so the server does not reveal that the method is privileged.

Auth legend

LegendCredentials
publicNo credentials required.
JWTAuthorization: Bearer <jwt> on HTTP, or ?token=<jwt> on WebSocket.
admin (JWT + is_admin)Bearer JWT of an account with users.is_admin = true.
admin tokenBearer ARONA_ADMIN_TOKEN (env-configured; when unset the method is always denied, default-deny).

All example credentials and addresses in this document are placeholders (RFC 5737 documentation IPs, sk-xxx keys). See Authentication & Security for the full auth model behind this legend.

Chat

MethodAuthParamsDescription
chat.sendJWTmodel (string), messages (array of { role, content, images?, tool_calls? }), temperature? (number), max_tokens? (integer), conversation_id? (string), memory? (bool), extra? (object), tools? (array of OpenAI-style function definitions), provider? (string)Send a streaming chat turn. Returns { "stream_id", "memory" }memory is the recall state (enabled / disabled / offline); tokens arrive as chat.stream notifications on the SSE sidecar. With a conversation_id, the completed persisted history is assembled server-side and the turn is persisted. Billing-gated (monthly quota → -32006); usage is recorded under jwt-<user-uuid>.

Realtime (full-duplex audio/video sessions)

MethodAuthParamsDescription
realtime.startJWTmodel (string), config? (session config object), conversation_id? (string)Open a full-duplex session against the backend serving model. Returns { "session_id", "stream_session" }: use session_id for realtime.event / realtime.stop, and subscribe to stream_session on the SSE sidecar to receive realtime.event notifications.
realtime.eventJWTsession_id (string), event (client event — audio append/commit/clear, image frame, response create/cancel, session stop)Send one client event into an open session; it is forwarded to the upstream backend. Returns { "ok": true }.
realtime.stopJWTsession_id (string)Close and remove a session. Returns { "removed": bool }.

Engine (generic perception/control channel)

MethodAuthParamsDescription
engine.invokeadmin (JWT + is_admin)model (string), method (string), params? (object)Synchronous request/response invocation of an arbitrary engine method on the backend serving model — the high-frequency channel for sensor.ingest / control.setpoint style calls (20–30 Hz loops). The result is the backend's raw response.

Auth

MethodAuthParamsDescription
auth.registerpublicemail, password, name?Register an account. Only allowed while registration is open (ARONA_REGISTRATION_OPEN); the first registered user becomes the admin. Returns the same token response as auth.login (access_token, refresh_token, token_type, expires_in, user).
auth.loginpublicemail, passwordLog in. Returns access_token, refresh_token, token_type, expires_in, user ({ id, email, name, is_admin }). Rate-limited per IP and account.
auth.refreshpublicrefresh_tokenExchange a refresh token for a fresh access token (and a new refresh token). Reused or expired refresh tokens are rejected with AUTH_ERROR.
auth.meJWTCurrent user profile: { "id", "email", "name" }.

Keys

MethodAuthParamsDescription
keys.listJWTList the caller's API keys (id, name, key_prefix, project, timestamps, active flag).
keys.createJWTname, project?Create an API key. Returns { id, name, key, key_prefix, project, created_at } — the full arona-<uuid> secret in key is shown once; store it immediately.
keys.revokeJWTkey_idRevoke an API key. Returns { "ok": true }.

Providers

MethodAuthParamsDescription
providers.listpublicList known providers: built-in official entries plus custom ones, as display metadata (id, name, description, website_domain, is_official, is_operator). Public by design — the list carries no credentials; only the mutations below are JWT-gated.
providers.addJWTid, name, description?, website_domain?Add a custom provider entry. Returns { "ok": true }.
providers.updateJWTprovider_id, name?, description?, website_domain?Update a custom provider's fields (only the provided ones). Returns { "ok": true }.
providers.removeJWTprovider_idRemove a custom provider. Returns { "ok": true }.
providers.testJWTTest a provider connection. Stub: returns { "ok": true, "message": "Provider connection test not yet implemented" }.

Agents

All agents.* methods are admin-only (JWT + is_admin). Agent nodes connect outbound over GET /ws/agent; this RPC group controls the registry (see Agent Cluster).

MethodAuthParamsDescription
agents.listadmin (JWT + is_admin)List registered agent nodes: id, name, host, online/offline status (heartbeat-based), GPU summary, deployed models, version, timestamps.
agents.registeradmin (JWT + is_admin)machine_name, versionRegister an agent node with the tunnel manager. Returns { "agent_id", "token" } (the token is the agent's control-plane credential).
agents.deregisteradmin (JWT + is_admin)agent_idDeregister (disconnect) an agent. Returns { "ok": true }.
agents.statusadmin (JWT + is_admin)agent_idPer-agent status: online flag, host, GPU summary, loaded models, GPU utilization, heartbeat/connection timestamps.
agents.deployadmin (JWT + is_admin)model_id, agent_id? (empty/missing = least-loaded node; errors if none online)Deploy a model on an agent. Returns { "ok": true, "stream_id" } — subscribe to stream_id on the SSE sidecar for models.progress download notifications.
agents.stopadmin (JWT + is_admin)agent_id, model_idStop a deployed model. Returns { "ok": true, "stream_id": null } (no progress stream).

Memory

Long-term memory is served by the entelecheia Philia service over a WebSocket; failures never block chat (see Memory Gateway).

MethodAuthParamsDescription
memory.statusJWTMemory gateway state: { "enabled", "writeback", "events" } — flags plus up to 50 recent activity events (newest first).
memory.deleteJWTnode_idDelete a stored memory node. Returns { "deleted": bool }.

Conversations

MethodAuthParamsDescription
conversations.listJWTList the caller's conversations with relative-age timestamps.
conversations.createJWTtitle? (default New Conversation)Create a conversation. Returns the new conversation object.
conversations.getJWTconversation_id (legacy alias: id)Fetch a conversation with its messages. Ownership-checked; cross-user access is rejected.
conversations.deleteJWTconversation_id (legacy alias: id)Delete a conversation (owner only). Returns { "ok": true }.

conversations.get / conversations.delete also accept the legacy id key from older dashboard clients; conversation_id wins when both are present.

Usage

MethodAuthParamsDescription
usage.listJWTlimit? (integer, default 50, clamped to 1–200), offset? (integer, default 0), project? (string)Paginated usage records for the caller, newest first, covering both API-key rows (arona-XX prefix) and JWT-attributed rows (jwt-<user-uuid>). Returns { "records", "total", "limit", "offset", "project" }; the project filter narrows to key-tagged rows only.

Billing

Tiers, quotas and usage accounting are described in Billing & Usage.

MethodAuthParamsDescription
billing.planJWTCurrent billing state: { "tiers", "current_tier", "usage", "remaining", "quota_exceeded" } — monthly usage (cost_usd, tokens, request count) and remaining quota.
billing.plan.setadmin tokenuser_email, tierSet a user's billing tier. Returns { "ok": true }. Denied with AUTH_ERROR when the bearer does not match ARONA_ADMIN_TOKEN.
billing.video.pricing.getJWTVideo pricing table. Returns { "pricing": [...] }.
billing.video.pricing.setadmin tokenmodel, mode? (default per_second_resolution), base_price? (number, default 0), price_per_second? (number, default 0), price_per_frame? (number, default 0), resolution_coeff? (object), currency? (default USD), enabled? (bool, default true)Upsert video pricing for a model. Returns { "ok": true }. Denied with AUTH_ERROR when the bearer does not match ARONA_ADMIN_TOKEN.

Video

Async video generation jobs (see Realtime & Video). Job progress is pushed as video.progress / video.done / video.failed notifications on the session channel.

MethodAuthParamsDescription
video.createJWTmodel, prompt, negative_prompt?, images? (array of { data_base64, mime_type }), duration_seconds? (integer), width? (integer), height? (integer), provider? (string), extra? (object)Submit an async video generation job. Returns { "job_id", "stream_id" } — subscribe to stream_id for progress notifications.
video.getJWTjob_id (UUID)Poll a job's status/result (status, progress, result, error, cost).
video.listJWTlimit? (integer, default 20)List the caller's jobs. Returns { "jobs": [...] }.
video.cancelJWTjob_id (UUID)Cancel a running job. Returns { "ok": true }.

System

MethodAuthParamsDescription
system.statuspublicAggregate gateway status: { "agents_online", "gpu_nodes", "models_deployed", "requests_total", "requests_per_minute", "uptime_seconds" }.
system.probeanonymous (WS only)One-shot liveness probe over the WebSocket transport. The server acks { "ok": true, "status": "ok" } and then closes the socket — anonymous visitors never hold an open connection. Any other method on an unauthenticated socket is rejected with AUTH_ERROR.