CaaS
Create an app
Section titled “Create an app”POST /v1/caas/{id}/apps · scope caas:write · reversible · idempotent · deprecated
Creates the app and configures its source, but does not deploy it: it stays idle until you call POST /v1/caas/{id}/apps/{app_id}/deploy. Separating the two is what lets you create the app, load its variables, and only then deploy — the reverse order would start the application without its configuration.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"api"}'truo caas app create svc_10432await truo.caas.apps.create("svc_10432", {"name":"api"});truo_caas({ "action": "app_create", "id": "svc_10432"})operationId: caas.apps.create
Delete an app
Section titled “Delete an app”DELETE /v1/caas/{id}/apps/{app_id} · scope caas:write · destructive — cannot be undone · idempotent · deprecated
Destructive. Deletes the app, its variables, and its domains. The service’s database data is untouched: it lives separately.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas app delete svc_10432 app_9f2c1b7eawait truo.caas.apps.delete("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "app_delete", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.apps.delete
Deploy an app
Section titled “Deploy an app”POST /v1/caas/{id}/apps/{app_id}/deploy · scope caas:deploy · reversible · asynchronous · idempotent · deprecated
Returns 202 as soon as the deployment starts. The operation resolves by looking up that deployment in the app’s history, the only place the backend reports its outcome. Wait on it with GET /v1/operations/{id}; failure details are in GET /v1/caas/{id}/apps/{app_id}/logs.
It lives in its own scope (caas:deploy) because deploying executes whatever code is in the configured source — which is different from editing the app’s configuration.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/deploy \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas deploy svc_10432 app_9f2c1b7eawait truo.caas.apps.deploy("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "deploy", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.apps.deploy
Get an app
Section titled “Get an app”GET /v1/caas/{id}/apps/{app_id} · scope caas:read · deprecated
Returns only the declared fields. The backend responds with the deployment engine’s full internal object —including plaintext environment variables—; none of that leaves through here. For variable names, use GET /v1/caas/{id}/apps/{app_id}/env.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas app get svc_10432 app_9f2c1b7eawait truo.caas.apps.get("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "app_get", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.apps.get
List the service’s apps
Section titled “List the service’s apps”GET /v1/caas/{id}/apps · scope caas:read · deprecated
source comes back null: the backend does not include it in the listing.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas app list svc_10432await truo.caas.apps.list("svc_10432");truo_caas({ "action": "app_list", "id": "svc_10432"})operationId: caas.apps.list
Get an app’s logs
Section titled “Get an app’s logs”GET /v1/caas/{id}/apps/{app_id}/logs · scope caas:read · deprecated
A snapshot, not a stream. Returns whatever the backend has at call time, and there is no way to ask for “what came after”: the backend accepts a cursor but never emits the next one, so this endpoint publishes none. To follow an application live, call again.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/logs \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas logs svc_10432 app_9f2c1b7eawait truo.caas.apps.logs("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "logs", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.apps.logs
Restart an app
Section titled “Restart an app”POST /v1/caas/{id}/apps/{app_id}/restart · scope caas:write · reversible · idempotent · deprecated
Restarts the process without rebuilding the image: it picks up the current environment variables but does not pull new code. That is what deploy is for.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/restart \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas app restart svc_10432 app_9f2c1b7eawait truo.caas.apps.restart("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "app_restart", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.apps.restart
Cancel a build
Section titled “Cancel a build”POST /v1/caas/{id}/builds/{number}/cancel · scope caas:deploy · reversible · idempotent
A queued build never starts; a running one is stopped. Refused with 400 validation_failed once the build has finished or is already deploying.
curl https://api.truo.cloud/v1/caas/svc_10432/builds/12/cancel \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas build cancel svc_10432 12await truo.caas.builds.cancel("svc_10432", "12");truo_caas({ "action": "build_cancel", "id": "svc_10432", "number": "12"})operationId: caas.builds.cancel
Get a build
Section titled “Get a build”GET /v1/caas/{id}/builds/{number} · scope caas:read
Its status, the commit it built, the resulting image by digest and, with deploy, the stack version it created.
curl https://api.truo.cloud/v1/caas/svc_10432/builds/12 \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas build get svc_10432 12await truo.caas.builds.get("svc_10432", "12");truo_caas({ "action": "build_get", "id": "svc_10432", "number": "12"})operationId: caas.builds.get
List a Truo Deploy service’s builds
Section titled “List a Truo Deploy service’s builds”GET /v1/caas/{id}/builds · scope caas:read
Newest first. Builds from every origin: the API, reruns, and pushes to connected repositories.
curl https://api.truo.cloud/v1/caas/svc_10432/builds \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas build list svc_10432await truo.caas.builds.list("svc_10432");truo_caas({ "action": "build_list", "id": "svc_10432"})operationId: caas.builds.list
Get a build’s log
Section titled “Get a build’s log”GET /v1/caas/{id}/builds/{number}/log · scope caas:read
Read it incrementally: pass the offset of each response to the next call to get only what is new, and stop when done is true.
curl https://api.truo.cloud/v1/caas/svc_10432/builds/12/log \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas build log svc_10432 12await truo.caas.builds.log("svc_10432", "12");truo_caas({ "action": "build_log", "id": "svc_10432", "number": "12"})operationId: caas.builds.log
Run a build again
Section titled “Run a build again”POST /v1/caas/{id}/builds/{number}/rerun · scope caas:deploy · reversible · idempotent
Queues a new build (a new number) with the same settings. A build of a source runs with what the source says now, and is coalesced like any build of a source.
The build runs inside your own cluster: with the Dockerfile if the repository has one, otherwise with automatic detection (Railpack). It is capped at 30 minutes. One build runs at a time per cluster and up to 5 wait in the queue; a sixth is refused with 400 validation_failed. The image is stored by digest in the cluster registry (image), so rolling a stack back brings back the same bytes.
With deploy, a successful build is deployed as a new version of that stack service; the stack must already be deployed with that service, or the request fails with 400 validation_failed before anything is queued. A build that ends succeeded with an error built the image but could not deploy it.
Returns 202 with the build; poll GET /v1/caas/{id}/builds/{number} (or its /log) until it finishes. It is not an operation.
curl https://api.truo.cloud/v1/caas/svc_10432/builds/12/rerun \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas build rerun svc_10432 12await truo.caas.builds.rerun("svc_10432", "12");truo_caas({ "action": "build_rerun", "id": "svc_10432", "number": "12"})operationId: caas.builds.rerun
Build an image from a Git repository
Section titled “Build an image from a Git repository”POST /v1/caas/{id}/builds · scope caas:deploy · reversible · idempotent
From a public repository (repo), or from a connected one (source) — private repositories go through a source. Starting from a source while a build of it is already waiting returns that build (coalesced: true) instead of queuing another.
The build runs inside your own cluster: with the Dockerfile if the repository has one, otherwise with automatic detection (Railpack). It is capped at 30 minutes. One build runs at a time per cluster and up to 5 wait in the queue; a sixth is refused with 400 validation_failed. The image is stored by digest in the cluster registry (image), so rolling a stack back brings back the same bytes.
With deploy, a successful build is deployed as a new version of that stack service; the stack must already be deployed with that service, or the request fails with 400 validation_failed before anything is queued. A build that ends succeeded with an error built the image but could not deploy it.
Returns 202 with the build; poll GET /v1/caas/{id}/builds/{number} (or its /log) until it finishes. It is not an operation.
Requires caas:deploy: with deploy, the build ends up running.
curl https://api.truo.cloud/v1/caas/svc_10432/builds \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{}'truo caas build start svc_10432await truo.caas.builds.start("svc_10432", {});truo_caas({ "action": "build_start", "id": "svc_10432"})operationId: caas.builds.start
Add a domain to an Enterprise cluster
Section titled “Add a domain to an Enterprise cluster”POST /v1/caas/{id}/domains · scope caas:write · reversible · asynchronous · idempotent
First create a CNAME from your domain to the cluster hostname (cluster.name). The request checks it and fails with 400 validation_failed if the domain does not resolve to the cluster yet — nothing is changed. Then the three nodes start routing it and the certificate is issued; the operation finishes when TLS answers on all three.
curl https://api.truo.cloud/v1/caas/svc_10432/domains \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"host":"app.example.com"}'truo caas cluster-domain add svc_10432await truo.caas.cluster_domains.create("svc_10432", {"host":"app.example.com"});truo_caas({ "action": "cluster_domain_create", "id": "svc_10432"})operationId: caas.cluster_domains.create
Remove a domain from an Enterprise cluster
Section titled “Remove a domain from an Enterprise cluster”DELETE /v1/caas/{id}/domains/{host} · scope caas:write · destructive — cannot be undone · asynchronous · idempotent
Refused with 400 validation_failed while a deployed stack routes it (remove it from the stack’s labels and deploy first), and for the cluster hostname.
curl https://api.truo.cloud/v1/caas/svc_10432/domains/app.example.com \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas cluster-domain remove svc_10432 app.example.comawait truo.caas.cluster_domains.delete("svc_10432", "app.example.com");truo_caas({ "action": "cluster_domain_delete", "id": "svc_10432", "host": "app.example.com"})operationId: caas.cluster_domains.delete
List an Enterprise cluster’s domains
Section titled “List an Enterprise cluster’s domains”GET /v1/caas/{id}/domains · scope caas:read
The cluster hostname first, then yours, each with whether its DNS points at the cluster right now and when its certificate expires. A stack serves a domain from this list by naming it in a Host() rule of its Traefik labels.
curl https://api.truo.cloud/v1/caas/svc_10432/domains \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas cluster-domain list svc_10432await truo.caas.cluster_domains.list("svc_10432");truo_caas({ "action": "cluster_domain_list", "id": "svc_10432"})operationId: caas.cluster_domains.list
Recover a single-node service from its VM backup
Section titled “Recover a single-node service from its VM backup”POST /v1/caas/{id}/recover · scope caas:write · destructive — cannot be undone · asynchronous · idempotent
Brings the node back from the latest daily backup of its VM: the machine, its private address, and the data in your stack volumes. Use it when the node is lost or its disk is damaged. Your apps are down while it runs (a few minutes), and anything written after that backup is lost. The operation result carries backup_at and backup_age_seconds. Stacks that changed after the backup are redeployed as currently declared.
Only for single-node services (capabilities.recover); on three nodes it responds 400 unsupported_for_product. Refused with 412 precondition_failed when there is no backup to restore, and with 409 operation_in_progress while another cluster operation runs.
curl https://api.truo.cloud/v1/caas/svc_10432/recover \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas cluster recover svc_10432await truo.caas.cluster.recover("svc_10432");truo_caas({ "action": "cluster_recover", "id": "svc_10432"})operationId: caas.cluster.recover
Rolling restart of an Enterprise cluster
Section titled “Rolling restart of an Enterprise cluster”POST /v1/caas/{id}/restart · scope caas:write · reversible · asynchronous · idempotent
Restarts the three nodes one at a time. Each one leaves the cluster hostname first, its replicas move to the other two, and it rejoins once it serves TLS again. Services with two or more replicas keep answering; one with a single replica is cut while its node restarts. Takes several minutes.
Refused with 412 precondition_failed if a node is already unhealthy, and with 409 operation_in_progress while another cluster operation runs. Deploys are refused until it finishes.
curl https://api.truo.cloud/v1/caas/svc_10432/restart \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas cluster restart svc_10432await truo.caas.cluster.restart("svc_10432");truo_caas({ "action": "cluster_restart", "id": "svc_10432"})operationId: caas.cluster.restart
Delete a config
Section titled “Delete a config”DELETE /v1/caas/{id}/configs/{name} · scope caas:write · destructive — cannot be undone · idempotent
Refused with 400 validation_failed while a deployed stack uses it: remove it from the compose and deploy first.
curl https://api.truo.cloud/v1/caas/svc_10432/configs/db-url \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas config delete svc_10432 db-urlawait truo.caas.configs.delete("svc_10432", "db-url");truo_caas({ "action": "config_delete", "id": "svc_10432", "name": "db-url"})operationId: caas.configs.delete
List an Enterprise service’s configs
Section titled “List an Enterprise service’s configs”GET /v1/caas/{id}/configs · scope caas:read
Names and when they were last written. Never the value, by any endpoint.
curl https://api.truo.cloud/v1/caas/svc_10432/configs \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas config list svc_10432await truo.caas.configs.list("svc_10432");truo_caas({ "action": "config_list", "id": "svc_10432"})operationId: caas.configs.list
Create or replace a config
Section titled “Create or replace a config”PUT /v1/caas/{id}/configs/{name} · scope caas:write · reversible · idempotent
A config is mounted as a file wherever the services that list it under configs: say (target:). Stored encrypted; the value is never returned.
Services that already run keep the value they started with: redeploy_needed lists the stacks to deploy again for them to pick up the new one.
curl https://api.truo.cloud/v1/caas/svc_10432/configs/db-url \ -X PUT \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"value":"<value>"}'truo caas config set svc_10432 db-urlawait truo.caas.configs.put("svc_10432", "db-url", {"value":"<value>"});truo_caas({ "action": "config_put", "id": "svc_10432", "name": "db-url"})operationId: caas.configs.put
Create a database
Section titled “Create a database”POST /v1/caas/{id}/databases · scope caas:write · reversible · idempotent · deprecated
The platform generates the password and it is not returned here or by any other /v1 endpoint: there is no way to recover it through this API. Connect from an app in the same service, where the connection string is already available.
Deleting a database is not in this version: the backend does not implement it yet, and publishing an endpoint that always fails would be publishing roadmap.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/databases \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"engine":"postgres","name":"main"}'truo caas database create svc_10432await truo.caas.databases.create("svc_10432", {"engine":"postgres","name":"main"});truo_caas({ "action": "database_create", "id": "svc_10432"})operationId: caas.databases.create
List the service’s databases
Section titled “List the service’s databases”GET /v1/caas/{id}/databases · scope caas:read · deprecated
They belong to the service, not to an app: several apps in the same service can use the same database. Credentials are not returned by any endpoint of this API.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/databases \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas database list svc_10432await truo.caas.databases.list("svc_10432");truo_caas({ "action": "database_list", "id": "svc_10432"})operationId: caas.databases.list
Rotate the CI deploy key
Section titled “Rotate the CI deploy key”POST /v1/caas/{id}/deploy-key · scope caas:deploy · reversible
Generates the key your CI uses to redeploy a stack after building an image, and returns it once. The previous key stops working immediately.
The key only redeploys stacks that already exist, with their current compose file (optionally swapping images): it cannot create stacks, read anything, or touch secrets. That is why it is separate from your API keys.
curl https://api.truo.cloud/v1/caas/svc_10432/deploy-key \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas deploy-key rotate svc_10432await truo.caas.deploy_key.rotate("svc_10432");truo_caas({ "action": "deploy_key_rotate", "id": "svc_10432"})operationId: caas.deploy_key.rotate
List an app’s deployment history
Section titled “List an app’s deployment history”GET /v1/caas/{id}/apps/{app_id}/deployments · scope caas:read · deprecated
Newest first, as the backend returns it.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/deployments \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas deployment list svc_10432 app_9f2c1b7eawait truo.caas.deployments.list("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "deployment_list", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.deployments.list
Add a domain to an app
Section titled “Add a domain to an app”POST /v1/caas/{id}/apps/{app_id}/domains · scope caas:write · reversible · idempotent · deprecated
The host’s DNS must point at the service’s IP before you call: certificate issuance is validated over HTTP.
Two more things to know:
- It is not atomic. Creation registers the domain and then rebuilds the ingress routing; if the second step fails, the call returns an error with the domain already created. Retrying is safe and is the right move — creation is idempotent per host.
- The certificate is issued afterwards, asynchronously, with no state or id to query. That is why
certificate_typecomes backnullhere. The only real check is an HTTPS request to the host.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/domains \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"host":"app.example.com"}'truo caas domain add svc_10432 app_9f2c1b7eawait truo.caas.domains.create("svc_10432", "app_9f2c1b7e", {"host":"app.example.com"});truo_caas({ "action": "domain_create", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.domains.create
Remove a domain from an app
Section titled “Remove a domain from an app”DELETE /v1/caas/{id}/apps/{app_id}/domains/{host} · scope caas:write · destructive — cannot be undone · idempotent · deprecated
Deleting a host that is not on the app is not an error: the ingress routing is rebuilt either way, which is what makes retrying safe.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/domains/app.example.com \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas domain remove svc_10432 app_9f2c1b7e app.example.comawait truo.caas.domains.delete("svc_10432", "app_9f2c1b7e", "app.example.com");truo_caas({ "action": "domain_delete", "id": "svc_10432", "appId": "app_9f2c1b7e", "host": "app.example.com"})operationId: caas.domains.delete
List an app’s domains
Section titled “List an app’s domains”GET /v1/caas/{id}/apps/{app_id}/domains · scope caas:read · deprecated
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/domains \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas domain list svc_10432 app_9f2c1b7eawait truo.caas.domains.list("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "domain_list", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.domains.list
List environment variable names
Section titled “List environment variable names”GET /v1/caas/{id}/apps/{app_id}/env · scope caas:read · deprecated
Returns names, never values. There is no version of this endpoint that returns them: once written, a value is read only by the application. The backend masks by applying a regex to the key name, which lets anything not named like a secret (DATABASE_URL, SENTRY_DSN) through in plaintext; that is not a classification policy and it is not published.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/env \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas env list svc_10432 app_9f2c1b7eawait truo.caas.env.list("svc_10432", "app_9f2c1b7e");truo_caas({ "action": "env_list", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.env.list
Replace the environment variables
Section titled “Replace the environment variables”PUT /v1/caas/{id}/apps/{app_id}/env · scope caas:write · destructive — cannot be undone · idempotent · deprecated
Replaces the entire set: anything missing from vars is deleted. Not a limitation — it is the semantics of the backend, which writes the whole block at once.
Since GET /env returns no values, the set has to come from your side — your secrets manager or your configuration repository. That is the natural shape for declarative infrastructure, and it also removes the panel’s failure mode, where saving without rewriting the secrets erased them.
Changes take effect on the next deploy or restart.
Deprecated on 2026-10-19; it stops working on 2027-10-19. See https://docs.truo.cloud/changelog/caas-discovery-apps.
curl https://api.truo.cloud/v1/caas/svc_10432/apps/app_9f2c1b7e/env \ -X PUT \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"vars":[]}'truo caas env set svc_10432 app_9f2c1b7eawait truo.caas.env.replace("svc_10432", "app_9f2c1b7e", {"vars":[]});truo_caas({ "action": "env_replace", "id": "svc_10432", "appId": "app_9f2c1b7e"})operationId: caas.env.replace
Disconnect a GitHub installation
Section titled “Disconnect a GitHub installation”DELETE /v1/caas/{id}/github/installations/{installation_id} · scope caas:deploy · reversible · idempotent
Unbinds it from this service; the app stays installed on GitHub. Builds of github sources on that account fail until it is connected again.
curl https://api.truo.cloud/v1/caas/svc_10432/github/installations/51234567 \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas github disconnect svc_10432 51234567await truo.caas.github_installations.delete("svc_10432", "51234567");truo_caas({ "action": "github_disconnect", "id": "svc_10432", "installationId": "51234567"})operationId: caas.github_installations.delete
GitHub connection of a service
Section titled “GitHub connection of a service”GET /v1/caas/{id}/github · scope caas:read
Whether the TruoCloud GitHub App is available and which of its installations (GitHub users or organizations) are connected to this service. A github source needs an installation that covers its repository.
curl https://api.truo.cloud/v1/caas/svc_10432/github \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas github status svc_10432await truo.caas.github.get("svc_10432");truo_caas({ "action": "github_get", "id": "svc_10432"})operationId: caas.github.get
Get a link to connect GitHub
Section titled “Get a link to connect GitHub”POST /v1/caas/{id}/github/install · scope caas:deploy
A link, valid for one hour, to install (or reconfigure) the TruoCloud GitHub App and connect it to this service. Open it signed in to GitHub, choose the repositories, and authorize: only installations your GitHub user can see are connected.
curl https://api.truo.cloud/v1/caas/svc_10432/github/install \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas github install-link svc_10432await truo.caas.github.install_link("svc_10432");truo_caas({ "action": "github_install_link", "id": "svc_10432"})operationId: caas.github.install_link
Get a CaaS service with its live state
Section titled “Get a CaaS service with its live state”GET /v1/caas/{id} · scope caas:read
Queries the control plane. If it does not respond, provisioning_state and machine come back null instead of failing: a control plane hiccup should not stop you from reading the rest of the resource or its capabilities.
curl https://api.truo.cloud/v1/caas/svc_10432 \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas get svc_10432await truo.caas.instances.get("svc_10432");truo_caas({ "action": "get", "id": "svc_10432"})operationId: caas.instances.get
List CaaS services
Section titled “List CaaS services”GET /v1/caas · scope caas:read
Served from the database, without querying the control plane: provisioning_state and machine come back null. Fetching them would cost two calls per page item.
A page can come back with fewer items than limit even when more exist: every control plane product shares the same provisioning module, so the family filter can only be applied after reading the page. has_more remains the correct signal for whether anything is left to fetch.
curl https://api.truo.cloud/v1/caas \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas listawait truo.caas.instances.list();truo_caas({ "action": "list"})operationId: caas.instances.list
Rebuild a preview
Section titled “Rebuild a preview”POST /v1/caas/{id}/previews/{name}/build · scope caas:deploy · reversible · idempotent
The same as a push to its branch: builds the latest commit, deploys it and pushes its expiry back.
curl https://api.truo.cloud/v1/caas/svc_10432/previews/pr-12/build \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas preview build svc_10432 pr-12await truo.caas.previews.build("svc_10432", "pr-12");truo_caas({ "action": "preview_build", "id": "svc_10432", "name": "pr-12"})operationId: caas.previews.build
Open a preview of a branch
Section titled “Open a preview of a branch”POST /v1/caas/{id}/previews · scope caas:deploy · reversible · idempotent
Builds ref from the production source and, when it succeeds, runs it in its own stack pv-<name> with the compose file of the running base_stack: one replica per service, every route to https://<name>.<cluster name>, TRUO_PREVIEW=<name> in every service (so the app can turn off real emails or jobs with side effects) and new, empty volumes. With database, it also gets its own empty PostgreSQL database, mounted under the production secret name; the app runs its migrations on start.
A push to ref rebuilds it and pushes its expiry back; after ttl_hours (24 by default) without one, it closes by itself. Up to 3 may be open per service. Requires previews enabled (POST /v1/caas/{id}/previews/enable).
Returns 202 with the preview in building; poll GET /v1/caas/{id}/previews/{name}.
curl https://api.truo.cloud/v1/caas/svc_10432/previews \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{}'truo caas preview create svc_10432await truo.caas.previews.create("svc_10432", {});truo_caas({ "action": "preview_create", "id": "svc_10432"})operationId: caas.previews.create
Close a preview
Section titled “Close a preview”DELETE /v1/caas/{id}/previews/{name} · scope caas:deploy · reversible · idempotent
Removes what the preview created —its stack, its secret, its database— and nothing else. Answers 202 with the preview in closing.
curl https://api.truo.cloud/v1/caas/svc_10432/previews/pr-12 \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas preview close svc_10432 pr-12await truo.caas.previews.delete("svc_10432", "pr-12");truo_caas({ "action": "preview_close", "id": "svc_10432", "name": "pr-12"})operationId: caas.previews.delete
Enable previews on a service
Section titled “Enable previews on a service”POST /v1/caas/{id}/previews/enable · scope caas:write · reversible · asynchronous · idempotent
Once per service: publishes the wildcard *.<cluster name>, issues its certificate and routes it on every node, then checks TLS through each one. After that, opening a preview touches neither DNS nor certificates. Running it again only checks again.
curl https://api.truo.cloud/v1/caas/svc_10432/previews/enable \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas preview enable svc_10432await truo.caas.previews.enable("svc_10432");truo_caas({ "action": "preview_enable", "id": "svc_10432"})operationId: caas.previews.enable
Get a preview
Section titled “Get a preview”GET /v1/caas/{id}/previews/{name} · scope caas:read
An open preview, with its status, address, latest build and error.
curl https://api.truo.cloud/v1/caas/svc_10432/previews/pr-12 \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas preview get svc_10432 pr-12await truo.caas.previews.get("svc_10432", "pr-12");truo_caas({ "action": "preview_get", "id": "svc_10432", "name": "pr-12"})operationId: caas.previews.get
List the previews of a service
Section titled “List the previews of a service”GET /v1/caas/{id}/previews · scope caas:read
The open ones, whether previews are enabled, and how many may be open at once.
curl https://api.truo.cloud/v1/caas/svc_10432/previews \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas preview list svc_10432await truo.caas.previews.list("svc_10432");truo_caas({ "action": "preview_list", "id": "svc_10432"})operationId: caas.previews.list
Delete private registry credentials
Section titled “Delete private registry credentials”DELETE /v1/caas/{id}/registries/{server} · scope caas:write · destructive — cannot be undone · idempotent
Services already running are not affected; the next deploy that pulls from that server fails.
curl https://api.truo.cloud/v1/caas/svc_10432/registries/ghcr.io \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas registry delete svc_10432 ghcr.ioawait truo.caas.registries.delete("svc_10432", "ghcr.io");truo_caas({ "action": "registry_delete", "id": "svc_10432", "server": "ghcr.io"})operationId: caas.registries.delete
List private registry credentials
Section titled “List private registry credentials”GET /v1/caas/{id}/registries · scope caas:read
Server and username. Never the password or token, by any endpoint.
curl https://api.truo.cloud/v1/caas/svc_10432/registries \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas registry list svc_10432await truo.caas.registries.list("svc_10432");truo_caas({ "action": "registry_list", "id": "svc_10432"})operationId: caas.registries.list
Store credentials for a private registry
Section titled “Store credentials for a private registry”PUT /v1/caas/{id}/registries/{server} · scope caas:write · reversible · idempotent
Every deploy pulls with them, for any image on that server (ghcr.io, registry.gitlab.com, docker.io…). Use a read-only token.
curl https://api.truo.cloud/v1/caas/svc_10432/registries/ghcr.io \ -X PUT \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"username":"<username>","password":"<password>"}'truo caas registry set svc_10432 ghcr.ioawait truo.caas.registries.put("svc_10432", "ghcr.io", {"username":"<username>","password":"<password>"});truo_caas({ "action": "registry_put", "id": "svc_10432", "server": "ghcr.io"})operationId: caas.registries.put
Delete a secret
Section titled “Delete a secret”DELETE /v1/caas/{id}/secrets/{name} · scope caas:write · destructive — cannot be undone · idempotent
Refused with 400 validation_failed while a deployed stack uses it: remove it from the compose and deploy first.
curl https://api.truo.cloud/v1/caas/svc_10432/secrets/db-url \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas secret delete svc_10432 db-urlawait truo.caas.secrets.delete("svc_10432", "db-url");truo_caas({ "action": "secret_delete", "id": "svc_10432", "name": "db-url"})operationId: caas.secrets.delete
List an Enterprise service’s secrets
Section titled “List an Enterprise service’s secrets”GET /v1/caas/{id}/secrets · scope caas:read
Names and when they were last written. Never the value, by any endpoint.
curl https://api.truo.cloud/v1/caas/svc_10432/secrets \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas secret list svc_10432await truo.caas.secrets.list("svc_10432");truo_caas({ "action": "secret_list", "id": "svc_10432"})operationId: caas.secrets.list
Create or replace a secret
Section titled “Create or replace a secret”PUT /v1/caas/{id}/secrets/{name} · scope caas:write · reversible · idempotent
A secret is mounted as a file at /run/secrets/<name> in the services that list it under secrets:. Stored encrypted; the value is never returned.
Services that already run keep the value they started with: redeploy_needed lists the stacks to deploy again for them to pick up the new one.
curl https://api.truo.cloud/v1/caas/svc_10432/secrets/db-url \ -X PUT \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"value":"<value>"}'truo caas secret set svc_10432 db-urlawait truo.caas.secrets.put("svc_10432", "db-url", {"value":"<value>"});truo_caas({ "action": "secret_put", "id": "svc_10432", "name": "db-url"})operationId: caas.secrets.put
Build a connected repository now
Section titled “Build a connected repository now”POST /v1/caas/{id}/sources/{name}/build · scope caas:deploy · reversible · idempotent
The latest commit of its branch, with its settings. ref builds another branch or tag and deploy sends the image elsewhere (null = build only); a different ref without deploy builds only, so a branch never lands on the source stack unless you name it. If a build of this source for the same branch and target is already waiting, that one is returned (coalesced: true).
The build runs inside your own cluster: with the Dockerfile if the repository has one, otherwise with automatic detection (Railpack). It is capped at 30 minutes. One build runs at a time per cluster and up to 5 wait in the queue; a sixth is refused with 400 validation_failed. The image is stored by digest in the cluster registry (image), so rolling a stack back brings back the same bytes.
With deploy, a successful build is deployed as a new version of that stack service; the stack must already be deployed with that service, or the request fails with 400 validation_failed before anything is queued. A build that ends succeeded with an error built the image but could not deploy it.
Returns 202 with the build; poll GET /v1/caas/{id}/builds/{number} (or its /log) until it finishes. It is not an operation.
curl https://api.truo.cloud/v1/caas/svc_10432/sources/web/build \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas source build svc_10432 webawait truo.caas.sources.build("svc_10432", "web");truo_caas({ "action": "source_build", "id": "svc_10432", "name": "web"})operationId: caas.sources.build
Connect a repository
Section titled “Connect a repository”POST /v1/caas/{id}/sources · scope caas:deploy · reversible
A source is a private repository this service builds from, by API or on every push.
github: the TruoCloud GitHub App must be installed on the repository and connected to this service (POST /v1/caas/{id}/github/install). Each build clones with a short-lived, read-only token.git: any Git server over SSH. Add the returneddeploy_keyto the repository as a read-only deploy key, and point the server’s push webhook atwebhook_urlwith thewebhook_secret(HMAC signature on GitHub, Gitea, Forgejo, and Bitbucket; secret token on GitLab). Thewebhook_secretis shown only in this response. The server’s SSH host key is pinned on the first clone.
With auto_deploy on, a push to branch builds and, with deploy, deploys. A push while a build of the source is already queued does not queue another.
curl https://api.truo.cloud/v1/caas/svc_10432/sources \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{}'truo caas source create svc_10432await truo.caas.sources.create("svc_10432", {});truo_caas({ "action": "source_create", "id": "svc_10432"})operationId: caas.sources.create
Disconnect a repository
Section titled “Disconnect a repository”DELETE /v1/caas/{id}/sources/{name} · scope caas:deploy · destructive — cannot be undone · idempotent
Its queued builds are canceled; finished builds and their images stay. A git source loses its deploy key for good: remove it from the repository too.
curl https://api.truo.cloud/v1/caas/svc_10432/sources/web \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas source delete svc_10432 webawait truo.caas.sources.delete("svc_10432", "web");truo_caas({ "action": "source_delete", "id": "svc_10432", "name": "web"})operationId: caas.sources.delete
Get a connected repository
Section titled “Get a connected repository”GET /v1/caas/{id}/sources/{name} · scope caas:read
Never the webhook secret: if it was lost, rotate it.
curl https://api.truo.cloud/v1/caas/svc_10432/sources/web \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas source get svc_10432 webawait truo.caas.sources.get("svc_10432", "web");truo_caas({ "action": "source_get", "id": "svc_10432", "name": "web"})operationId: caas.sources.get
List connected repositories
Section titled “List connected repositories”GET /v1/caas/{id}/sources · scope caas:read
The repositories this service builds from, with what each builds and where it deploys.
curl https://api.truo.cloud/v1/caas/svc_10432/sources \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas source list svc_10432await truo.caas.sources.list("svc_10432");truo_caas({ "action": "source_list", "id": "svc_10432"})operationId: caas.sources.list
Rotate a repository’s webhook secret
Section titled “Rotate a repository’s webhook secret”POST /v1/caas/{id}/sources/{name}/webhook-secret · scope caas:deploy · reversible
git sources only (GitHub sources receive pushes through the app). Returns the new secret once; the previous one stops working immediately, so update the webhook on the Git server.
curl https://api.truo.cloud/v1/caas/svc_10432/sources/web/webhook-secret \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas source rotate-secret svc_10432 webawait truo.caas.sources.rotate_secret("svc_10432", "web");truo_caas({ "action": "source_rotate_secret", "id": "svc_10432", "name": "web"})operationId: caas.sources.rotate_secret
Change a connected repository
Section titled “Change a connected repository”PATCH /v1/caas/{id}/sources/{name} · scope caas:deploy · reversible · idempotent
What it builds (branch, path, dockerfile, image), where it deploys, and auto_deploy. The repository and the provider cannot change: delete the source and connect it again. Applies from the next build.
curl https://api.truo.cloud/v1/caas/svc_10432/sources/web \ -X PATCH \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{}'truo caas source update svc_10432 webawait truo.caas.sources.update("svc_10432", "web", {});truo_caas({ "action": "source_update", "id": "svc_10432", "name": "web"})operationId: caas.sources.update
Get a stack service’s logs
Section titled “Get a stack service’s logs”GET /v1/caas/{id}/stacks/{stack}/services/{service}/logs · scope caas:read
The latest lines from every replica, interleaved and timestamped. A snapshot, not a stream: to follow a service, call again with since.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web/services/web/logs \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack logs svc_10432 web webawait truo.caas.stack_services.logs("svc_10432", "web", "web");truo_caas({ "action": "stack_logs", "id": "svc_10432", "stack": "web", "service": "web"})operationId: caas.stack_services.logs
Restart a stack service
Section titled “Restart a stack service”POST /v1/caas/{id}/stacks/{stack}/services/{service}/restart · scope caas:write · reversible · idempotent
Replaces its replicas one at a time, the same way an update does: with two or more replicas the service keeps answering. Same image and configuration — to change either, deploy.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web/services/web/restart \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack restart svc_10432 web webawait truo.caas.stack_services.restart("svc_10432", "web", "web");truo_caas({ "action": "stack_restart", "id": "svc_10432", "stack": "web", "service": "web"})operationId: caas.stack_services.restart
Change a stack service’s replicas
Section titled “Change a stack service’s replicas”POST /v1/caas/{id}/stacks/{stack}/services/{service}/scale · scope caas:write · reversible · asynchronous · idempotent
Deploys the current version with the new replica count, as a new version (origin: scale): a later deploy of your compose file sets it back to what the file says.
Returns 202 with an operation. Fast outcomes come back already finished; otherwise wait on GET /v1/operations/{id}. Only one change runs at a time per cluster: requests queue, and a request during a rolling restart fails with 409 operation_in_progress.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web/services/web/scale \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"replicas":1}'truo caas stack scale svc_10432 web webawait truo.caas.stack_services.scale("svc_10432", "web", "web", {"replicas":1});truo_caas({ "action": "stack_scale", "id": "svc_10432", "stack": "web", "service": "web"})operationId: caas.stack_services.scale
Get a version of a stack
Section titled “Get a version of a stack”GET /v1/caas/{id}/stacks/{stack}/versions/{version} · scope caas:read
With the compose file exactly as it was sent. The last 20 versions of each stack are kept.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web/versions/3 \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack version svc_10432 web 3await truo.caas.stack_versions.get("svc_10432", "web", "3");truo_caas({ "action": "stack_version_get", "id": "svc_10432", "stack": "web", "version": "3"})operationId: caas.stack_versions.get
Remove a stack
Section titled “Remove a stack”DELETE /v1/caas/{id}/stacks/{stack} · scope caas:write · destructive — cannot be undone · asynchronous · idempotent
Destructive. Stops and removes every service of the stack. Its version history stays: deploying the same name again continues the numbering. Secrets, configs, and domains are not touched.
Returns 202 with an operation. Fast outcomes come back already finished; otherwise wait on GET /v1/operations/{id}. Only one change runs at a time per cluster: requests queue, and a request during a rolling restart fails with 409 operation_in_progress.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack delete svc_10432 webawait truo.caas.stacks.delete("svc_10432", "web");truo_caas({ "action": "stack_delete", "id": "svc_10432", "stack": "web"})operationId: caas.stacks.delete
Deploy a stack
Section titled “Deploy a stack”PUT /v1/caas/{id}/stacks/{stack} · scope caas:deploy · reversible · asynchronous · idempotent
Creates the stack or deploys a new version of it from a docker-compose file. Every deploy is a numbered version you can roll back to.
The compose is validated before anything runs; a rule it breaks comes back as 400 validation_failed with the reason, and nothing is changed. If the cluster rejects the deploy, the operation fails with deploy_failed and the previous version keeps running. A service whose healthcheck fails during the update is rolled back by the cluster on its own (update.state = rollback_completed).
Requires caas:deploy: deploying runs whatever the images contain.
Returns 202 with an operation. Fast outcomes come back already finished; otherwise wait on GET /v1/operations/{id}. Only one change runs at a time per cluster: requests queue, and a request during a rolling restart fails with 409 operation_in_progress.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web \ -X PUT \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"compose":"<compose>"}'truo caas stack deploy svc_10432 webawait truo.caas.stacks.deploy("svc_10432", "web", {"compose":"<compose>"});truo_caas({ "action": "stack_deploy", "id": "svc_10432", "stack": "web"})operationId: caas.stacks.deploy
Get a stack
Section titled “Get a stack”GET /v1/caas/{id}/stacks/{stack} · scope caas:read
The version running now with its compose file, the version history, each service with its replicas, and the tasks that are not running with their error — the first place to look when a deploy does not come up.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack get svc_10432 webawait truo.caas.stacks.get("svc_10432", "web");truo_caas({ "action": "stack_get", "id": "svc_10432", "stack": "web"})operationId: caas.stacks.get
List an Enterprise service’s stacks
Section titled “List an Enterprise service’s stacks”GET /v1/caas/{id}/stacks · scope caas:read
What is declared, with what is running now. If the cluster does not answer, services comes back null and the rest is still accurate.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack list svc_10432await truo.caas.stacks.list("svc_10432");truo_caas({ "action": "stack_list", "id": "svc_10432"})operationId: caas.stacks.list
Roll a stack back
Section titled “Roll a stack back”POST /v1/caas/{id}/stacks/{stack}/rollback · scope caas:deploy · reversible · asynchronous · idempotent
Deploys the compose file of an earlier version as a new version (origin: rollback), so the history only grows and rolling back a rollback works the same way. Secrets and configs are the current ones, not the ones the old version had.
Returns 202 with an operation. Fast outcomes come back already finished; otherwise wait on GET /v1/operations/{id}. Only one change runs at a time per cluster: requests queue, and a request during a rolling restart fails with 409 operation_in_progress.
curl https://api.truo.cloud/v1/caas/svc_10432/stacks/web/rollback \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo caas stack rollback svc_10432 webawait truo.caas.stacks.rollback("svc_10432", "web");truo_caas({ "action": "stack_rollback", "id": "svc_10432", "stack": "web"})operationId: caas.stacks.rollback