Skip to content

Errors, paging, limits ​

Format ​

JSON in, JSON out, camelCase for panel payloads (some job, schedule and audit rows keep SQL-style fields where the endpoint reference shows them). Dates are UTC ISO strings. IDs are UUIDs.

Errors ​

Errors are { "error": "<code>", "message": "<human text>" } with one of these statuses:

StatuserrorMeaning
400bad_requestInvalid input; the message says what
401Authentication required or invalid
403forbiddenNot allowed (role, permission, token scope, IP allow-list, suspended)
404Not found, or you may not know it exists
409conflictState conflict (quota exceeded, already running, lease lost)
413Body or upload too large
429Rate limited
502An upstream (plugin, catalog) failed
503A feature is not set up (object storage, plugin host, email)
504Timed out waiting for a node

Some errors carry extra details (for example permissions_changed on a plugin update lists the new permissions).

Paging ​

List endpoints for customers, servers, jobs and activity accept limit (1 to 100, default 50) and offset. They return arrays.

Rate limits ​

Authentication and other sensitive writes are throttled with fixed windows stored in PostgreSQL, shared by every API replica. Exceeding them returns 429. Behind a reverse proxy set TRUST_PROXY, otherwise all callers share the proxy's address (see Reverse proxy).

Request size ​

JSON bodies are limited to 12 MB; file transfers to 1 GiB; the file editor to 1 MiB.

CORS and headers ​

Only WEB_ORIGIN is allowed, with credentials. Content-Disposition is exposed for downloads. The API sends defensive headers (X-Content-Type-Options, frame protection) and the panel sends a Content-Security-Policy.

Idempotency and jobs ​

Mutating server operations queue a job and return { jobId, state }. Poll GET /api/jobs?serverId= or watch the WebSocket. Jobs are durable and leased to the node; a repeated request creates a repeated job, so check the state before retrying.

Released under the AGPL-3.0-only license.