Skip to content

Platform API overview

Comwit Cloud has one public HTTP API. The comwit CLI and the web console at https://cloud.comwit.io both call it, and you can call it yourself for automation, CI, or custom tooling.

The routes are product-shaped: you work with stable project, database, app, and domain resources, and the API hides the underlying database (Louhi) and runtime (brrrd) internals from you. This page explains the shape of the API so the rest of the reference makes sense.

Everything lives under one host:

https://api.cloud.comwit.io

Project-scoped routes are versioned under /v1, for example /v1/projects.

Discovery: the spec is the source of truth

Section titled “Discovery: the spec is the source of truth”

The API ships a generated OpenAPI specification, and that spec — not any prose doc — is the authoritative description of every route, field, and status code.

EndpointWhat it is
GET /openapi.jsonThe generated OpenAPI spec. It is produced from the typed route contracts, so it always matches the running service. There is no hand-written spec.
GET /docsAn interactive API reference (Scalar) rendered from that same spec — browse routes, schemas, and try requests.

You can fetch the spec with no auth:

Fetch the OpenAPI spec
curl https://api.cloud.comwit.io/openapi.json

Then open https://api.cloud.comwit.io/docs in a browser for the interactive reference.

Two unauthenticated endpoints report service health:

EndpointMeaning
GET /healthzLiveness — is the API process up and serving?
GET /readyzReadiness — is the API ready to do real work? This also checks that the control-plane dependencies (Louhi and brrrd) are reachable.
Check that the API is ready
curl https://api.cloud.comwit.io/readyz

Use /healthz for simple “is it alive” probes and /readyz when you need to know the platform can actually serve database and runtime requests.

Most routes require a bearer token. Send it in the Authorization header:

An authenticated request
curl -H "Authorization: Bearer cwt_xxx" \
https://api.cloud.comwit.io/v1/projects

User tokens always start with cwt_. See Authentication for how to get a token and what scopes it carries.

Responses wrap their payload in a named envelope rather than returning a raw array or an implementation root object. This keeps responses stable and easy to read.

A single resource is returned under its singular name:

GET a single app
{
"app": { "...": "..." }
}

The single-resource keys you’ll see are database, app, domain, and record.

A list is returned under its plural name:

List apps in a project
{
"apps": [
{ "...": "..." }
]
}

The list keys are apps, builds, domains, records, databases, and projects.

One-time tokens keep explicit, descriptive names so they’re never confused with a resource — for example database_token and query_token. These are returned once when you create them; store them right away.

The ids you see in responses are product ids — for example an app is identified by app_id, not by an internal service_id. Product behavior keys off the project id, the resource id, the resource name, and status. Lower-level infrastructure identifiers (such as DNS provider zone or change ids, or registrar operation ids) may show up in operator or debug fields, but you don’t need them for normal use.

Almost every request body is JSON, and you should send Content-Type: application/json.

The one exception is a deploy upload. When you deploy an app, the request body is the raw build artifact (application/octet-stream, a .tar.zst archive), and the deploy options — hosts, environment reference, concurrency — are passed as query parameters instead of in the body. See Deploy an app for the full flow.

Errors are returned as standard HTTP status codes with a generated problem-detail JSON body. Upstream detail is logged server-side and stripped out of public responses, so error bodies never leak secret values or internal tokens.

Idempotency behavior and the full status-code table are covered on their own page — see Errors and idempotency.