# API reference The Qaldy control plane REST API. How to sign in, what an error looks like, what is limited, and every route, generated from the code that serves it. URL: https://docs.qaldy.com/reference/api/ The API is the same one the console, the `qaldy` command line and the MCP server use. Everything here is generated from the live route table and its zod schemas when this site is built: **252 routes**, grouped by what they are for, in the list on [All routes](https://docs.qaldy.com/reference/api/routes/). The same document as OpenAPI 3 is at [openapi.json](https://docs.qaldy.com/openapi.json), and the running API serves it at `https://cloud-api.qaldy.com/openapi.json`. :::pilot Pilot The API is in a live pilot. Routes can be added or tightened, and a field can become required, before there is a version promise. Pin to the OpenAPI document you built against. Anything labelled Preview on a route's page is saved but not yet applied by the runtime. ::: ## Base address ```text https://cloud-api.qaldy.com ``` Every path starts with `/v1`. The API is for the Qaldy console, tokens and your own code. It does not accept requests from web pages on other origins except the console's, and public OAuth and metadata routes. ## Who is calling A route is called by one of three kinds of caller. Each route's page says which. | Caller | How it proves itself | Where it works | |---|---|---| | **A signed-in person** | The console's session cookie (an HTTP-only `SameSite=Lax` cookie), plus an `Origin` header that is the console's own on anything that changes state | Every route | | **A personal access token or an MCP sign-in** | `Authorization: Bearer qat_…` (personal) or `qao_…` (MCP sign-in) | Only the routes marked with a token scope. Everything else answers `403 console_only` | | **A project key** | `Authorization: Bearer qk_pub_…` or `qk_sec_…` | Only the [backend data API](https://docs.qaldy.com/reference/api/backend-data-api/) of one backend project | A token works in the one workspace it was made in and never exceeds what its person may do there. A scope is **read** or **write**. A route a token cannot call is refused until it is deliberately listed; the list is generated from the code and shown on every route. See [Tokens, CI and scripts](https://docs.qaldy.com/mcp/tokens/) to make one. ## Authorization is per route Signing in is not enough: every route checks the caller's rights on the thing it touches, and answers 404 (not 403) for something the caller cannot see, so an id does not confirm that something exists. The **Who** line on a route says which right it needs: workspace member, builder, admin or owner, or `can_view`, `can_run`, `can_edit`, `can_share` on an app. Mutations write an [audit row](https://docs.qaldy.com/concepts/audit/) in the same transaction. ## Errors Every error has the same shape: ```json { "error": { "code": "app_not_found", "message": "We could not find that app.", "hint": "Check the link and try again.", "requestId": "req_5f0c…" } } ``` `code` is stable and meant for programs. `message` says what happened and why; `hint` says what to do next. `requestId` is also the `x-request-id` response header: quote it when you ask us about a failure. Common codes: | Status | Code | Meaning | |---|---|---| | 400 | `invalid_request` | The body or query did not match what the route expects; the message names the field | | 401 | `unauthorized` | Not signed in, or the token is missing, expired or revoked | | 403 | `forbidden`, `console_only`, `admin_only`, `admin_approval_required`, `approval_needs_console`, `insufficient_scope` | Signed in, but not allowed. The code says why | | 404 | `app_not_found`, `workspace_not_found`, `connection_not_found` and others | Not found, or not visible to you | | 409 | `stale_version`, `manifest_changed`, `last_owner` and others | The request is fine but the state moved | | 413 | `too_large` | The body is over the limit | | 429 | `rate_limited` | Too many requests; `Retry-After` says how long | ## Limits - **Bodies are limited to 1 MiB**, except site uploads (a zip of up to 50 MB, see [Sites](https://docs.qaldy.com/reference/api/sites/)). - **Requests are rate-limited** per signed-in person, or per address when not signed in, with heavier routes costing more. A refusal is `429` with a `Retry-After` header in seconds. Some routes have their own tighter limits, stated on their page (invite emails, test sends, login attempts). - **Lists** page with a `cursor`: the response has the next one, and `limit` is capped per route. - **Live data** comes over Server-Sent Events: `GET /v1/workspaces/{ws}/events` for runs, builds and refusals as they happen (`qaldy logs --follow` uses it), and an app's log stream. They are not in the OpenAPI document; the console and the CLI show how to read them. ## Try a call With a read token: ```sh curl -s https://cloud-api.qaldy.com/v1/tokens/self -H "Authorization: Bearer $QALDY_TOKEN" curl -s "https://cloud-api.qaldy.com/v1/workspaces/$WS/runs?limit=5" -H "Authorization: Bearer $QALDY_TOKEN" ``` The first answers who the token acts as: the workspace (with the id to use in the second call), the person, the role and the scopes. ## The routes :::cards - [All routes](https://docs.qaldy.com/reference/api/routes/) One table of every group, with how many routes each has. - [Apps](https://docs.qaldy.com/reference/api/apps/) Read, run, pause and move an app. - [Runs](https://docs.qaldy.com/reference/api/runs/) The list, one run, its explanation and its gate trace. - [Backend data API](https://docs.qaldy.com/reference/api/backend-data-api/) The REST API your own apps call with a project key. ::: The app manifest, which `build` and `versions` carry, is described on [App manifest](https://docs.qaldy.com/reference/manifest/).