Webapps & Websites¶
Manage WordPress sites, custom web apps, domain aliases, SSL, cron, backups, SSH keys, rate limits, performance, Cloudflare integration, Varnish, DNS, and per-website ACL.
All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.
Endpoint count: 92
Endpoints¶
| Method | Path | Handler | Guards |
|---|---|---|---|
DELETE |
/api/webapps/:id |
deleteWebsite |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id |
getWebsite |
RequirePermission(db, "websites_management") |
PATCH |
/api/webapps/:id |
updateWebsite |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/access |
listWebsiteAccess |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/access |
grantWebsiteAccess |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/access/:uid |
revokeWebsiteAccess |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/acl |
listWebsiteACLRulesets |
RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl") |
DELETE |
/api/webapps/:id/acl/:ruleset_id |
unassignACLFromWebsite |
RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl") |
POST |
/api/webapps/:id/acl/assign |
assignACLToWebsite |
RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl") |
GET |
/api/webapps/:id/acl/available |
listAvailableACLRulesets |
RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl") |
DELETE |
/api/webapps/:id/aliases/:alias |
removeAlias |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/analytics |
middleware |
AuthWithDB(cfg.JWTSecret, db) |
POST |
/api/webapps/:id/backup-sets |
createWebsiteBackupSet |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/backup-sets/:set |
deleteWebsiteBackupSet |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/backups |
listWebsiteBackups |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/backups |
createWebsiteBackup |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/backups/progress |
getBackupProgress |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/backups/restore |
restoreWebsiteBackup |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/change-domain |
changeWebsiteDomain |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/cloudflare/attack-mode |
cfAttackMode |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/cloudflare/cache/mode |
cfCacheMode |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/cloudflare/cache/purge |
cfPurgeCache |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/cloudflare/connect |
cfDisconnect |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/cloudflare/connect |
cfConnect |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/cloudflare/dns |
cfGetDNS |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/cloudflare/dns |
cfCreateDNS |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/cloudflare/dns/:record_id |
cfDeleteDNS |
RequirePermission(db, "websites_administration") |
PATCH |
/api/webapps/:id/cloudflare/dns/:record_id |
cfUpdateDNS |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/cloudflare/status |
cfStatus |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/crontab |
getWebsiteCrontab |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/crontab |
addCrontabEntry |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/crontab/:index |
deleteCrontabEntry |
RequirePermission(db, "websites_administration") |
PUT |
/api/webapps/:id/crontab/:index |
updateCrontabEntry |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/cust-nginx |
getCustNginxConfig |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/cust-nginx |
saveCustNginxConfig |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/databases |
listDatabases |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/databases/mongo |
listMongoDatabases |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/databases/pg |
listPgDatabases |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/databases/sync |
syncDatabasesFromServer |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/detected-apps |
middleware |
AuthWithDB(cfg.JWTSecret, db) |
POST |
/api/webapps/:id/dns/reset |
resetDNSZone |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/duplicate |
duplicateWebsite |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/files/search |
searchWebsiteFiles |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/fix-permissions |
fixWebsitePermissions |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/install/minio |
middleware |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/install/nextcloud |
installNextcloud |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/install/starkstudio |
middleware |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/install/wordpress |
installWordPress |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/limits |
removeWebappLimits |
RequirePermission(db, "administrative_operations") |
GET |
/api/webapps/:id/limits |
getWebappLimits |
RequirePermission(db, "administrative_operations") |
POST |
/api/webapps/:id/limits |
setWebappLimits |
RequirePermission(db, "administrative_operations") |
GET |
/api/webapps/:id/limits/logs |
getWebappLimitsLogs |
RequirePermission(db, "administrative_operations") |
GET |
/api/webapps/:id/limits/stats |
getWebappLimitsStats |
RequirePermission(db, "administrative_operations") |
POST |
/api/webapps/:id/nextcloud-autologin |
middleware |
AuthWithDB(cfg.JWTSecret, db) |
GET |
/api/webapps/:id/pm2 |
middleware |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/pm2 |
middleware |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/pm2/:app/:action |
middleware |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/pm2/:app/info |
middleware |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/pm2/:app/logs |
middleware |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/pm2/dirs |
middleware |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/pm2/save |
middleware |
RequirePermission(db, "websites_management") |
GET |
/api/webapps/:id/pm2/scripts |
middleware |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/pm2/startup |
middleware |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/refresh-disk |
refreshWebsiteDiskUsage |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/remote-log |
getWebsiteRemoteLog |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/remote-log |
setWebsiteRemoteLog |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/replications |
getWebsiteReplications |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/replications |
createWebsiteReplication |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/replications/:replication_id |
deleteWebsiteReplication |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/replications/:replication_id/initial-sync |
triggerInitialSync |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/replications/:replication_id/sync |
triggerReplicationSync |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/replications/:replication_id/watch-folders |
getReplicationWatchFolders |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/replications/:replication_id/watch-folders |
addReplicationWatchFolder |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/replications/:replication_id/watch-folders/:folder_id |
deleteReplicationWatchFolder |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/replications/:replication_id/watch-folders/:folder_id/toggle |
toggleReplicationWatchFolder |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/rewrite-config |
rewriteWebsiteConfig |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/ssh-keys |
listSSHKeys |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/ssh-keys |
addSSHKey |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/ssh-private-keys |
listSSHPrivateKeys |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/ssh-private-keys |
generateSSHPrivateKey |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/:id/ssh-private-keys/:name |
deleteSSHPrivateKey |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/ssh-private-keys/:name/download |
downloadSSHPrivateKey |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/:id/ssl/issue |
websiteSSLIssueLetsEncrypt |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/ssl/status |
websiteSSLStatus |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/:id/stats |
middleware |
AuthWithDB(cfg.JWTSecret, db) |
GET |
/api/webapps/:id/terminal |
websiteTerminal |
- |
POST |
/api/webapps/:id/varnish/purge |
purgeWebsiteVarnish |
RequirePermission(db, "websites_management") |
POST |
/api/webapps/:id/wp-autologin |
middleware |
AuthWithDB(cfg.JWTSecret, db) |
POST |
/api/webapps/import-orphans |
importOrphanUsers |
RequirePermission(db, "websites_administration") |
GET |
/api/webapps/scan-orphans |
scanOrphanUsers |
RequirePermission(db, "websites_administration") |
DELETE |
/api/webapps/ssh-keys/:keyid |
deleteSSHKey |
RequirePermission(db, "websites_administration") |
POST |
/api/webapps/system-user |
createSystemWebapp |
RequirePermission(db, "websites_administration") |
Source¶
Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:
On error:
Reference¶
DELETE /api/webapps/:id¶
Deletes a website and all of its associated resources via the websites.Delete package routine.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
id is not a valid integer. |
500 |
delete_failed |
The underlying delete operation failed. |
Side effects
- Removes the website's system user, files, vhost, and DB rows (handled inside
websites.Delete).
Example
GET /api/webapps/:id¶
Returns the full record for a single website.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "website": { "id": 42, "domain": "example.com", "username": "user1", "php_ver": "8.2" } } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
id is not a valid integer. |
404 |
not_found |
Website does not exist. |
500 |
server_error |
Lookup failed. |
Example
PATCH /api/webapps/:id¶
Updates per-website settings (PHP version, HSTS, IP mode, proxy, email limit, backup skip, Varnish, port, custom docroot); only supplied fields change.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
{
"php_ver": "string (optional)",
"is_hsts": "bool (optional)",
"ip_mode": "string (optional)",
"proxy_pass": "int (optional)",
"proxy_url": "string (optional)",
"proxy_ssl_verify": "bool (optional)",
"email_limit": "int (optional)",
"skip_backup": "bool (optional)",
"varnish_enabled": "bool (optional)",
"port": "int (optional)",
"custom_docroot": "string (optional)"
}
| Field | Type | Required | Description |
|---|---|---|---|
php_ver |
string | no | PHP version; if changed, updates .bashrc and rewrites configs. |
is_hsts |
bool | no | Toggle HSTS. |
ip_mode |
string | no | One of ipv4, ipv6, both. |
proxy_pass |
int | no | Proxy-pass flag. |
proxy_url |
string | no | Upstream proxy URL. |
proxy_ssl_verify |
bool | no | Verify upstream TLS. |
email_limit |
int | no | Per-website mail send limit. |
skip_backup |
bool | no | Exclude site from backups. |
varnish_enabled |
bool | no | Toggle Varnish caching. |
port |
int | no | Backend port. |
custom_docroot |
string | no | Custom document root path. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id, bad JSON, or invalid ip_mode. |
500 |
server_error |
Update query failed. |
Side effects
- Updates the
webappsrow. - If proxy/varnish/port/docroot or PHP version changed, asynchronously runs
starkone-cli rewrite_user_confs. - On PHP version change, rewrites the user's
.bashrc.
Example
curl -X PATCH https://panel.example.com:2087/api/webapps/42 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"php_ver": "8.3", "varnish_enabled": true}'
GET /api/webapps/:id/access¶
Lists the panel users granted explicit access to a website.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "access": [ { "id": 1, "user_id": 7, "username": "user1", "permission_type": "view" } ] } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
500 |
server_error |
Query failed. |
Example
POST /api/webapps/:id/access¶
Grants (or replaces) a panel user's access level to a website.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
user_id |
int | yes | Panel user to grant access to. |
permission_type |
string | yes | One of admin, admin-limited, view. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id, bad JSON, or invalid permission_type. |
500 |
server_error |
Insert failed. |
Side effects
- Deletes any existing access row for the user/website, then inserts the new one into
user_websites_access.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/access \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_id": 7, "permission_type": "view"}'
DELETE /api/webapps/:id/access/:uid¶
Revokes a panel user's access to a website.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
uid |
integer | Panel user ID whose access is revoked. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id or uid. |
Side effects
- Deletes the matching row from
user_websites_access.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/access/7 \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/acl¶
Lists the nginx ACL rulesets currently assigned to a website (read from the user's ACL JSON).
Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": [ { "id": "rs1", "name": "Block bots", "description": "", "is_public": false, "enabled": true, "match_mode": "any", "action_type": "deny", "action_value": "", "rule_count": 3 } ] }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website/username could not be resolved. |
500 |
read_error |
Could not read the ACL JSON config. |
Example
DELETE /api/webapps/:id/acl/:ruleset_id¶
Unassigns an ACL ruleset from a website by removing it from the user's ACL config.
Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
ruleset_id |
string | ID of the ruleset to remove. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website/username could not be resolved. |
500 |
read_error / write_error |
Failed to read or write the ACL config. |
Side effects
- Rewrites the user's ACL JSON config file (file-locked).
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/acl/rs1 \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/acl/assign¶
Copies a public ACL ruleset into a website's ACL config.
Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
ruleset_id |
string | yes | ID of a public ruleset to assign. |
Response - 201 Created
If the ruleset is already present, returns 200 OK with { "data": { "message": "already assigned" } }.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
ruleset_id missing. |
404 |
not_found |
Website/username unresolved, or public ruleset not found. |
500 |
read_error / write_error |
Failed to read or write the ACL config. |
Side effects
- Appends the ruleset to the user's ACL JSON config file (file-locked).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/acl/assign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ruleset_id": "rs1"}'
GET /api/webapps/:id/acl/available¶
Lists public ACL rulesets that are not yet assigned to the website.
Guards: RequirePermission(db, "websites_administration"), RequirePlugin(db, "nginx-acl")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": [ { "id": "rs2", "name": "Country block", "description": "", "is_public": true, "match_mode": "any", "action_type": "deny", "action_value": "", "rule_count": 5 } ] }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website/username could not be resolved. |
Example
curl https://panel.example.com:2087/api/webapps/42/acl/available \
-H "Authorization: Bearer $TOKEN"
DELETE /api/webapps/:id/aliases/:alias¶
Removes a domain alias from a website via starkone-cli alias_remove.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
alias |
string | The alias domain to remove. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id or empty alias. |
404 |
not_found |
Website not found. |
500 |
cli_error |
The CLI alias-remove command failed. |
Side effects
- Runs
/usr/sbin/starkone-cli alias_remove <domain> <alias>(rewrites webserver config). - Deletes the row from
domain_aliases.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/aliases/www.example.com \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/analytics¶
Returns an HTML GoAccess analytics report for a single website's access log.
Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Returns text/html (a rendered GoAccess report), not the JSON envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
404 |
not_found |
Website not found. |
404 |
no_logs |
No access log found for the website. |
500 |
goaccess_error |
GoAccess failed to generate the report. |
Side effects
- Runs
goaccessagainst the site's access log (read-only).
Example
POST /api/webapps/:id/backup-sets¶
Creates a named backup set directory for a website.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Set name; lowercased and stripped to [a-z0-9-], max 32 chars. |
Response - 201 Created
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Missing/invalid set name. |
404 |
not_found |
Website not found. |
500 |
server_error |
Directory creation failed. |
Side effects
- Creates
/mnt/starkone-backups/<user>/<domain>/<name>(mode 0750, owned root:root).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/backup-sets \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "weekly"}'
DELETE /api/webapps/:id/backup-sets/:set¶
Deletes a named backup set directory (cannot delete default).
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
set |
string | Backup set name; sanitized to [a-z0-9-]. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Set is default or name invalid. |
404 |
not_found |
Website or backup set not found. |
500 |
server_error |
Directory removal failed. |
Side effects
- Recursively removes
/mnt/starkone-backups/<user>/<domain>/<set>.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/backup-sets/weekly \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/backups¶
Lists backup chains for a website via starkone-cli backup_status, merged with the available backup sets.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Query parameters
| Name | Type | Optional | Description |
|---|---|---|---|
set |
string | yes | Backup set name; defaults to default. |
Response - 200 OK
When no backups exist yet, status is no_backups.
Side effects
- Runs
starkone-cli backup_status(read-only) and scans the backup directory for sets.
Example
curl "https://panel.example.com:2087/api/webapps/42/backups?set=default" \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/backups¶
Starts an asynchronous website backup (files and/or databases), blocked when the root disk is near full.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
backup_type |
string | no | FULL or INCREMENTAL (default INCREMENTAL). |
set |
string | no | Backup set name (default default). |
include_files |
bool | no | Back up files. Defaults to true if neither flag set. |
include_db |
bool | no | Back up databases. Defaults to true if neither flag set. |
Response - 202 Accepted
Errors
| Status | Code | Meaning |
|---|---|---|
503 |
disk_full |
Root disk usage is at/above the 95% threshold; backup blocked. |
Side effects
- Spawns a background
starkone-cli backup_websitejob. - On disk-full, sends a critical notification to root.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/backups \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"backup_type": "FULL", "include_files": true, "include_db": true}'
GET /api/webapps/:id/backups/progress¶
Returns the progress of a running backup job, or the last progress log if none is running.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
When idle: { "running": false } (optionally with output from the last log).
Example
curl https://panel.example.com:2087/api/webapps/42/backups/progress \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/backups/restore¶
Starts an asynchronous restore of a website backup, optionally into a sandbox subdomain.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
{
"time_mark": "1h",
"restore_path": "",
"restore_files": true,
"restore_database": true,
"delete_before_restore": false,
"sandbox_restore": false,
"backup_set": "default",
"passphrase": ""
}
| Field | Type | Required | Description |
|---|---|---|---|
time_mark |
string | no | Restore point (e.g. 30M, 1h, 2024-01-01). |
restore_path |
string | no | Target path; defaults to /home/<user>. |
restore_files |
bool | no | Restore files. |
restore_database |
bool | no | Restore databases. |
delete_before_restore |
bool | no | Wipe files before restoring. |
sandbox_restore |
bool | no | Restore into a new rp-<label>.<domain> sandbox site. |
backup_set |
string | no | Backup set name (default default). |
passphrase |
string | no | Manual passphrase for encrypted backups. |
Response - 202 Accepted
{ "data": { "status": "started", "message": "Restore started in background", "restore_path": "/home/user1", "time_mark": "1h", "restore_files": true, "restore_database": true } }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
500 |
sandbox_error |
Sandbox site creation failed. |
Side effects
- Spawns a background
starkone-cli restore_websitejob. - With
sandbox_restore, creates a new website (rp-<label>.<domain>).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/backups/restore \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"time_mark": "1h", "restore_files": true, "restore_database": true}'
POST /api/webapps/:id/change-domain¶
Renames a website's primary domain via starkone-cli change_domain.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
new_domain |
string | yes | The new primary domain. |
Response - 200 OK
If the CLI returns valid JSON, that JSON is returned under data instead.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
new_domain missing. |
500 |
change_failed |
The CLI command failed. |
Side effects
- Runs
starkone-cli change_domain(renames vhost, files, DB references). - Logs a
change_domainaction.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/change-domain \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"new_domain": "newexample.com"}'
POST /api/webapps/:id/cloudflare/attack-mode¶
Enables or disables Cloudflare "Under Attack" mode for the website's zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
bool | no | Whether to enable Under Attack mode. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id or bad JSON. |
400 |
not_connected |
Cloudflare not connected for this website. |
502 |
cf_error |
Cloudflare API call failed. |
Side effects
- Calls the Cloudflare API to set the security level.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/attack-mode \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true}'
POST /api/webapps/:id/cloudflare/cache/mode¶
Toggles Cloudflare development mode (cache bypass) for the website's zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
mode |
string | yes | development or normal. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id, bad JSON, or mode not development/normal. |
400 |
not_connected |
Cloudflare not connected. |
502 |
cf_error |
Cloudflare API call failed. |
Side effects
- Calls the Cloudflare API to set development mode.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/cache/mode \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode": "development"}'
POST /api/webapps/:id/cloudflare/cache/purge¶
Purges the entire Cloudflare cache for the website's zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
400 |
not_connected |
Cloudflare not connected. |
502 |
cf_error |
Cloudflare API call failed. |
Side effects
- Calls the Cloudflare purge-cache API.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/cache/purge \
-H "Authorization: Bearer $TOKEN"
DELETE /api/webapps/:id/cloudflare/connect¶
Removes stored Cloudflare credentials for a website.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
500 |
server_error |
Failed to clear credentials. |
Side effects
- Clears the website's stored Cloudflare credentials in the DB.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/cloudflare/connect \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/cloudflare/connect¶
Stores (encrypted) Cloudflare API credentials for a website after verifying them.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
api_token |
string | yes | Cloudflare API token. |
zone_id |
string | yes | Cloudflare zone ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id or bad JSON. |
400 |
cf_invalid |
Credentials failed verification against Cloudflare. |
500 |
server_error |
Failed to persist credentials. |
Side effects
- Verifies the token via the Cloudflare API, then stores encrypted credentials in the DB.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/connect \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"api_token": "cf_token", "zone_id": "zone123"}'
GET /api/webapps/:id/cloudflare/dns¶
Returns all DNS records for the website's Cloudflare zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "records": [ { "id": "rec1", "type": "A", "name": "example.com", "content": "203.0.113.10", "proxied": true } ] } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
400 |
not_connected |
Cloudflare not connected. |
502 |
cf_error |
Cloudflare API call failed. |
Example
curl https://panel.example.com:2087/api/webapps/42/cloudflare/dns \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/cloudflare/dns¶
Creates a DNS record in the website's Cloudflare zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | yes | Record type (A, CNAME, TXT, ...). |
name |
string | yes | Record name. |
content |
string | yes | Record content. |
proxied |
bool | no | Whether the record is proxied through Cloudflare. |
Response - 201 Created
{ "data": { "id": "rec2", "type": "A", "name": "www.example.com", "content": "203.0.113.10", "proxied": true } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id or bad JSON. |
400 |
not_connected |
Cloudflare not connected. |
502 |
cf_error |
Cloudflare API call failed. |
Side effects
- Creates the DNS record via the Cloudflare API.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/cloudflare/dns \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"type": "A", "name": "www.example.com", "content": "203.0.113.10", "proxied": true}'
DELETE /api/webapps/:id/cloudflare/dns/:record_id¶
Deletes a DNS record from the website's Cloudflare zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
record_id |
string | Cloudflare DNS record ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
400 |
not_connected |
Cloudflare not connected. |
502 |
cf_error |
Cloudflare API call failed. |
Side effects
- Deletes the DNS record via the Cloudflare API.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/cloudflare/dns/rec2 \
-H "Authorization: Bearer $TOKEN"
PATCH /api/webapps/:id/cloudflare/dns/:record_id¶
Updates a DNS record in the website's Cloudflare zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
record_id |
string | Cloudflare DNS record ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | yes | Record type. |
name |
string | yes | Record name. |
content |
string | yes | Record content. |
proxied |
bool | no | Whether the record is proxied. |
Response - 200 OK
{ "data": { "id": "rec2", "type": "A", "name": "www.example.com", "content": "203.0.113.20", "proxied": false } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id or bad JSON. |
400 |
not_connected |
Cloudflare not connected. |
502 |
cf_error |
Cloudflare API call failed. |
Side effects
- Updates the DNS record via the Cloudflare API.
Example
curl -X PATCH https://panel.example.com:2087/api/webapps/42/cloudflare/dns/rec2 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"type": "A", "name": "www.example.com", "content": "203.0.113.20", "proxied": false}'
GET /api/webapps/:id/cloudflare/status¶
Returns whether Cloudflare is connected for the website, with zone info; attempts auto-connect using a global token if available.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
When not connected: { "data": { "connected": false } }.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
500 |
server_error |
Failed to load the Cloudflare client. |
502 |
cf_error |
Cloudflare zone lookup failed. |
Side effects
- May auto-connect the website to a Cloudflare zone (writes credentials) when a global token matches the domain.
Example
curl https://panel.example.com:2087/api/webapps/42/cloudflare/status \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/crontab¶
Returns the parsed crontab entries for the website's system user.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Runs
crontab -l -u <username>(read-only).
Example
POST /api/webapps/:id/crontab¶
Appends a crontab entry for the website's system user.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
schedule |
string | no | Cron schedule expression. |
command |
string | no | Command to run. |
Response - 201 Created
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON body. |
404 |
(message only) | Website not found. |
500 |
(message only) | Failed to update crontab. |
Side effects
- Reinstalls the user's crontab with the new entry appended.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/crontab \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"schedule": "0 3 * * *", "command": "/usr/bin/php cron.php"}'
DELETE /api/webapps/:id/crontab/:index¶
Removes the crontab entry at the given zero-based index for the website's user.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
index |
integer | Zero-based index of the entry among non-comment lines. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Reinstalls the user's crontab without the removed entry.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/crontab/0 \
-H "Authorization: Bearer $TOKEN"
PUT /api/webapps/:id/crontab/:index¶
Replaces the crontab entry at the given index for the website's user.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
index |
integer | Zero-based index of the entry to replace. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
schedule |
string | no | New cron schedule. |
command |
string | no | New command. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON body. |
404 |
(message only) | Website not found. |
500 |
(message only) | Failed to update crontab. |
Side effects
- Reinstalls the user's crontab with the entry replaced.
Example
curl -X PUT https://panel.example.com:2087/api/webapps/42/crontab/0 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"schedule": "*/5 * * * *", "command": "/usr/bin/php cron.php"}'
GET /api/webapps/:id/cust-nginx¶
Returns the website's custom nginx config snippet (empty if none exists).
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
404 |
not_found |
Website not found. |
Side effects
- Reads
/etc/starkone/users_data/<user>/cust_nginx.conf(read-only).
Example
POST /api/webapps/:id/cust-nginx¶
Saves a custom nginx config snippet for the website, validates the full nginx config, and reloads if valid.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
content |
string | no | The custom nginx config snippet. |
Response - 200 OK
If nginx -t fails the file is still saved but not applied:
{ "success": false, "nginx_error": "...", "message": "Nginx configuration test failed. File was saved but NOT applied." }
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id or bad JSON. |
404 |
not_found |
Website not found. |
500 |
write_error |
Failed to create directory or write the config. |
Side effects
- Writes
/etc/starkone/users_data/<user>/cust_nginx.conf. - Runs
starkone-cli rewrite_user_confs, tests withnginx -t, and on success runssystemctl reload nginx.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/cust-nginx \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"content": "location /api { proxy_pass http://127.0.0.1:3000; }"}'
GET /api/webapps/:id/databases¶
Lists the MySQL databases tracked in the panel for a website.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
500 |
server_error |
Lookup failed. |
Example
GET /api/webapps/:id/databases/mongo¶
Lists the MongoDB databases tracked for a website; returns an empty list when the Mongo plugin is disabled.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
When the plugin is disabled: { "data": { "databases": [], "plugin_enabled": false } }.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
500 |
server_error |
Lookup failed. |
Example
curl https://panel.example.com:2087/api/webapps/42/databases/mongo \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/databases/pg¶
Lists the PostgreSQL databases tracked for a website; returns an empty list when the PG plugin is disabled.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
When the plugin is disabled: { "data": { "databases": [], "plugin_enabled": false } }.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
500 |
server_error |
Lookup failed. |
Example
POST /api/webapps/:id/databases/sync¶
Reconciles the panel's database/user records with the actual MySQL server state for a website (by username_% prefix).
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
500 |
mysql_error |
Could not list databases from MySQL. |
500 |
db_error |
Panel DB query failed. |
Side effects
- Runs
mysqlqueries against the server, then inserts missing rows into thedatabasesanddatabases_userstables.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/databases/sync \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/detected-apps¶
Detects installed applications (WordPress, Nextcloud) in the website's document root and reports versions.
Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
404 |
not_found |
Website not found. |
Side effects
- Reads
wp-config.php/version.phpfrom the docroot (read-only).
Example
curl https://panel.example.com:2087/api/webapps/42/detected-apps \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/dns/reset¶
Resets the website's DNS zone to the default template via starkone-cli dns_reset_zone.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
500 |
dns_error |
The CLI reset command failed. |
Side effects
- Runs
starkone-cli dns_reset_zone <domain>(rewrites the DNS zone).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/dns/reset \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/duplicate¶
Clones a website to a new domain via starkone-cli duplicate_website, optionally on a custom port.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Source website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
new_domain |
string | yes | Domain for the clone. |
port |
int | no | Custom backend port for the clone. |
Response - 200 OK
The data value is the raw JSON returned by the CLI.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
new_domain missing. |
500 |
duplicate_failed |
The CLI command failed. |
Side effects
- Runs
starkone-cli duplicate_website. With a custom port, updates the new row, rewrites configs, and reloads nginx. - Logs a
duplicate_websiteaction.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/duplicate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"new_domain": "copy.example.com"}'
POST /api/webapps/:id/files/search¶
Searches a website's home directory for files matching extensions, up to a max depth.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
pattern |
string | no | Reserved (not used for matching in the current implementation). |
max_depth |
int | no | Max directory depth (default 3). |
extensions |
string[] | no | File extensions to include (without the dot). |
Response - 200 OK
{ "data": { "files": [ { "path": "/home/user1/public_html/index.php", "name": "index.php", "size": 1024 } ] } }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
Side effects
- Walks
/home/<user>(read-only).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/files/search \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"max_depth": 3, "extensions": ["php"]}'
POST /api/webapps/:id/fix-permissions¶
Repairs file ownership/permissions for a website via starkone-cli fix_permissions.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
500 |
server_error |
The CLI command failed. |
Side effects
- Runs
starkone-cli fix_permissions <id>(chowns/chmods website files).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/fix-permissions \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/install/minio¶
Installs and starts a per-website MinIO S3 server under PM2 with generated credentials.
Guards: RequirePermission(db, "websites_administration") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "access_key": "minioXXXXXXXX", "secret_key": "...", "api_port": 9042, "console_port": 9043, "api_url": "http://SERVER_IP:9042", "console_url": "http://SERVER_IP:9043", "message": "MinIO S3 installed successfully!" } }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only, {"error": "Website not found"}) |
Website not found. |
500 |
(message only) | MinIO download or start failed. |
Side effects
- Creates
/home/<user>/apps/minio-s3, downloads the MinIO binary, writes a PM2 ecosystem file, and starts/saves it under PM2.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/install/minio \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/install/nextcloud¶
Downloads and installs Nextcloud into the website's docroot, creating a database, admin user, nginx/PHP-FPM tweaks, and a background cron job.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "success": true, "database": "user1_nextcloud", "db_user": "user1_nextcloud", "admin_user": "admin", "admin_password": "...", "data_dir": "/home/user1/nextcloud-data", "message": "Nextcloud installed successfully!" } }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
500 |
db_error |
Database creation failed. |
500 |
download_error |
Nextcloud download failed. |
500 |
install_error |
occ maintenance:install failed. |
Side effects
- Creates a MySQL database, downloads Nextcloud, runs
occinstall, writes custom nginx + PHP-FPM configs, rewrites vhost, installs the autologin app, and adds a cron entry.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/install/nextcloud \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/install/starkstudio¶
Installs and starts StarkStudio under PM2, proxied at /starkstudio/ via custom nginx config.
Guards: RequirePermission(db, "websites_administration") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "message": "StarkStudio installed successfully!", "url": "https://example.com/starkstudio/", "admin_user": "admin", "admin_pass": "...", "port": 8542, "app_dir": "/home/user1/apps/stark-studio" }
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
500 |
(message only) | Directory creation, download, config write, or PM2 start failed. |
Side effects
- Creates
/home/<user>/apps/stark-studio, downloads the binary, writes a PM2 ecosystem file, starts/saves it under PM2, appends an nginx proxy block, and rewrites the vhost.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/install/starkstudio \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/install/wordpress¶
Downloads WordPress core into the website's docroot and creates a database and wp-config.php.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "success": true, "database": "user1_wp", "db_user": "user1_wp", "message": "WordPress downloaded. Visit your site to complete installation." } }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
500 |
db_error |
Database creation failed. |
500 |
wp_error |
wp core download or wp config create failed. |
Side effects
- Creates a MySQL database, runs
wp core download,wp config create, andwp config shuffle-saltsas the website user.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/install/wordpress \
-H "Authorization: Bearer $TOKEN"
DELETE /api/webapps/:id/limits¶
Removes resource limits (cgroup slice, PHP-FPM isolation, network bandwidth) for a website's user.
Guards: RequirePermission(db, "administrative_operations")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
500 |
(message only) | OS user lookup failed. |
Side effects
- Restores the main PHP-FPM pool, removes tc/iptables bandwidth rules, stops and deletes the user's systemd slice, and runs
systemctl daemon-reload.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/limits \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/limits¶
Returns the configured cgroup resource limits and live stats for a website's user.
Guards: RequirePermission(db, "administrative_operations")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "supported": true, "active": true, "config": { "cpuPercent": "50", "memoryValue": "512", "memoryUnit": "MB" }, "stats": { "memUsage": "120 MB", "cpuLimit": "50%" } }
When cgroups v2 is unavailable: { "supported": false }. Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
500 |
(message only) | Could not resolve the user slice. |
Side effects
- Reads the user's systemd slice file and cgroup stats (read-only).
Example
POST /api/webapps/:id/limits¶
Sets cgroup resource limits (CPU, memory, I/O, network bandwidth) for a website's user and moves its processes into the slice.
Guards: RequirePermission(db, "administrative_operations")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
{ "cpuPercent": 50, "memoryValue": 512, "memoryUnit": "MB", "ioWriteSpeed": 50, "ioReadSpeed": 50, "networkBandwidth": 100 }
| Field | Type | Required | Description |
|---|---|---|---|
cpuPercent |
int | no | CPU quota percent (1-100). |
memoryValue |
float | no | Memory limit value. |
memoryUnit |
string | no | percent, MB, or GB. |
ioWriteSpeed |
int | no | Max write bandwidth (MB/s). |
ioReadSpeed |
int | no | Max read bandwidth (MB/s). |
networkBandwidth |
int | no | Network bandwidth limit (Mbit/s); 0/nil removes it. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON or cgroups v2 unsupported. |
404 |
(message only) | Website not found. |
500 |
(message only) | User lookup or slice file write failed. |
Side effects
- Writes a
/etc/systemd/system/user-<uid>.slicefile, reloads systemd, moves the user's processes into the cgroup, optionally sets up an isolated PHP-FPM service, and configures/removes tc + iptables bandwidth rules.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/limits \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"cpuPercent": 50, "memoryValue": 512, "memoryUnit": "MB"}'
GET /api/webapps/:id/limits/logs¶
Returns systemctl status output for the user's resource-limit slice.
Guards: RequirePermission(db, "administrative_operations")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
500 |
(message only) | OS user lookup failed. |
Side effects
- Runs
systemctl status user-<uid>.slice(read-only).
Example
GET /api/webapps/:id/limits/stats¶
Returns live cgroup usage stats (memory, CPU, I/O) for the website's user.
Guards: RequirePermission(db, "administrative_operations")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "stats": { "memUsage": "120 MB", "memLimit": "512 MB", "cpuLimit": "50%", "ioWriteLimit": "Unlimited" } }
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Reads the user's cgroup stat files (read-only).
Example
POST /api/webapps/:id/nextcloud-autologin¶
Generates a one-click Nextcloud login (autologin token or admin password reset).
Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_administration"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "url": "https://example.com/apps/starkone_autologin/token/...", "method": "token", "message": "Opening Nextcloud..." } }
If the autologin app is absent, falls back to resetting the admin password and returns url, username, password.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
not_nextcloud |
Nextcloud is not installed on this site. |
404 |
not_found |
Website not found. |
500 |
reset_failed |
Password reset failed. |
Side effects
- Runs
occto create a login token or reset the admin password.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/nextcloud-autologin \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/pm2¶
Lists the PM2 applications running under the website's system user.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": [ { "pm_id": 0, "name": "app", "status": "online", "cpu": 0.5, "memory": 52428800, "port": "3000" } ], "pm2_installed": true }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Runs
pm2 jlistas the website user (read-only).
Example
POST /api/webapps/:id/pm2¶
Creates and starts a new PM2 application for the website user.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
{ "name": "app", "script": "server.js", "interpreter": "node", "cwd": "/home/user1/app", "args": "", "instances": 1, "watch": false, "max_memory": "500M", "env": { "NODE_ENV": "production" }, "port": 3000 }
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | PM2 app name. |
script |
string | yes | Entry script (resolved within the user's home). |
interpreter |
string | no | Interpreter (default node; none for binaries). |
cwd |
string | no | Working directory; must be within the user's home. |
args |
string | no | Script arguments. |
instances |
int | no | Instance count (default 1). |
watch |
bool | no | Enable watch mode. |
max_memory |
string | no | Memory restart threshold (e.g. 500M). |
env |
object | no | Environment variables. |
port |
int | no | Sets PORT env var. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON, or cwd outside the user's home. |
404 |
(message only) | Website not found. |
500 |
(message only) | Failed to write ecosystem file or start the app. |
Side effects
- Writes a PM2 ecosystem
.jsfile in the user's home, runspm2 startandpm2 saveas the user.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/pm2 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "app", "script": "server.js", "port": 3000}'
POST /api/webapps/:id/pm2/:app/:action¶
Performs a PM2 lifecycle action (start/stop/restart/reload/delete) on an app.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
app |
string | PM2 app name or ID. |
action |
string | One of start, stop, restart, reload, delete. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Invalid action. |
404 |
(message only) | Website not found. |
500 |
(message only) | PM2 command failed. |
Side effects
- Runs
pm2 <action> <app>as the website user.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/pm2/app/restart \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/pm2/:app/info¶
Returns detailed PM2 info for a specific app (raw pm2 show --format json).
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
app |
string | PM2 app name or ID. |
Response - 200 OK
Returns the raw JSON output of pm2 show <app> --format json (not the data envelope).
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found, or app not found. |
Side effects
- Runs
pm2 showas the website user (read-only).
Example
GET /api/webapps/:id/pm2/:app/logs¶
Returns the last N lines of a PM2 app's combined logs.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
app |
string | PM2 app name or ID. |
Query parameters
| Name | Type | Optional | Description |
|---|---|---|---|
lines |
integer | yes | Number of log lines (default 100). |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Runs
pm2 logs <app> --nostreamas the website user (read-only).
Example
curl "https://panel.example.com:2087/api/webapps/42/pm2/app/logs?lines=200" \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/pm2/dirs¶
Lists directories (up to 5 levels deep) under the website user's home for app selection.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Walks
/home/<user>(read-only), skipping hidden/node_modules/vendor/cachedirs.
Example
POST /api/webapps/:id/pm2/save¶
Saves the current PM2 process list for the website user.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
500 |
(message only) | pm2 save failed. |
Side effects
- Runs
pm2 saveas the website user.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/pm2/save \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/pm2/scripts¶
Lists candidate script files in a directory for PM2 app selection.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Query parameters
| Name | Type | Optional | Description |
|---|---|---|---|
dir |
string | no | Directory to scan; must be within the user's home. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | dir outside the user's home. |
404 |
(message only) | Website not found. |
Side effects
- Reads the given directory (read-only).
Example
curl "https://panel.example.com:2087/api/webapps/42/pm2/scripts?dir=/home/user1/app" \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/pm2/startup¶
Enables PM2 startup-on-boot for the website user and saves current state.
Guards: RequirePermission(db, "websites_management") (route also applies RequirePlugin(db, "pm2"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Website not found. |
Side effects
- Runs
pm2 startupas the user, executes the resulting sudo command as root, and runspm2 save.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/pm2/startup \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/refresh-disk¶
Recomputes and stores the website's disk usage.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
500 |
server_error |
Disk usage computation failed. |
Side effects
- Recomputes disk usage and updates the
webappsrow.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/refresh-disk \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/remote-log¶
Returns whether per-website remote (syslog) logging is enabled.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
404 |
not_found |
Website not found. |
Side effects
- Checks for
/etc/starkone/users_data/<user>/remote_log.conf(read-only).
Example
POST /api/webapps/:id/remote-log¶
Enables or disables remote syslog logging for the website's nginx access/error logs.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
bool | no | Enable or disable remote logging. |
server |
string | no | Syslog server; falls back to global setting. |
port |
int | no | Syslog port; falls back to global setting or 514. |
protocol |
string | no | udp or tcp; defaults to udp. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id, bad JSON, or no server configured. |
404 |
not_found |
Website not found. |
500 |
server_error |
Failed to write the config file. |
Side effects
- Writes or removes
/etc/starkone/users_data/<user>/remote_log.confand runsstarkone-cli rewrite_user_confs.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/remote-log \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true, "server": "203.0.113.50", "port": 514, "protocol": "udp"}'
GET /api/webapps/:id/replications¶
Lists the configured replications for a website, with target server details and watch-folder counts.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": [ { "id": 1, "server_id": 2, "server_name": "dr-site", "server_host": "203.0.113.60", "remote_username": "user1", "status": "active", "last_sync": "2026-06-17 02:00:00", "sync_interval": 3600, "watch_count": 2 } ] }
Example
POST /api/webapps/:id/replications¶
Sets up replication of a website to a remote StarkOne server (creates the remote site, exchanges SSH keys, schedules rsync).
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
server_id |
int | yes | Target replication server ID. |
force |
bool | no | Proceed even if the user already exists remotely. |
Response - 201 Created
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON, cannot connect to remote, or remote create failed. |
404 |
(message only) | Website or server not found. |
409 |
user_exists |
User already exists remotely and force is false. |
500 |
(message only) | SSH key read or DB insert failed. |
Side effects
- Calls the remote server's API to create the site, generates an SSH replication keypair, installs the public key remotely, adds an hourly rsync crontab entry, and inserts a
website_replicationsrow.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/replications \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"server_id": 2}'
DELETE /api/webapps/:id/replications/:replication_id¶
Removes a replication, deleting its rsync crontab entry and DB record.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Replication not found. |
Side effects
- Removes the matching rsync line from the user's crontab and deletes the
website_replicationsrow.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/replications/1 \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/replications/:replication_id/initial-sync¶
Runs a full initial rsync of the website to the remote replication target (creating the remote directory first).
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Replication not found. |
500 |
(message only) | rsync failed. |
Side effects
- Runs
ssh mkdir -pthenrsync -avz --deleteas the website user; updateslast_sync.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/initial-sync \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/replications/:replication_id/sync¶
Triggers an immediate rsync of the website to the remote replication target.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
(message only) | Replication not found. |
500 |
(message only) | rsync failed. |
Side effects
- Runs
rsync -avz --deleteas the website user; updateslast_sync.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/sync \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/replications/:replication_id/watch-folders¶
Lists the real-time watch folders configured for a replication.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
Response - 200 OK
Example
curl https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/replications/:replication_id/watch-folders¶
Adds a real-time watch folder to a replication and restarts the watcher.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
path |
string | yes | Folder to watch; must exist on disk. |
Response - 201 Created
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON or folder does not exist. |
500 |
(message only) | DB insert failed. |
Side effects
- Inserts a
replication_watch_foldersrow, updates the user's sync-folders file, and restarts the watcher service.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"path": "/home/user1/domains/example.com/public_html"}'
DELETE /api/webapps/:id/replications/:replication_id/watch-folders/:folder_id¶
Removes a watch folder from a replication and restarts the watcher.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
folder_id |
integer | Watch-folder ID. |
Response - 200 OK
Side effects
- Deletes the
replication_watch_foldersrow, updates the sync-folders file, and restarts the watcher.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders/3 \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/replications/:replication_id/watch-folders/:folder_id/toggle¶
Toggles the enabled state of a watch folder and restarts the watcher.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
replication_id |
integer | Replication record ID. |
folder_id |
integer | Watch-folder ID. |
Response - 200 OK
Side effects
- Flips the
enabledflag, updates the sync-folders file, and restarts the watcher.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/replications/1/watch-folders/3/toggle \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/rewrite-config¶
Rewrites a website's webserver vhost config via the CLI and reloads nginx/Apache.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
404 |
not_found |
Website not found. |
500 |
rewrite_error |
The CLI rewrite failed. |
Side effects
- Runs
starkone-cli rewrite_user_confs <domain>and reloads nginx, apache2, and httpd.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/rewrite-config \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/ssh-keys¶
Lists authorized SSH public keys registered for the website's system user.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": [ { "id": 1, "username": "user1", "key_type": "ssh-ed25519", "public_key": "ssh-ed25519 AAAA...", "fingerprint": "SHA256:...", "label": "laptop", "expires_at": "", "created_at": "2026-06-17 10:00:00" } ] }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid website id. |
404 |
not_found |
Website not found. |
500 |
db_error |
Query failed. |
Example
POST /api/webapps/:id/ssh-keys¶
Adds an SSH public key for the website's user (DB + authorized_keys).
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
public_key |
string | yes | The SSH public key. |
label |
string | no | Friendly label. |
expires_at |
string | no | Optional expiry timestamp. |
Response - 200 OK
{ "id": 1, "username": "user1", "key_type": "ssh-ed25519", "fingerprint": "SHA256:...", "label": "laptop", "expires_at": "" }
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id, missing/invalid public key. |
404 |
not_found |
Website not found. |
500 |
db_error / file_error |
DB insert or authorized_keys write failed. |
Side effects
- Inserts into
ssh_keysand appends the key to/home/<user>/.ssh/authorized_keys(creating/chmod-ing.ssh).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/ssh-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"public_key": "ssh-ed25519 AAAA...", "label": "laptop"}'
GET /api/webapps/:id/ssh-private-keys¶
Lists SSH key pairs stored in the website user's .ssh directory.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": [ { "name": "id_ed25519", "type": "ssh-ed25519", "modified": "2026-06-17 10:00", "public_key": "ssh-ed25519 AAAA..." } ] }
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
Side effects
- Reads
/home/<user>/.ssh(read-only).
Example
curl https://panel.example.com:2087/api/webapps/42/ssh-private-keys \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/ssh-private-keys¶
Generates a new SSH key pair in the website user's .ssh directory and returns the private key once.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Key file name (sanitized). |
type |
string | no | Key type (default ed25519; rsa uses 4096 bits). |
Response - 200 OK
{ "data": { "name": "deploy", "type": "ed25519", "private_key": "-----BEGIN ...", "public_key": "ssh-ed25519 AAAA...", "public_key_path": "/home/user1/.ssh/deploy.pub" } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Missing name. |
400 |
exists |
A key with that name already exists. |
404 |
not_found |
Website not found. |
500 |
ssh_dir_failed / keygen_failed |
.ssh setup or ssh-keygen failed. |
Side effects
- Runs
ssh-keygen, writing the key pair to/home/<user>/.sshand fixing ownership/mode.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/ssh-private-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "deploy", "type": "ed25519"}'
DELETE /api/webapps/:id/ssh-private-keys/:name¶
Deletes an SSH key pair (private and public) from the website user's .ssh directory.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
name |
string | Key file name (sanitized). |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
Side effects
- Removes
/home/<user>/.ssh/<name>and<name>.pub.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/42/ssh-private-keys/deploy \
-H "Authorization: Bearer $TOKEN"
GET /api/webapps/:id/ssh-private-keys/:name/download¶
Returns the contents of a private key file from the website user's .ssh directory.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
name |
string | Key file name (sanitized). |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website or key file not found. |
Side effects
- Reads the key file (read-only).
Example
curl https://panel.example.com:2087/api/webapps/42/ssh-private-keys/deploy/download \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/ssl/issue¶
Issues a Let's Encrypt certificate for the website via starkone-cli issue_letsencrypt.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Request body
{ "email": "[email protected]" }
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | yes | Contact email for the certificate. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
email missing. |
500 |
ssl_error |
The CLI issuance failed. |
Side effects
- Runs
starkone-cli issue_letsencrypt <id> <email>(obtains and installs a certificate).
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/ssl/issue \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
GET /api/webapps/:id/ssl/status¶
Returns the SSL certificate status for the website (raw CLI JSON).
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Returns the raw JSON output of starkone-cli ssl_status (not wrapped in the data envelope).
Errors
| Status | Code | Meaning |
|---|---|---|
500 |
ssl_error |
The CLI command failed. |
Side effects
- Runs
starkone-cli ssl_status <id>(read-only).
Example
GET /api/webapps/:id/stats¶
Returns usage stats for a website (emails sent, disk usage, logs size, domain aliases).
Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
{ "data": { "emails_sent": 12, "disk_usage": 10485760, "logs_size": 524288, "aliases": ["www.example.com"] } }
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
404 |
not_found |
Website not found. |
Side effects
- Runs
wc,du, andstarkone-cli alias_list(read-only).
Example
GET /api/webapps/:id/terminal¶
Opens a WebSocket PTY terminal running as the website's OS user.
Guards: none in the route table (-); the handler authenticates via a JWT token query parameter.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Query parameters
| Name | Type | Optional | Description |
|---|---|---|---|
token |
string | no | JWT used to authenticate the WebSocket upgrade. |
Response
Upgrades to a WebSocket connection (no JSON envelope). Errors before the upgrade return JSON.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id. |
401 |
unauthorized |
Missing or invalid token. |
403 |
forbidden |
Caller is not superuser and does not own the website. |
404 |
not_found |
Website not found. |
Side effects
- Spawns an interactive shell as the website's OS user; logs a
terminal_websiteaction.
Example
# WebSocket connection (illustrative)
wscat -c "wss://panel.example.com:2087/api/webapps/42/terminal?token=$TOKEN"
POST /api/webapps/:id/varnish/purge¶
Purges Varnish cache entries for the website's domain.
Guards: RequirePermission(db, "websites_management")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
Website not found. |
500 |
purge_failed |
The varnishadm ban command failed. |
Side effects
- Runs
varnishadm ban req.http.host ~ <domain>.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/varnish/purge \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/:id/wp-autologin¶
Generates a one-time WordPress admin login link by dropping a temporary login script into the docroot.
Guards: AuthWithDB(cfg.JWTSecret, db) (route also applies RequirePermission(db, "websites_management"))
Path parameters
| Name | Type | Description |
|---|---|---|
id |
integer | Numeric website ID. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid id, or WordPress not detected. |
404 |
not_found |
Website not found. |
500 |
server_error |
Failed to write the login script. |
Side effects
- Writes
starkone-autologin.phpinto the docroot (owned by the website user); the script self-deletes after a successful login.
Example
curl -X POST https://panel.example.com:2087/api/webapps/42/wp-autologin \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/import-orphans¶
Imports selected orphan system users/domains found on disk into the panel database.
Guards: RequirePermission(db, "websites_administration")
Request body
| Field | Type | Required | Description |
|---|---|---|---|
users |
array | no | Users to import. |
users[].username |
string | no | System username. |
users[].domains |
string[] | no | Domains to register for the user. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
(message only) | Bad JSON body. |
Side effects
- Inserts missing rows into the
usersandwebappstables.
Example
curl -X POST https://panel.example.com:2087/api/webapps/import-orphans \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"users": [{"username": "user1", "domains": ["example.com"]}]}'
GET /api/webapps/scan-orphans¶
Scans /home for system users/domains that are not tracked in the panel database.
Guards: RequirePermission(db, "websites_administration")
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
500 |
(message only) | DB query or /home read failed. |
Side effects
- Reads
/homeand itsdomains/subdirectories (read-only).
Example
DELETE /api/webapps/ssh-keys/:keyid¶
Deletes an SSH key by its DB ID from both the database and the user's authorized_keys.
Guards: RequirePermission(db, "websites_administration")
Path parameters
| Name | Type | Description |
|---|---|---|
keyid |
integer | The ssh_keys row ID. |
Response - 200 OK
Note: this endpoint does not use the data envelope.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid key id. |
404 |
not_found |
SSH key not found. |
500 |
file_error / db_error |
Failed to update authorized_keys or the DB. |
Side effects
- Removes the marked line from
/home/<user>/.ssh/authorized_keysand deletes thessh_keysrow.
Example
curl -X DELETE https://panel.example.com:2087/api/webapps/ssh-keys/1 \
-H "Authorization: Bearer $TOKEN"
POST /api/webapps/system-user¶
Creates a minimal hidden webapp record for an existing system user (used for SSH-only/system accounts).
Guards: RequirePermission(db, "websites_administration")
Request body
| Field | Type | Required | Description |
|---|---|---|---|
username |
string | yes | The system username. |
Response - 201 Created
If a record already exists, returns 200 OK with { "data": { "message": "already exists" } }.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
username missing. |
500 |
db_error |
Insert failed. |
Side effects
- Inserts a
webappsrow (domain<username>.system) and creates/home/<user>/.ssh.
Example