API
The control plane answers at https://api.tori.host. Customer routes require a bearer token. Mint one in the console under Settings → API tokens, or use the token the CLI saves after sign-in (TORI_TOKEN in CI).
A token is either for the whole account (tori_…) or a deploy token for one app (torid_…). A deploy token may read that app, create, read, watch and cancel its deploys, read their build logs, and roll back. Every other customer route answers it 403 with "code": "token_scope"; billing and sign-in routes answer 401. Either kind may carry an expiry, after which it answers 401 like a revoked token. Mint one with POST /api/tokens; see Deploy from CI for what it is for.
Authorization: Bearer <token>Admin, enrollment, and zone-protocol endpoints are not part of the public documentation set.
The tori.host CLI identifies itself with User-Agent: tori.host/<version>. When a CLI release is too old for the API, any /api/* request from it gets HTTP 426 with { "error": "…", "code": "CLI_TOO_OLD", "minVersion": "<version>" }. Requests with any other User-Agent are never refused for their version, so do not send tori.host/… from your own scripts.
Interactive reference
Section titled “Interactive reference”The API reference is a Scalar explorer of the public OpenAPI document generated from the server. Try-it-out calls https://api.tori.host from this origin. There is no documentation proxy; the token stays in this tab and is not written to localStorage. CORS allows https://docs.tori.host.
Pagefind indexes this overview and the representative operation page below. The Scalar panel is rendered in the browser and is not the search index.
Representative operation
Section titled “Representative operation”- GET /api/apps — list the signed-in account’s apps
Task-oriented examples
Section titled “Task-oriented examples”Replace <token> with a secret from Settings → API tokens (or the token tori login saved). Replace <slug> with the app in tori.json. Request and response schemas live in the interactive reference; these examples do not repeat them.
List apps (listApps):
curl -sS -H "Authorization: Bearer <token>" \ https://api.tori.host/api/appsEach app’s kind says how it runs: static is files served from storage, service is a container the zone runs. Read kind, not framework. framework is deprecated: it is static for every app, services included, and says nothing about how the app was built. It stays in the response only so that existing clients keep parsing it.
Show the signed-in account and plan (getAccount):
curl -sS -H "Authorization: Bearer <token>" \ https://api.tori.host/api/accountdefaultRegion is eu, and eu is the only region with a location behind it: new apps run in Prague (cz-prg-1). The Region enum still lists us because apps created before it was withdrawn may carry it. Sending us — PATCH /api/account with {"defaultRegion":"us"}, or POST /api/apps with "region":"us" — returns HTTP 422 with "code": "region_unavailable" and writes nothing. Omit region on create to get eu.
List deploys (listDeploys):
curl -sS -H "Authorization: Bearer <token>" \ https://api.tori.host/api/apps/<slug>/deploysA deploy’s progress is the latest sentence about a rollout that is still
moving, and it is present only while status is building. Once a deploy is
live, failed, cancelled or superseded, progress and progressUpdatedAt
are null — read error for why a failed deploy failed. Do not treat a
non-null progress as a description of a settled deploy.
Read logs (listLogs) — ISO timestamps, maximum 24 hours, limit 1–500. Bounds must fall inside retention (retentionDays on the response; a start older than that is HTTP 410). Generate them rather than copying a calendar day:
start=$(date -u -v-15M +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%SZ)end=$(date -u +%Y-%m-%dT%H:%M:%SZ)curl -sS -H "Authorization: Bearer <token>" \ "https://api.tori.host/api/apps/<slug>/logs?start=${start}&end=${end}&limit=200"Set a service environment variable (setEnvVar). On a service it applies on the next deploy. On a static app the API still stores the row; static deploys do not snapshot it:
curl -sS -X PUT -H "Authorization: Bearer <token>" \ -H 'Content-Type: application/json' \ -d '{"key":"API_TOKEN","value":"secret"}' \ https://api.tori.host/api/apps/<slug>/envCreating a static deploy is POST /api/apps/{slug}/deploys as multipart/form-data (createDeploy). A service deploy is the same path: JSON { "image": "registry/repo@sha256:…" } for a digest you brought, or multipart/form-data with a source tarball for a Tori build. Prefer the CLI for those: it handles packing, digest checks, progress, and waiting until live. Try-it-out for the rest is on the Scalar page.
Downloads
Section titled “Downloads”- OpenAPI document (
openapi.public.json) - Markdown sources
Agents and scripts should prefer the Markdown and OpenAPI downloads over scraping the HTML.
If you generate a client from the OpenAPI document, response shapes that recur across operations, such as Deploy and Domain, are named schemas under components.schemas. A field typed integer is always a whole number; a field typed number, such as sizeKb, is not promised to be one even when current responses only carry whole values. Fields whose meaning is not obvious from their name, such as progress on a deploy, carry a description, which most generators turn into a doc comment.
Download this page as Markdown.