Skip to content

REST API & tokens

There’s no hidden side to the web UI: everything it does goes through the REST API under /api/v1/. That means you can do all of it from a script, too. Scripts, CI jobs, the command line and MCP all use it with API tokens.

Go to Settings → API tokens & MCP, type a name under New token, pick its scope, limit it to projects if you want, and click Create token. You see the value (it starts with stq_) exactly once, so copy it right away - Envoryx only keeps a hash and can’t show it again. If a token leaks, revoke it; that takes effect immediately.

You send it as a bearer token. Requests like that need neither a session cookie nor the CSRF headers a browser sends:

Terminal window
curl -H "Authorization: Bearer stq_…" https://envoryx.test/api/v1/projects
curl -H "Authorization: Bearer stq_…" -X POST https://envoryx.test/api/v1/projects/<id>/restart

A token doesn’t have to be able to do everything, and usually it shouldn’t. There are three levels, and each one includes the ones below it:

Scope Allows
read Looking: project list and details, status, logs, statistics, backup list, runtimes. No secrets (not even the values of secret variables), no changes.
operate Working with existing projects: start/stop/restart, image rollback, actions, creating backups and databases, git, domains, addons, SSH/SFTP, terminal, database browser. The default for new tokens.
admin Everything an admin may do: creating and deleting projects, settings, TLS, notifications, instance backups, restores, dropping databases.

On top of that you can limit a token to particular projects. It then only sees those, every other project answers 403, instance-wide endpoints are closed, and it can’t create projects - whatever its scope. People get the same levels as roles. That’s the right choice for a CI job that should deploy one project and nothing else.

When a token isn’t allowed to do something, the answer says why (this token has read scope, the operation needs operate). GET /api/v1/auth/me shows the name, scope and projects of the token you’re calling with. A few things no token can do: change the password, create or revoke tokens, manage users, and show or replace the secret key. For those you need a browser session, and for the last two an admin’s.

Not the full list, but the ones you’ll reach for first:

Endpoint Purpose
GET /api/v1/projects all projects
POST /api/v1/projects create a project
PATCH /api/v1/projects/{id} change runtimes and services, e.g. {"ruby": {"enabled": true, "version": "3.4", "server": true}}
POST /api/v1/projects/{id}/start · /stop · /restart lifecycle
GET /api/v1/runtimes versions, templates and presets Envoryx offers
GET /api/v1/projects/{id}/tests test suites and recent runs
GET/POST/DELETE /api/v1/projects/{id}/share a public share
PUT /api/v1/projects/{id}/proxy-rules the project’s proxy rules
GET /api/v1/audit · /audit/export the audit log
GET /api/v1/system/diagnostics the set-up check (admin)
GET /api/v1/health health, without authentication

A few things about PATCH on services save surprises. Whatever you leave out stays as it is, so {"memcached": {"exposePort": false}} only unpublishes the port and leaves the service in place. {"redis": {"enabled": false}} removes Redis but keeps its data; add "removeData": true to delete it. The same goes for RabbitMQ, Meilisearch, Typesense and OpenSearch. GET /api/v1/projects/{id}/extras lists kept data under keptData, and DELETE /api/v1/projects/{id}/extras/{kind}/data deletes it. Mailpit and Meilisearch always publish their port, so "exposePort": false gets a 422 for them. A new bucket is private unless you send "publicRead": true with storage.

Runtimes work the same way. {"go": {"enabled": true, "version": "1.27"}} changes the version and keeps the server, its mode, package and port; leave out version and the version stays too. That holds for every runtime, Node’s devServer included. Picking a new preset is the one exception: it drops the stored port (and Python’s application) unless you send them along. You can also send back a runtime object exactly as GET returned it: the read-only hostPort and debugHostPort in it are accepted and ignored. For the database type, postgres and postgresql both mean PostgreSQL, as in envoryx.yml.

Projects in the API carry their full url, proxy port included. Service credentials (object storage keys, the RabbitMQ password, the Meilisearch and Typesense keys, an external Redis password) are not part of the project responses, just like database passwords. The generated ones come from the credentials endpoints such as GET /api/v1/projects/{id}/storage/credentials, which need operate.

Actions, terminals and test runs stream their output over WebSockets (…/tests/{suite}/ws and similar). That’s a bit fiddly from a shell script, which is why the CLI wraps them for you.

Every change made through the API is written to the audit log as user (token: name), so you can always tell afterwards which script did what.