The panel is driven entirely by this HTTP API — the web UI is just a client of it. It listens on
127.0.0.1:5252 and is reached through nginx.
POST /api/login with an email and password sets a session cookie. Send that cookie with every
subsequent request.
curl -sk -c jar -b jar https://your-panel/api/login \
-H ' Content-Type: application/json ' \
curl -sk -c jar -b jar https://your-panel/api/sites
The cookie is HttpOnly, Secure and SameSite=Strict, and the token is an opaque random value
stored in the database, so sessions can be revoked. Sessions last 24 hours.
If the account has 2FA, the first response is 401 with {"totp_required": true}; repeat the
request including "totp":"123456". Each code works once.
Every endpoint below is marked with the role it needs.
Marker Requires open no authentication auth any signed-in account manage admin or operator (operators only for their own sites)global admin or readonly — not operator, as these are server-wideadmin admin only
An operator sees and touches only assigned sites; requests for others return 404, not 403, so
the API does not confirm that a site exists.
JSON in, JSON out.
Long operations return 202 with a task; poll the resource or GET /api/tasks/{id}.
Errors are {"error":"..."} with a meaningful status code.
Request bodies are capped at 4 MB and unknown fields are rejected.
Method Path Role Purpose GET/api/bootstrapopen whether initial setup is complete POST/api/setupopen consume the one-time token, create the first admin POST/api/loginopen sign in POST/api/logoutauth sign out and delete the session GET/api/meauth the current account GET/healthzopen liveness
Method Path Role Purpose POST/api/account/passwordauth change your own password POST/api/account/2fa/beginauth start TOTP enrolment; returns the secret POST/api/account/2fa/confirmauth confirm with a code and switch 2FA on POST/api/account/2fa/disableauth disable 2FA (requires your password) GET/api/account/sessionsauth your active sessions DELETE/api/account/sessions/{id}auth revoke one
Method Path Role Purpose GET/api/usersglobal list accounts POST/api/usersadmin create one — role is admin, operator or readonly; an operator needs site_ids DELETE/api/users/{id}admin delete an account
Method Path Role Purpose GET/api/sitesauth list (scoped to the caller) POST/api/sitesmanage create — returns 202 and a task GET/api/sites/{id}auth one site DELETE/api/sites/{id}manage delete — returns 202 PUT/api/sites/{id}/configmanage update the site configuration PUT/api/sites/{id}/phpmanage change PHP version or limits POST/api/sites/{id}/certificatemanage issue a Let’s Encrypt certificate POST/api/sites/{id}/purgemanage purge the page cache POST/api/sites/{id}/warmmanage crawl the sitemap to warm the cache GET/api/sites/{id}/cache-statsauth object-cache hit rate and memory POST/api/sites/{id}/migrationmanage import an existing site from an archive + SQL dump
POST /api/sites/{id}/migration takes {"archive": "...", "sql": "...", "old_domain": "...", "confirm": "<destination domain>"}. archive and sql are paths inside the destination site’s
directory — upload them over SFTP first. old_domain drives the search-and-replace through the
imported database, and confirm must equal the destination domain or the request is rejected.
Creating a site:
curl -sk -c jar -b jar https://your-panel/api/sites \
-H ' Content-Type: application/json ' \
-d ' {"domain":"shop.example.com","type":"woocommerce","profile":"commerce",
"admin_password":"strong-password","object_cache":true} '
Method Path Role Purpose GET/api/sites/{id}/deploymentsauth deployment history with Guard verdicts POST/api/sites/{id}/deploymentsmanage create a release POST/api/deployments/{id}/promotemanage promote a specific release POST/api/sites/{id}/rollbackmanage roll back to the previous release POST/api/sites/{id}/stagingmanage clone production to staging GET/api/sites/{id}/staging/tablesmanage what a database sync would copy POST/api/sites/{id}/staging/databasemanage re-sync the staging database POST/api/sites/{id}/safe-pushmanage measure and promote if it holds up
Method Path Role Purpose GET/api/sites/{id}/backupsauth list snapshots POST/api/sites/{id}/backupsmanage take one — kind is full, files or database POST/api/backups/{id}/verifymanage restore it to scratch and check it POST/api/backups/{id}/restoremanage restore, in place or to a target directory POST/api/backups/testadmin check the repository is reachable
Jailed to the site root. Paths are resolved and then opened with openat2(RESOLVE_NO_SYMLINKS), so
a symlink cannot redirect the operation outside the site.
Method Path Role Purpose GET/api/sites/{id}/files?path=auth list a directory GET/api/sites/{id}/files/read?path=auth read a text file (2 MB cap) POST/api/sites/{id}/files/writemanage write a text file GET/api/sites/{id}/files/download?path=auth download (16 MB cap; use SFTP above that) POST/api/sites/{id}/files/uploadmanage upload (16 MB cap) POST/api/sites/{id}/files/managemanage delete, rename, mkdir
Method Path Role Purpose GET/api/sites/{id}/databaseauth tables, row counts, sizes POST/api/sites/{id}/database/querymanage run SQL POST/api/sites/{id}/database/exportmanage dump to the site’s shared/exports/ POST/api/sites/{id}/database/importmanage import a .sql from inside the site POST/api/sites/{id}/database/adminermanage launch a time-limited Adminer session
The console is read-only unless you pass "allow_writes": true , and a read-only query must be a
single statement — select 1; drop table x is refused. Queries run as a throwaway account granted on
that site’s database only, so cross-database access fails at the server.
Method Path Role Purpose POST/api/sites/{id}/wp/loginmanage one-click login URL (single-use, 5 minutes) GET/api/sites/{id}/wp/pluginsauth plugins and themes with available updates POST/api/sites/{id}/wp/updatemanage update core, plugins, themes or all POST/api/sites/{id}/wp/object-cachemanage enable or disable the object cache
Method Path Role Purpose POST/api/sites/{id}/sftpmanage enable SFTP and set the password GET/api/sites/{id}/ssh-keysauth list authorised keys POST/api/sites/{id}/ssh-keysmanage add a key DELETE/api/sites/{id}/ssh-keys/{fingerprint}manage remove one
Method Path Role Purpose GET/api/sites/{id}/cronauth this site’s jobs POST/api/sites/{id}/cronmanage create one DELETE/api/cron/{id}manage delete one POST/api/cron/{id}/runmanage run it now
Method Path Role Purpose GET/api/system/statusglobal CPU, memory, disk headroom, service state GET/api/system/metricsglobal 24 hours of capacity samples GET/api/system/driftglobal managed files that no longer match POST/api/system/drift/{id}/resolveadmin {"action":"restore"} or {"action":"accept"}GET/api/servicesglobal nginx, PHP-FPM, MariaDB, Redis state POST/api/services/{name}/restartadmin restart one GET/api/logsglobal tail a server log GET/api/firewallglobal ufw rules POST/api/firewall/ruleadmin add or remove a rule GET/api/auditglobal audit log — who did what, from where GET/api/settingsglobal panel settings PUT/api/settingsadmin update them POST/api/panel/certificateadmin install a certificate for the panel itself POST/api/panel/updateadmin self-update, health-checked with automatic rollback
POST /api/panel/update takes {"base_url": "https://…", "version": "v0.1.0"}. base_url is the
release root holding the binaries and their .sha256 files; it must be https:// and is required
in practice. See Operations .
Method Path Role Purpose GET/api/tasksauth recent tasks GET/api/tasks/{id}auth one task with its log GET/api/eventsauth server-sent events stream for live updates
Method Path Role Purpose POST/api/connector/purgesite token called by the WordPress mu-plugin
Authenticated with a per-site bearer token, not a session. A site’s token can only purge URLs whose
host matches that site’s own domains.