API for custom panels and reselling
Manage your services from your own system: the same features as in your customer account, with keys and permissions you set yourself.
What the API is for
The API is for anyone who wants to manage StormHost services from their own environment: resellers with their own panel for their customers, companies with internal tools, or developers who want to handle server restarts, backups or DNS with a script.
Almost everything you do in your customer account works through the API: power and restart, statistics, reinstall, firewall, rDNS, backups, files, databases, game server and Minecraft settings, Discord bots, domain DNS, plan changes, renewals and paying with credit.
A key acts on your behalf, but only within the permissions you give it. For a service someone shares with you, the permissions its owner gave you apply as well.
Quick start
- In your customer account, open Account settings, the API keys tab, and create a key. Choose a name, permissions, optionally the services and IP addresses you'll use it from, and a validity period. You confirm creation with an authenticator code or your password.
- The key is shown only once. Copy it and store it securely, for example in an environment variable on your server. We only keep its fingerprint, so we can't show it again.
- Try your first request. The /api/v1/key endpoint returns information about the key and works with any valid key:
curl https://stormhost.eu/api/v1/key \
-H "Authorization: Bearer shk_…"Authentication
- Every request sends the key in the Authorization header as Bearer shk_… (you can use the X-API-Key header instead).
- The API doesn't use cookies. It rejects requests carrying a customer account login cookie, so the key can't be abused from a browser.
- Never put the key in code that runs in a browser or a mobile app. It belongs on your server only.
- After a password change or reset and when an account is blocked, existing keys stop working. Create new ones afterwards.
Authorization: Bearer shk_ab12cd34ef56_…Permissions
Give a key only the permissions it really needs. A missing permission is rejected with 403, code insufficient_scope and the required permission in the requiredScope field.
| Permission | What it allows |
|---|---|
account:read | Account details (read only) |
services:read | Service list, status and statistics |
services:power | Start, stop, restart |
services:console | Console |
services:network | Network: rDNS, private network, addresses |
services:firewall | Firewall |
services:files | Files, worlds, plugins and mods |
services:database | Databases |
services:backups | Backups and restore |
services:settings | Service settings (games, bots, scheduler, alerts, ISO) |
services:secrets | Sensitive: reinstall, passwords, SFTP, domain transfer codeonly with an IP restriction |
domains:read | DNS records (read only) |
domains:write | DNS records and nameservers (changes) |
billing:read | Invoices and credit (read only) |
billing:write | Upgrade orders, renewals and paying with credit |
Service and IP restrictions
You can restrict a key to selected services. It then can't reach other services and the API answers 404 as if they didn't exist. A restricted key has no access to the account's invoices and credit. Without a restriction it works for all your services, including future ones.
You can restrict a key to IP addresses or ranges (e.g. 203.0.113.10 or 203.0.113.0/24, up to 20). From any other address the API answers 403 with code ip_not_allowed. The sensitive services:secrets permission requires this restriction.
You choose the validity when creating the key (30 to 365 days, or no expiry). You can revoke a key at any time and it stops working immediately.
Requests and responses
- The API base address is https://stormhost.eu/api/v1. All responses are JSON in UTF-8, except file and PDF downloads.
- Send requests with a body (POST, PUT, PATCH, DELETE) as JSON with the Content-Type: application/json header.
- Choose the message language with ?locale=cs, en, sk, pl or de, and for writes also with a locale field in the body. Without it we answer in Czech.
- The service identifier {id} is in the service list (GET /api/v1/services) or in the service address in your customer account.
- A successful response has status 200. An error has status 4xx or 5xx with an error field (human-readable text) and usually a code field (machine-readable).
{
"error": "This API key lacks the services:power scope.",
"code": "insufficient_scope",
"requiredScope": "services:power"
}Errors
Errors shared by all endpoints. Endpoint-specific errors are listed with each endpoint in the reference.
| Status | Code | Meaning |
|---|---|---|
| 400 | cookies_not_allowed | The request carries cookies. The API is used with a key only. |
| 401 | missing_key | The Authorization header with a key is missing. |
| 401 | invalid_key | The key is invalid, expired or revoked. |
| 403 | ip_not_allowed | The key can't be used from this IP address. |
| 403 | insufficient_scope | The key lacks the required permission (requiredScope field). |
| 403 | email_unverified | The account e-mail is not verified. |
| 404 | not_found | Unknown address, or a service the key has no access to. |
| 400 | — | The endpoint doesn't apply to this service type (e.g. Minecraft settings on a VPS). |
| 409 | — | The service is not active (pending setup, suspended or terminated). |
| 405 | method_not_allowed | Method not allowed; the Allow header lists the allowed ones. |
| 429 | rate_limited | Rate limit exceeded; retry after the time in the Retry-After header. |
| 503 | unavailable | Temporarily unavailable, please try again shortly. |
Limits
- One key may send at most 300 requests per minute (all endpoints together).
- Some endpoints have their own stricter limit, the same as in the customer account, for example power actions at 5 per minute.
- When a limit is exceeded the API answers 429 and the Retry-After header says how many seconds to wait. Repeated invalid keys from one IP address are blocked for a while.
- An account can have at most 20 active keys.
Code examples
The examples check the key and list services. They read the key from the STORMHOST_API_KEY environment variable. A typical reseller flow: list services, check the status and restart a service when needed.
curl
curl https://stormhost.eu/api/v1/key \
-H "Authorization: Bearer $STORMHOST_API_KEY"
curl "https://stormhost.eu/api/v1/services?status=active" \
-H "Authorization: Bearer $STORMHOST_API_KEY"
curl -X POST https://stormhost.eu/api/v1/services/SERVICE_ID/power \
-H "Authorization: Bearer $STORMHOST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"restart"}'JavaScript (Node.js)
const API = "https://stormhost.eu/api/v1";
const headers = { Authorization: `Bearer ${process.env.STORMHOST_API_KEY}` };
const res = await fetch(`${API}/services`, { headers });
if (!res.ok) throw new Error((await res.json()).error);
const { data } = await res.json();
for (const s of data) console.log(s.id, s.name, s.status);
await fetch(`${API}/services/${data[0].id}/power`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ action: "restart" }),
});PHP
<?php
$api = "https://stormhost.eu/api/v1";
$key = getenv("STORMHOST_API_KEY");
function call(string $method, string $url, string $key, ?array $body = null): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $key", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
return $out;
}
$services = call("GET", "$api/services", $key)["data"];
call("POST", "$api/services/{$services[0]['id']}/power", $key, ["action" => "restart"]);Python
import os, requests
API = "https://stormhost.eu/api/v1"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['STORMHOST_API_KEY']}"
services = s.get(f"{API}/services").json()["data"]
for svc in services:
print(svc["id"], svc["name"], svc["status"])
r = s.post(f"{API}/services/{services[0]['id']}/power", json={"action": "restart"})
r.raise_for_status()Endpoint reference
Each endpoint shows the method, address, required permission, parameters and a sample response. Values in the samples are made up.
Key and account
GET/keyAPI key details
Returns the key name, its scopes, service and IP restrictions and expiry. Needs no scope, so it's handy for checking a key.
Permission: –
Sample response
{
"name": "Můj panel",
"key": "shk_a1b2c3d4_…",
"scopes": [
"services:read",
"services:power"
],
"serviceIds": [],
"allowedIps": [
"203.0.113.10"
],
"expiresAt": null,
"createdAt": "2026-10-01T08:00:00.000Z"
}Possible errors
- 401
invalid_keyThe key is invalid, expired or revoked.
GET/meAccount details
Returns basic details of the account the key belongs to: e-mail, name, company, company ID, VAT ID and country.
Permission: account:read
Sample response
{
"id": "clx9k2m4p0000cus01abcdef",
"email": "[email protected]",
"firstName": "Jan",
"lastName": "Novák",
"companyName": null,
"ico": null,
"dic": null,
"country": "CZ",
"emailVerified": true,
"createdAt": "2026-03-12T09:30:00.000Z"
}Possible errors
- 401
invalid_keyThe key's account wasn't found.
Services
GET/overview/metricsLive service metrics
Returns the current state and usage of up to 8 services at once: power, CPU, memory and uptime for VPS and game servers, players for Minecraft, power draw for rack housing, expiry date for domains. When data can't be read, the service has state: unavailable with the reason in reason.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
ids | string | no | Comma-separated service IDs, at most 8. Without it, the first 8 services are returned. Unknown IDs are silently skipped. A key restricted to selected services may only send their IDs. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample response
{
"at": "2026-11-07T10:00:00.000Z",
"ttlMs": 2500,
"truncated": false,
"services": [
{
"serviceId": "clx9k2m4p0000svc01abcdef",
"sampledAt": "2026-11-07T09:59:58.000Z",
"kind": "vps",
"state": "ok",
"power": "running",
"cpuPercent": 12.5,
"cpuAt": "2026-11-07T09:59:00.000Z",
"memUsedMib": 1536,
"memTotalMib": 4096,
"uptimeSeconds": 864000,
"cpuSeries": [
{
"t": 1794045540000,
"v": 11.2
},
{
"t": 1794045600000,
"v": 12.5
}
]
},
{
"serviceId": "clx9k2m4p0001svc02abcdef",
"sampledAt": "2026-11-07T09:59:58.000Z",
"kind": "game",
"state": "ok",
"power": "running",
"cpuPercent": 34.1,
"memUsedMib": 2900,
"memTotalMib": 6144,
"uptimeSeconds": 7200,
"players": {
"state": "ok",
"online": 5,
"max": 20,
"at": "2026-11-07T09:55:00.000Z",
"stale": false
}
},
{
"serviceId": "clx9k2m4p0002svc03abcdef",
"sampledAt": "2026-11-07T09:59:58.000Z",
"kind": "domain",
"state": "ok",
"expiresOn": "2027-05-01"
},
{
"serviceId": "clx9k2m4p0003svc04abcdef",
"sampledAt": "2026-11-07T09:59:58.000Z",
"kind": "vps",
"state": "unavailable",
"reason": "provider"
}
]
}Possible errors
- 404
not_foundThe key is restricted to selected services and ids contains another service. - 429
rate_limitedLimit of 40 requests per minute exceeded. - 503
unavailableThe service list can't be loaded right now. Try again in a few seconds.
GET/servicesList services
Returns your services and services shared with you (role member, without price and with your permissions). A key restricted to selected services sees only those.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | string | no | Filter by status: PENDING, ACTIVE, SUSPENDED or TERMINATED (case-insensitive). |
locale | string | no | Language of service names: cs, en, sk, pl or de. Default is cs. |
Sample response
{
"data": [
{
"id": "clx9k2m4p0000svc01abcdef",
"name": "VPS Start",
"type": "VPS",
"kind": "vps",
"status": "ACTIVE",
"role": "owner",
"plan": "vps-start",
"address": "203.0.113.25",
"priceCzk": 249,
"currency": "CZK",
"expiresAt": "2026-12-01T00:00:00.000Z",
"createdAt": "2026-06-01T10:00:00.000Z"
},
{
"id": "clx9k2m4p0001svc02abcdef",
"name": "Minecraft server",
"type": "GAME_SERVER",
"kind": "game",
"status": "ACTIVE",
"role": "member",
"permissions": [
"power",
"console"
],
"plan": null,
"address": "mc.example.com",
"priceCzk": null,
"currency": "CZK",
"expiresAt": "2026-11-20T00:00:00.000Z",
"createdAt": "2026-08-15T12:00:00.000Z"
}
]
}GET/services/{id}Service details
Returns one service in the same shape as the service list. Works for suspended and terminated services too.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of the service name and error messages: cs, en, sk, pl or de. Default is cs. |
Sample response
{
"data": {
"id": "clx9k2m4p0000svc01abcdef",
"name": "VPS Start",
"type": "VPS",
"kind": "vps",
"status": "ACTIVE",
"role": "owner",
"plan": "vps-start",
"address": "203.0.113.25",
"priceCzk": 249,
"currency": "CZK",
"expiresAt": "2026-12-01T00:00:00.000Z",
"createdAt": "2026-06-01T10:00:00.000Z"
}
}Possible errors
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 400 This kind of service isn't supported by the API.
- 403
mfa_requiredThe owner of the shared service requires two-factor authentication, which isn't enabled on your account.
Services in general
POST/services/{id}/console-tokenToken for the console connection
Issues a one-time token valid for 30 s for the console websocket of a VPS (VNC or terminal) or a game server. A console opened with a token from the API runs for about a minute at most; active services only, limit 10 per minute.
Permission: services:console
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
mode | string | yes | VPS only: vnc or term. Ignored for game servers. |
Sample body
{
"mode": "vnc"
}Sample response
{
"token": "eyJhbGciOi…example",
"password": "example-rfb-ticket"
}Possible errors
- 400 For a VPS, mode is missing or invalid.
- 403 Missing Console permission on a shared service.
- 404 Service not found or it has no console.
- 409 The service isn't active or the VPS is locked (backup restore).
- 412
NOT_PROVISIONEDThe server isn't ready yet. - 429 More than 10 tokens per minute.
GET/services/{id}/crashServer crash and log analysis
Analyses the log, crash report or console output of a game server or bot and returns findings with suggested fixes. Changes nothing on the server; findings carry an id and parameters, not ready-made texts. Limit 10 analyses per minute per service.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
source | string | no | auto (default), latest, crash-report, console or file. |
name | string | no | File for source=file, e.g. logs/latest.log, crash-reports/crash-….txt or hs_err_pid….log (max 120 characters). |
list | string | no | files returns the list of files that can be analysed (at most 50). |
lang | string | no | Message language: cs, en, sk, pl or de. |
tpl | string | no | 1 = also allow fixes that send a fixed console command. |
Sample response
{
"result": {
"v": 1,
"kind": "mc",
"findings": [
{
"id": "mc.oom",
"sev": "critical",
"p": {
"heapMb": 2048
},
"evidence": [
{
"line": 412,
"text": "java.lang.OutOfMemoryError: Java heap space"
}
],
"fix": {
"type": "recalc-memory"
},
"alt": [
{
"type": "upgrade"
}
]
}
],
"rootCause": {
"cls": "java.lang.OutOfMemoryError",
"msg": "Java heap space",
"line": 412
},
"stats": {
"lines": 1830,
"truncated": false,
"ms": 42,
"timedOut": false,
"crashReport": false
}
},
"sources": [
{
"kind": "log",
"name": "logs/latest.log",
"mtime": 1762508000,
"truncated": false
}
]
}Possible errors
- 400
bad-requestInvalid parameter. Code bad-name: this file can't be analysed. - 403
forbiddenMissing Console or Files permission on a shared service. - 404
no-sourceNo log found, the server may not have run yet. - 412
not-readyThe server isn't ready yet. - 413
too-largeThe log is too large to analyse (over 16 MiB). - 429
rateRate limit exceeded. Code busy: other servers are being analysed right now. - 502
failedThe log couldn't be loaded.
GET/services/{id}/databaseDatabase server on the VPS and overview
Detects whether MariaDB/MySQL or PostgreSQL is on the VPS and, once connected, returns the version, databases and users. VPS only.
Permission: services:database
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of the reason in connectError: cs, en, sk, pl or de. |
Sample response
{
"detection": {
"mysql": {
"installed": true,
"listening": true
},
"postgres": {
"installed": false,
"listening": false
},
"os": {
"id": "debian",
"like": "",
"name": "Debian GNU/Linux 12 (bookworm)"
},
"canAutoInstall": true,
"install": {
"running": false,
"engine": null,
"exitCode": null,
"logTail": null
}
},
"config": {
"engine": "mysql",
"port": 3306,
"username": "stormhost_panel",
"auto": true
},
"overview": {
"version": "10.11.6-MariaDB",
"databases": [
{
"name": "shop",
"sizeBytes": 5242880,
"tables": 14,
"system": false
}
],
"users": [
{
"name": "shop",
"host": "localhost",
"databases": [
"shop"
],
"system": false
}
]
},
"connectError": null
}Possible errors
- 403 Missing the Databases permission.
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
POST/services/{id}/databaseInstall, connect, databases and users
Manages the database server on a VPS, chosen by the action field. Installation runs in the background (Debian/Ubuntu only); deleting a database or user cannot be undone. A generated password is returned only once.
Permission: services:database
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | install, connectAuto, connectManual, disconnect, createDatabase, dropDatabase, createUser, dropUser, setPassword or grant. |
engine | string | no | install, connectAuto, connectManual: mysql or postgres. connectAuto creates the panel account using local root access. |
port | integer | no | connectManual: database server port. |
username | string | no | connectManual: user the panel connects as. |
password | string | no | connectManual: password (required). createUser, setPassword: new password, 8–128 characters; generated if omitted. |
name | string | no | Database actions: database or user name, ASCII letters, digits and underscore, up to 63 characters, not starting with a digit. |
createUser | boolean | no | createDatabase: also create a user with the same name and full rights. |
host | string | no | createDatabase, createUser (MySQL): localhost (default) or %. dropUser: a specific host, otherwise all. |
database | string | no | createUser (optional), grant (required): database the user gets full rights to. |
Sample body
{
"action": "createDatabase",
"name": "shop",
"createUser": true,
"host": "localhost"
}Sample response
{
"ok": true,
"username": "shop",
"password": "<generated>"
}Possible errors
- 400 Invalid request, reserved name, install already running, the system does not support automatic install, or an error from the database server.
- 412 The database server is not connected yet.
- 403 Missing the Databases permission.
- 500 Internal error (the response includes a ref for support).
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
GET/services/{id}/database/browseTables and rows of a VPS database
Without table it returns the tables of a database with estimated row counts and sizes. With table it returns rows, 50 per page. VPS with a connected database server only.
Permission: services:database
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
db | string | yes | Database name. |
table | string | no | Table name; without it the table list is returned. |
schema | string | no | PostgreSQL: table schema, default public. |
page | integer | no | Page from 0, 50 rows each. |
Sample response
{
"columns": [
"id",
"name",
"created_at"
],
"rows": [
[
"1",
"Alice",
"2026-10-01 12:00:00"
],
[
"2",
"Bob",
"2026-10-03 08:30:00"
]
],
"page": 0,
"pageSize": 50,
"total": 2
}Possible errors
- 400 Invalid name or an error from the database server.
- 412 The database server is not connected yet.
- 403 Missing the Databases permission.
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
POST/services/{id}/database/queryRun an SQL query on the VPS
Runs an SQL query under the account the panel uses to connect to the VPS database server. Returns up to 500 rows, 25 s limit. The query can also change or delete data.
Permission: services:database
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
sql | string | yes | SQL query, up to 100,000 characters. |
db | string | no | Database to run the query in. |
Sample body
{
"db": "shop",
"sql": "SELECT id, name FROM customers ORDER BY id LIMIT 10"
}Sample response
{
"columns": [
"id",
"name"
],
"rows": [
[
"1",
"Alice"
],
[
"2",
"Bob"
]
],
"rowCount": 2,
"affectedRows": null,
"truncated": false,
"command": "SELECT",
"durationMs": 7
}Possible errors
- 400 Invalid request or an error from the database server.
- 412 The database server is not connected yet.
- 403 Missing the Databases permission.
- 429 At most 60 queries per minute.
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
GET/services/{id}/filesList a folder on the server
Lists a folder on a VPS (via SFTP) or on a game server or Discord bot, up to 5000 entries. For a VPS it adds access details without the password.
Permission: services:files
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
path | string | no | Absolute folder path. Default / for games and bots, the user's home folder for a VPS. |
Sample response
{
"path": "/home/app",
"entries": [
{
"name": "logs",
"type": "dir",
"size": 4096,
"mtime": 1762500000,
"mode": "drwxr-xr-x",
"owner": "app",
"group": "app"
},
{
"name": "config.yml",
"type": "file",
"size": 1832,
"mtime": 1762489123,
"mode": "-rw-r--r--",
"owner": "app",
"group": "app"
},
{
"name": "current",
"type": "link",
"size": 12,
"mtime": 1762400000,
"mode": "lrwxrwxrwx",
"owner": "app",
"group": "app",
"linkTarget": "/srv/releases/42",
"linkType": "dir"
}
],
"truncated": false,
"access": {
"host": "203.0.113.20",
"port": 22,
"username": "root",
"customCredentials": false,
"hasDefaultPassword": true,
"hostKey": "SHA256:AbCdEfGhIjKlMnOpQrStUvWxYz0123456789abcdefg"
}
}Possible errors
- 400 Invalid path or SFTP error.
- 403 Missing the Files permission.
- 404 The folder does not exist on the game server.
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
POST/services/{id}/filesCreate, rename, edit or delete files
Runs a file action on a VPS, game server or Discord bot, chosen by the action field. Deleting cannot be undone; on a VPS folders are deleted with their content. Uploads are not available via the API, use SFTP.
Permission: services:files
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | mkdir, touch, rename, delete, chmod, write; VPS and owner only: credentials, resetCredentials, trustHostKey; games and bots only: compress, decompress. |
path | string | no | mkdir, touch, rename, chmod, write, decompress: absolute path of the item. |
to | string | no | rename: new absolute path. |
paths | string[] | no | delete: 1–200 absolute paths. |
mode | string | no | chmod: octal permissions, 3–4 digits (e.g. 644). |
content | string | no | write: full new file content in UTF-8, up to 2 MB. |
username | string | no | credentials: SSH user for the file manager (instead of root). |
password | string | no | credentials: SSH user password, stored encrypted after a successful login. |
port | integer | no | credentials: SSH port 1–65535. |
fingerprint | string | no | trustHostKey: new key fingerprint in the form SHA256:…. |
dir | string | no | compress: folder containing the items to pack. |
names | string[] | no | compress: 1–200 names from the dir folder. |
archiveName | string | no | compress: name of the .tar.gz archive, up to 120 characters. |
into | string | no | decompress: here (default) or newFolder. |
folderName | string | no | decompress: name of the new folder, up to 120 characters. |
locale | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Sample body
{
"action": "write",
"path": "/server.properties",
"content": "motd=Můj server\nmax-players=20\n"
}Sample response
{
"ok": true
}Possible errors
- 400 Invalid request, path or name; system folders and /proc, /sys, /dev cannot be changed.
- 403 Only the owner can change credentials; or the Files permission is missing.
- 409 A file or folder with this name already exists.
- 409
JOB_RUNNINGA software change or modpack install is running on the Minecraft server. - 409
ARCHIVE_BUSYAnother pack or unpack is running. - 413 The write content is larger than 2 MB.
- 429 At most 10 packs and unpacks per 10 minutes.
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
GET/services/{id}/files/downloadDownload a file or read its text
Downloads a file from a VPS, game server or Discord bot. With as=text it returns the content of a UTF-8 text file as JSON, up to 1 MB.
Permission: services:files
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
path | string | yes | Absolute file path. |
as | string | no | text = return the content as JSON instead of a download. |
Sample response
{
"path": "/server.properties",
"content": "motd=Můj server\nmax-players=20\n",
"eol": "lf",
"bom": false
}Possible errors
- 400 Invalid path, not a regular file, or /proc, /sys, /dev on a VPS.
- 413
TOO_LARGEas=text: the file is larger than 1 MB. - 415
BINARYas=text: the file is not UTF-8 text. - 403 Missing the Files permission.
- 401
AUTH_FAILEDThe VPS rejected the login credentials. - 409
HOST_KEY_CHANGEDThe VPS SSH key fingerprint changed (new one in presentedHostKey); confirm it with the trustHostKey action. - 412
NO_TARGETThe VPS has no address or credentials yet (also NO_CREDENTIALS). - 502
UNREACHABLEThe VPS is unreachable (also TIMEOUT, FAILED).
GET/services/{id}/liveCurrent server state and usage
Returns the power state and current CPU, RAM, disk and network usage of a VPS or game server. For continuous monitoring use live/stream; if the state can't be read, the response is 200 with ok: false.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
poll | string | no | Value live for repeated polling (every 2 s); the answer comes from the latest live sample. |
Sample response
{
"ok": true,
"status": {
"status": "ACTIVE",
"details": {
"vmStatus": "running",
"gameState": null,
"cpuPercent": 12.4,
"memUsedMib": 1840,
"memTotalMib": 4096,
"diskUsedGb": 18.2,
"diskTotalGb": 60,
"netinMbps": 0.42,
"netoutMbps": 1.1,
"uptimeSeconds": 86400,
"diskVolumes": [
{
"mountpoint": "/",
"usedBytes": 19542261760,
"totalBytes": 64424509440
}
],
"netInBps": 420000,
"netOutBps": 1100000,
"diskReadBytesPerSec": 0,
"diskWriteBytesPerSec": 24576
}
}
}Possible errors
- 403
mfa_requiredThe service owner requires two-factor authentication for members. - 404 Service not found.
- 429 More than 240 requests per minute.
GET/services/{id}/live/streamLive statistics over SSE
Opens a Server-Sent Events stream with usage samples of a VPS or game server about every second (hello, backfill, sample, disk, status, unavailable, end). A connection made with an API key ends after about a minute with an end event; then reconnect.
Permission: services:read
Sample response
event: hello
data: {"v":1,"kind":"vps","intervalMs":1000,"serverNow":1762508460000}
event: sample
data: {"t":1762508461000,"power":"running","vmStatus":"running","gameState":null,"cpuPercent":8.3,"memUsedMib":1840,"memTotalMib":4096,"netInBps":420000,"netOutBps":1100000,"diskReadBytesPerSec":0,"diskWriteBytesPerSec":24576,"uptimeSeconds":86401}
event: end
data: {"reason":"rotate"}
Possible errors
- 403
mfa_requiredThe service owner requires two-factor authentication for members. - 404 Service not found.
- 200 Other problems arrive as an end event with a reason: unsupported, busy (with retryInMs), revoked, inactive, moved, rotate.
PATCH/services/{id}/nameRename a service
Sets a custom service name, returned afterwards in the name field (productName keeps the plan name). null or an empty string restores the original name. Service owner only.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
name | string | null | yes | New name, at most 60 characters. |
locale | string | no | Language of error messages (cs, en, sk, pl, de). |
Sample body
{
"name": "Survival server"
}Sample response
{
"name": "Survival server"
}Possible errors
- 400
invalidThe name is longer than 60 characters or the body contains other fields. - 404
not_allowedThe service doesn't belong to your account.
GET/services/{id}/outletsPDU outlet state and events
Returns the state of the rackhousing outlets (on, off, power, current), whether you may control them, and the last 20 PDU events. Rackhousing only; without outlets, view is null.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
before | string | no | Event ID to load older events from (paging). |
Sample response
{
"view": {
"canControl": true,
"controlBlocked": null,
"measuredAt": "2026-11-07T09:41:00.000Z",
"stale": false,
"outlets": [
{
"n": 3,
"state": "on",
"pending": false,
"powerW": 120,
"currentA": 0.52
}
],
"events": [
{
"id": "clx9k2m4p0001",
"at": "2026-11-06T18:12:00.000Z",
"kind": "command",
"severity": "info",
"outlet": 3,
"action": "reboot",
"result": "ok",
"actorType": "customer",
"actor": "vy",
"serviceRef": "#A1B2C3",
"message": "Restart zásuvky 3 — provedeno."
}
],
"eventsMore": false,
"eventsAvailable": true
}
}Possible errors
- 400 The service is not rackhousing.
- 404 Service not found.
POST/services/{id}/outletsSwitch or restart PDU outlets
Sends a command to the PDU outlets that belong to your rackhousing service. Works only for an active service where support has enabled control; limit 6 per minute per account and 5 per minute per service.
Permission: services:power
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
outlets | number[] | yes | Outlet numbers 1–48 (1 to 48 items), only outlets of this service. |
command | string | yes | on, off, reboot or cancel (cancels a pending command). |
Sample body
{
"outlets": [
3,
4
],
"command": "reboot"
}Sample response
{
"ok": true,
"outlets": [
{
"n": 3,
"state": "on",
"pending": true
},
{
"n": 4,
"state": "on",
"pending": true
}
]
}Possible errors
- 400
not_yoursAn outlet does not belong to this service. Without a code: invalid request body. - 403
not_allowedOutlet control is not enabled for the service. - 409
inactiveThe service is not active. Code no_config: the PDU is not available right now. - 429 Rate limit exceeded.
- 502
pdu_failedThe PDU did not carry out the command.
GET/services/{id}/powerRackhousing power consumption
Returns the outlet power draw against the contracted power, kWh for this and last month and a daily breakdown. Rackhousing only, data come from the last PDU reading; without outlets or data, view is null.
Permission: services:read
Sample response
{
"view": {
"contractedW": 500,
"currentW": 212,
"measuredAt": "2026-11-07T09:41:00.000Z",
"stale": false,
"monthKwh": 38.4,
"lastMonthKwh": 151.2,
"month": {
"y": 2026,
"m": 11,
"days": 30,
"today": 7
},
"daily": [
5.1,
5.3,
5.4,
5.6,
5.5,
5.7,
5.8,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null
],
"outlets": [
{
"n": 3,
"powerW": 120,
"monthKwh": 21.6
},
{
"n": 4,
"powerW": 92,
"monthKwh": 16.8
}
]
}
}Possible errors
- 400 The service is not rackhousing.
- 404 The service does not exist or you have no access to it.
POST/services/{id}/powerStart, stop or restart a server
Starts, stops or restarts a VPS or a game server (also a bot). Active services only, at most 5 requests per minute per service; the billing status does not change.
Permission: services:power
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | start, stop or restart. |
Sample body
{
"action": "restart"
}Sample response
{
"ok": true
}Possible errors
- 400 action is missing or invalid.
- 404 Service not found, or power control is not available for it (e.g. rackhousing, web).
- 409 The service is not active, a server software change is running, or a backup restore is still running.
- 429 More than 5 requests per minute for the service.
- 502 The action failed, try again.
GET/services/{id}/rackhousingRackhousing overview
Returns the rack position, height, contracted power, ports with link state and IP addresses of a rackhousing service. Port state comes from the last poll; the request changes nothing.
Permission: services:read
Sample response
{
"view": {
"site": "Praha",
"rack": "R12",
"unitRange": "U20–U21",
"heightU": 2,
"powerW": 500,
"ports1g": 1,
"ports10g": 1,
"ports": [
{
"port": "port 3",
"speed": "1g",
"speedText": "1 Gbit",
"status": "up"
},
{
"port": "port 4",
"speed": "10g",
"speedText": "10 Gbit",
"status": "down"
}
],
"ipv4": [
{
"address": "203.0.113.10",
"prefix": 29,
"gateway": "203.0.113.9"
}
],
"ipv6": "2001:db8:100::/48",
"equipment": "1× server 2U",
"polledAt": "2026-11-07T09:40:00.000Z"
}
}Possible errors
- 400 The service is not rackhousing.
- 404 Service not found.
GET/services/{id}/sftpSFTP login details
Returns the SFTP host, port and user name of a game server or bot; the password is never returned. A member of a shared service gets only owner: false. The feature isn't live yet and returns 404.
Permission: services:secrets
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | en for English messages, otherwise Czech. |
Sample response
{
"owner": true,
"host": "203.0.113.40",
"port": 2022,
"username": "user.1a2b3c4d",
"canSetPassword": true
}Possible errors
- 404 SFTP isn't live yet, or the service wasn't found.
- 400 The service is neither a game server nor a bot.
- 502 The details couldn't be loaded.
POST/services/{id}/sftpSet the SFTP password
Sets the given password for SFTP of a game server or bot; the password isn't stored anywhere and the owner gets an e-mail. Owner only, limit 5 per hour, the portal confirmation is replaced by the services:secrets scope. The feature isn't live yet and returns 404.
Permission: services:secrets
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | en for English messages, otherwise Czech. |
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | Always setPassword. |
password | string | yes | New password, 10–200 characters, not a common or leaked password. |
Sample body
{
"action": "setPassword",
"password": "example-Sftp-Pass-2026"
}Sample response
{
"ok": true,
"username": "user.1a2b3c4d"
}Possible errors
- 400
weak_passwordThe password doesn't meet the rules or the body is invalid. - 403
insufficient_scopeThe key lacks the services:secrets scope. Code not_owner: you are not the owner. - 404 SFTP isn't live yet.
- 409
admin_userSupport will set up SFTP for this service. Other codes: email_mismatch, foreign_server, inactive. - 429 More than 5 attempts per hour.
- 502
failedThe password couldn't be set. Code unavailable: the service isn't available right now.
VPS and rackhousing
GET/services/{id}/backupsList VPS backups
Returns VPS backups from all storages, only from the current owner's time. Results are cached for 20 s, limit 10 requests per minute; partial: true means a storage didn't respond. Backups and storages have opaque ids (a backup's storageId = a storage id in storages); send the backup id to backups/restore.
Permission: services:backups
Sample response
{
"backups": [
{
"id": "Q2xP7rT0aN4mVb8sKe1wYg",
"storageId": "h5Jd9LkQ2pWm0ZcR7tUv3A",
"storageLabel": "Backup 1",
"sizeBytes": 8589934592,
"createdAt": 1762394400,
"name": "vps-000001"
}
],
"storages": [
{
"id": "h5Jd9LkQ2pWm0ZcR7tUv3A",
"label": "Backup 1",
"enabled": true
},
{
"id": "Xo3bN6qE8yTf1GsD4lHk9w",
"label": "Backup 2",
"enabled": false
}
]
}Possible errors
- 403 Missing Backups permission on a shared service.
- 409 The service isn't active, or a backup restore is running.
- 412
NOT_PROVISIONEDThe VPS isn't ready yet. - 429 More than 10 requests per minute.
- 502 Backups couldn't be loaded.
POST/services/{id}/backups/restoreRestore the VPS from a backup
Hard-stops the VPS and overwrites its disk with the chosen backup; irreversible. For larger disks the restore finishes in the background (pending: true) and the VPS can be started when it ends. Limit 3 per minute per service.
Permission: services:backups
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | Backup id from the backups list. |
Sample body
{
"id": "Q2xP7rT0aN4mVb8sKe1wYg"
}Sample response
{
"ok": true,
"pending": true,
"message": "Obnova ze zálohy běží. VPS půjde zapnout, až skončí (u větších disků i několik minut)."
}Possible errors
- 400 id is missing.
- 403 The backup doesn't belong to this service or no longer exists.
- 409 The service isn't active or another restore is already running.
- 429 More than 3 attempts per minute.
- 502 The restore couldn't be started or completed.
GET/services/{id}/ddosAnti-DDoS protection availability
Tells whether Anti-DDoS settings are available for the VPS IP addresses. Currently always returns available: false; on a shared service a member needs the Network permission.
Permission: services:read
Sample response
{
"available": false
}Possible errors
- 400 The service is not a VPS.
- 409 The service isn't active.
GET/services/{id}/firewallVPS firewall rules
Returns the VPS firewall settings and all rules, including managed ones (field managed) that can't be changed. Send the digest value when editing a rule.
Permission: services:firewall
Sample response
{
"digest": "3f2a9c0d1e5b7a8c4d6e2f1a0b9c8d7e6f5a4b3c",
"baseline": true,
"enabled": true,
"ipfilter": true,
"macfilter": true,
"policyIn": "DROP",
"policyOut": "ACCEPT",
"inboundDefault": "accept",
"scannerBlock": true,
"ipfilterEntries": [
"203.0.113.25",
"2001:db8:10::/64"
],
"rules": [
{
"pos": 0,
"managed": "censys",
"type": "in",
"action": "DROP",
"proto": "",
"dport": "",
"sport": "",
"source": "+dc/censys",
"dest": "",
"macro": "",
"comment": "",
"enable": true
},
{
"pos": 1,
"managed": null,
"type": "in",
"action": "DROP",
"proto": "tcp",
"dport": "3306",
"sport": "",
"source": "",
"dest": "",
"macro": "",
"comment": "MySQL ven ne",
"enable": true
},
{
"pos": 2,
"managed": "allowIn",
"type": "in",
"action": "ACCEPT",
"proto": "",
"dport": "",
"sport": "",
"source": "",
"dest": "+guest/ipfilter-net0",
"macro": "",
"comment": "",
"enable": true
}
]
}Possible errors
- 403 Missing Firewall permission on a shared service.
- 409 The service isn't active or the action can't be done right now.
- 412 The VPS isn't ready yet.
- 502 The firewall couldn't be loaded.
POST/services/{id}/firewallEdit VPS firewall rules
Adds, edits, enables or disables, deletes or moves a custom rule, switches default inbound traffic or applies the recommended setup. Actions on a position need digest from GET; at most 50 custom rules, messages are in Czech.
Permission: services:firewall
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | add, update, toggle, delete, move, setInbound or applyBaseline. |
pos | number | no | For update, toggle, delete and move: position of a custom rule from GET. |
digest | string | no | For update, toggle, delete and move: digest from GET. |
rule | object | no | For add and update: type (in, out), action (ACCEPT, DROP, REJECT), proto ("", tcp, udp, icmp, ipv6-icmp), dport, sport (e.g. 22, 80,443, 25565:25570; TCP and UDP only), source, dest (IP or range, at most 10), comment (max 100), enable. |
enable | boolean | no | For toggle: enable or disable the rule. |
direction | string | no | For move: up or down (only among custom rules). |
value | string | no | For setInbound: accept or drop (default inbound traffic). |
Sample body
{
"action": "add",
"rule": {
"type": "in",
"action": "ACCEPT",
"proto": "tcp",
"dport": "22",
"source": "198.51.100.0/24",
"comment": "SSH z kanceláře"
}
}Sample response
{
"ok": true,
"firewall": {
"digest": "9b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c",
"baseline": true,
"enabled": true,
"ipfilter": true,
"macfilter": true,
"policyIn": "DROP",
"policyOut": "ACCEPT",
"inboundDefault": "accept",
"scannerBlock": true,
"ipfilterEntries": [
"203.0.113.25"
],
"rules": []
}
}Possible errors
- 400 Invalid body or rule (port, address, protocol), or more than 50 custom rules.
- 403 A managed rule can't be changed, or the Firewall permission is missing.
- 409 The firewall changed meanwhile (reload digest), the service isn't active, a restore is running or the recommended setup can't be applied.
- 502 The change couldn't be saved.
GET/services/{id}/historyVPS usage history
Returns CPU, RAM, network and disk history of a VPS for the chosen range (at most 300 points), CPU peaks and traffic for 30 days and since the start of the month. Results are cached for 20 s, limit 30 requests per minute.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
range | string | no | hour, day, week, month or year. Default day. |
lang | string | no | en for English error messages, otherwise Czech. |
Sample response
{
"range": "day",
"points": [
{
"t": 1762473600,
"cpu": 6.2,
"ram": 44.8,
"netIn": 52000,
"netOut": 138000,
"diskRead": 0,
"diskWrite": 20480
}
],
"cpuPeak": [
{
"t": 1762473600,
"cpu": 31.5
}
],
"cpu": {
"avg": 7.1,
"max": 64.2
},
"transfer30d": {
"inBytes": 18253611008,
"outBytes": 42949672960
},
"transferMonth": {
"inBytes": 4294967296,
"outBytes": 9663676416
}
}Possible errors
- 400 The service is not a VPS.
- 409 The VPS is neither active nor suspended, or can't be verified right now.
- 429 More than 30 requests per minute.
- 502 Statistics can't be loaded right now.
GET/services/{id}/installVPS installation status
Returns the phase of a VPS installation or reinstall (image, create, start, finish, done) and whether it is still running. Useful for polling after a reinstall, limit 90 requests per minute.
Permission: services:read
Sample response
{
"status": "ACTIVE",
"phase": "finish",
"active": true,
"source": "reinstall",
"restarting": false,
"running": true,
"reboot": true,
"failed": false
}Possible errors
- 400 The service is not a VPS.
- 404 Service not found.
- 429 More than 90 requests per minute.
GET/services/{id}/isoISO images and the attached ISO
Returns the ISO catalogue, your own ISOs, limits, the currently attached ISO with the boot setting and the state of the last action. VPS owner only.
Permission: services:settings
Sample response
{
"available": true,
"catalog": [
{
"id": "clx9a0iso0001",
"kind": "catalog",
"name": "Debian 13 netinst",
"osFamily": "debian",
"sizeBytes": 754974720,
"createdAt": "2026-10-20T08:00:00.000Z",
"lastUsedAt": null,
"expiresAt": null
}
],
"own": [
{
"id": "clx9a0iso0002",
"kind": "custom",
"name": "Rescue tools",
"osFamily": null,
"sizeBytes": 524288000,
"createdAt": "2026-11-02T12:00:00.000Z",
"lastUsedAt": "2026-11-03T09:00:00.000Z",
"expiresAt": "2026-12-03T09:00:00.000Z"
}
],
"limits": {
"count": 2,
"maxBytes": 10737418240,
"retentionDays": 30
},
"status": {
"attached": false,
"bootFromIso": false,
"isoId": null,
"name": null
},
"job": null
}Possible errors
- 400 The service is not a VPS.
- 403 You are not the service owner.
- 409 The service isn't active.
POST/services/{id}/isoAdd, attach or detach an ISO
Depending on action, adds your own ISO from an HTTPS link, deletes it, attaches an ISO to the VPS, switches boot or ejects the ISO. Attaching runs as a background action (the file is downloaded first); owner only, at most 2 own ISOs up to 10 GB.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | add, delete, attach, boot or detach. |
name | string | no | For add: ISO name (max 80 characters), otherwise taken from the file. |
url | string | no | For add: public https:// link to the ISO file that reports its size. |
isoId | string | no | For delete and attach: ISO ID from GET. |
boot | boolean | no | For attach and boot: true = boot from ISO, otherwise from disk. |
Sample body
{
"action": "attach",
"isoId": "clx9a0iso0001",
"boot": true
}Sample response
{
"ok": true,
"job": {
"id": "clx9r1t7w0004",
"kind": "iso",
"state": "running",
"step": null,
"startedAt": "2026-11-07T10:00:00.000Z",
"updatedAt": "2026-11-07T10:00:00.000Z"
}
}Possible errors
- 400
urlThe link can't be used (not HTTPS, non-public network, over 10 GB, no size, not a file). Other codes: limit, attached; without a code, invalid action. - 404
not_foundISO not found. - 409
unavailableISOs aren't available right now. Without a code: another action is running on the VPS. - 429 More than 10 ISOs added per day.
- 502 The action failed.
GET/services/{id}/private-networkPrivate networks in the VPS location
Returns your private networks in the VPS location, their members, limits and the state of this VPS in each network. VPS owner only.
Permission: services:network
Sample response
{
"enabled": true,
"scope": "location",
"maxNetworks": 5,
"maxMembers": 20,
"priceCzk": null,
"networks": [
{
"id": "clx9p7net0001",
"name": "zalohy",
"cidr": "10.20.0.0/24",
"prefix": 24,
"createdAt": "2026-10-18T08:00:00.000Z",
"members": [
{
"serviceId": "clx9svc000001",
"shortId": "#000001",
"ipv4": "203.0.113.25",
"address": "10.20.0.1",
"state": "active",
"isThis": true,
"own": true
}
],
"me": {
"address": "10.20.0.1",
"mac": "BC:24:11:00:00:01",
"nicIndex": 1,
"state": "active",
"cloudInit": false,
"failed": false
},
"canDelete": false
}
]
}Possible errors
- 403 You are not the service owner.
- 409 The service isn't active.
- 503
unavailablePrivate networks aren't available right now.
POST/services/{id}/private-networkCreate, attach or detach a network
Creates a private network, attaches or detaches this VPS, or deletes an empty network. Attaching needs the recommended firewall setup; when pendingRestart is true, the change takes effect after a VPS restart.
Permission: services:network
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | create, attach, detach or delete. |
name | string | no | For create: network name, 1–40 characters (letters, digits, space, . _ -). |
cidr | string | no | For create: private range /16 to /29 starting at the network address, e.g. 10.20.0.0/24. |
networkId | string | no | For attach, detach and delete: network ID from GET. |
address | string|null | no | For attach: VPS address in the network; empty = first free one. |
cloudInit | boolean | no | For attach: true = also write the address into the VPS cloud-init. |
Sample body
{
"action": "attach",
"networkId": "clx9p7net0001",
"address": "10.20.0.5"
}Sample response
{
"ok": true,
"address": "10.20.0.5",
"mac": "BC:24:11:00:00:05",
"pendingRestart": true,
"view": {
"enabled": true,
"scope": "location",
"maxNetworks": 5,
"maxMembers": 20,
"priceCzk": null,
"networks": []
}
}Possible errors
- 400
invalid_requestInvalid body. Other codes: name_invalid, cidr_invalid, address_invalid, address_outside, address_reserved. - 404
network_not_foundNetwork not found. Code not_member: the VPS isn't in the network. - 409
firewall_requiredApply the recommended firewall setup first. Other codes: cidr_overlap, limit_networks, limit_members, address_taken, network_full, not_empty, detaching, other_location, other_node, no_slot, vm_locked, disabled, no_vlan. - 412
vm_not_readyThe VPS isn't ready yet. - 502
apply_failedThe change couldn't be completed. - 503
unavailablePrivate networks aren't available right now.
GET/services/{id}/rdnsReverse DNS of VPS addresses
Returns PTR records of the VPS IP addresses: current, default and custom value, plus the service's IPv6 ranges. On a shared service a member needs the Network permission.
Permission: services:network
Sample response
{
"configured": true,
"error": null,
"entries": [
{
"ip": "203.0.113.25",
"family": 4,
"current": "mail.example.com",
"default": "25.113.0.203.example.net",
"custom": "mail.example.com"
},
{
"ip": "2001:db8:10::2",
"family": 6,
"current": null,
"default": "vps.example.net",
"custom": null
}
],
"ranges": [
{
"cidr": "2001:db8:10::/64",
"family": 6
}
],
"vmIpv6": "2001:db8:10::2"
}Possible errors
- 400 The service is not a VPS.
- 403 Missing Network permission on a shared service.
- 409 The service isn't active.
POST/services/{id}/rdnsSet or reset a PTR record
Sets a custom reverse DNS for an IP address assigned to the VPS, or with hostname null restores the default record. At most 32 custom records, limit 20 changes per minute; messages are in Czech.
Permission: services:network
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
ip | string | yes | IPv4 or IPv6 address of the service (also from its IPv6 range). |
hostname | string|null | yes | Host name, e.g. mail.example.com (max 253 characters); null or empty = default record. |
Sample body
{
"ip": "203.0.113.25",
"hostname": "mail.example.com"
}Sample response
{
"ok": true,
"ip": "203.0.113.25",
"ptr": "mail.example.com",
"isDefault": false
}Possible errors
- 400 Invalid host name, reverse or reserved domain, or more than 32 custom records.
- 403 The IP address doesn't belong to this service, or the Network permission is missing.
- 502 rDNS couldn't be saved right now.
GET/services/{id}/reinstallSystems available for VPS reinstall
Returns the systems that can be installed on the VPS, the current system, the time of the last reinstall and the state of the last action. Service owner only.
Permission: services:read
Sample response
{
"options": [
{
"key": "ubuntu-2404",
"label": "Ubuntu 24.04 LTS"
},
{
"key": "debian-13",
"label": "Debian 13"
}
],
"job": {
"id": "clx9r1t7w0002",
"kind": "reinstall",
"state": "done",
"step": null,
"startedAt": "2026-11-01T10:00:00.000Z",
"updatedAt": "2026-11-01T10:06:12.000Z",
"finishedAt": "2026-11-01T10:06:12.000Z",
"error": null
},
"os": "ubuntu-2404",
"reinstalledAt": "2026-11-01T10:06:12.000Z"
}Possible errors
- 400 The service is not a VPS.
- 403 You are not the service owner.
- 409 The service isn't active.
POST/services/{id}/reinstallReinstall the VPS
Wipes the VPS disk and installs the chosen system; runs in the background, follow progress via install. The new root password is only in this response. Owner only, at most 3 times a day; the portal confirmation is replaced by the services:secrets scope.
Permission: services:secrets
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
os | string | yes | System key from GET, e.g. ubuntu-2404 or debian-13. |
sshKeys | string | no | Public SSH keys, one per line, at most 10 (ssh-ed25519, ssh-rsa, ecdsa-…). |
Sample body
{
"os": "debian-13",
"sshKeys": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyExampleKeyExampleKeyExample user@laptop"
}Sample response
{
"started": true,
"password": "example-Root-Pass-123",
"job": {
"id": "clx9r1t7w0003",
"kind": "reinstall",
"state": "running",
"step": null,
"startedAt": "2026-11-07T10:00:00.000Z",
"updatedAt": "2026-11-07T10:00:00.000Z"
}
}Possible errors
- 400 Missing or invalid system, invalid SSH key or more than 10 keys.
- 403
insufficient_scopeThe key lacks the services:secrets scope, or you are not the owner. - 409 Another action is running, the VPS is in rescue mode, the node is busy or reinstall is unavailable.
- 429 The reinstall has already run 3 times in the last 24 hours.
- 502 The reinstall couldn't be started.
POST/services/{id}/reveal-secretReveal the stored VPS root password
Returns the stored VPS root password from creation, reinstall or the last change. Every reveal is audited and the owner gets an e-mail; limit 5 per 10 minutes, the portal confirmation is replaced by the services:secrets scope.
Permission: services:secrets
Sample response
{
"password": "example-Root-Pass-789"
}Possible errors
- 403
insufficient_scopeThe key lacks the services:secrets scope. Without a code: missing permission on a shared service. - 404 Service not found or no password is stored for it.
- 429 More than 5 reveals in 10 minutes.
- 503 The password can't be shown right now, try again.
GET/services/{id}/root-passwordCan a new root password be set?
Checks whether the VPS root password can be changed right now through qemu-guest-agent and returns the reason if not. The result is cached for 60 s.
Permission: services:secrets
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | en for English messages, otherwise Czech. |
Sample response
{
"available": false,
"reason": "stopped",
"message": "VPS je vypnutá. Heslo jde nastavit jen u běžící VPS."
}Possible errors
- 400 The service is not a VPS.
- 403 Missing Secrets permission on a shared service.
- 409 The service isn't active.
POST/services/{id}/root-passwordSet a new VPS root password
Generates a new root password, sets it in the running VPS through qemu-guest-agent and returns it only in this response; the owner gets an e-mail. Limit 3 per hour per service and 10 per day per account; the portal confirmation is replaced by the services:secrets scope.
Permission: services:secrets
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | en for English messages, otherwise Czech. |
Sample response
{
"password": "example-Root-Pass-456"
}Possible errors
- 403
insufficient_scopeThe key lacks the services:secrets scope. Without a code: missing permission on a shared service. - 409 The password can't be set right now; field reason: agent_down, stopped, rescue, command_disabled, not_configured, no_permission or busy.
- 429 Limit of 3 per hour or 10 per day exceeded.
- 502 The agent didn't set the password, the old one still works (reason failed or unavailable).
Game servers
GET/services/{id}/alertsCrash and outage alert settings
Returns the alert settings of a game server or Discord bot (e-mail, Discord webhook, events) and the history of recent alerts. The webhook address is never returned, only its ending. Owner only.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of messages: en, otherwise cs. |
Sample response
{
"settings": {
"enabled": true,
"email": true,
"discord": {
"hint": "discord.com/api/webhooks/…ab12",
"disabled": false
},
"events": {
"crash": true,
"down": true,
"recovered": true,
"cleanExit": false,
"node": true
},
"includeLog": true,
"updatedAt": "2026-11-02T18:20:00.000Z"
},
"history": [
{
"id": "alr_01",
"at": "2026-11-06T21:14:03.000Z",
"kind": "crash",
"exitCode": 137,
"oom": true,
"reason": null,
"cause": null,
"suppressed": null,
"channels": {
"email": "sent",
"discord": "sent"
},
"extra": 0,
"downtimeMin": null
}
]
}Possible errors
- 403 Only for the service owner.
POST/services/{id}/alertsSave, test or turn off alerts
Changes the alert settings, chosen by the action field. A new Discord webhook is saved only if a test message gets through. The body must not contain other fields. Owner only.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of messages: en, otherwise cs. |
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | save, test (test message to the saved webhook, once a minute and 5 times an hour), removeDiscord or disable. |
enabled | boolean | no | save: turn monitoring on. |
email | boolean | no | save: send alerts by e-mail to the owner. |
events | object | no | save: which events to report, keys crash, down, recovered, cleanExit, node (boolean). |
includeLog | boolean | no | save: attach the end of the log. |
webhook | string | no | save: new Discord webhook address (https://discord.com/api/webhooks/…), up to 300 characters. |
Sample body
{
"action": "save",
"enabled": true,
"email": true,
"events": {
"crash": true,
"down": true,
"recovered": true,
"cleanExit": false,
"node": true
},
"includeLog": true
}Sample response
{
"ok": true,
"settings": {
"enabled": true,
"email": true,
"discord": null,
"events": {
"crash": true,
"down": true,
"recovered": true,
"cleanExit": false,
"node": true
},
"includeLog": true,
"updatedAt": "2026-11-07T10:05:00.000Z"
}
}Possible errors
- 400
bad-requestInvalid body or extra fields. - 400
webhookThe address is not a Discord webhook. - 400
test-failedThe test message could not be delivered. - 400
no-webhooktest: no webhook is saved. - 400
no-channelEnabled monitoring needs e-mail or a webhook. - 429
rateA test message can be sent once a minute and 5 times an hour. - 500
failedThe settings could not be saved. - 403 Only for the service owner.
GET/services/{id}/gameNetwork, databases, backups or startup
Returns one part of a game server or Discord bot setup, chosen by the section parameter. Database passwords and secret variable values are returned only to the owner and members with the Secrets permission.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
section | string | yes | network (ports and SFTP), databases, backups (backups and the backup add-on) or startup (startup command and variables). |
locale | string | no | Language of access error messages: cs, en, sk, pl or de. |
Sample response
{
"allocations": [
{
"id": 1201,
"ip": "203.0.113.10",
"port": 25565,
"notes": null,
"primary": true
},
{
"id": 1202,
"ip": "203.0.113.10",
"port": 25566,
"notes": "Dynmap",
"primary": false
}
],
"limit": 3,
"sftp": {
"ip": "203.0.113.10",
"port": 2022
}
}Possible errors
- 400 The section is missing or unknown.
- 403 Missing permission for this section (Network, Databases, Backups or Settings).
- 412 The server is not available in the game panel yet.
POST/services/{id}/gameDatabases, backups, variables, ports and reinstall
Runs one action on a game server or Discord bot, chosen by the action field. Restoring or deleting a backup, deleting a database and reinstalling cannot be undone; reinstall stops the server and overwrites files.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | createDatabase, rotateDatabasePassword, deleteDatabase, createBackup, restoreBackup, deleteBackup, lockBackup (locks or unlocks), setVariable, reinstall, addAllocation or removeAllocation. |
name | string | no | createDatabase: database name, 1–48 characters (letters, digits, underscore), required. createBackup: optional backup name, up to 191 characters. |
remote | string | no | createDatabase: where connections to the database are allowed from (IP address or %). Default % = anywhere. |
databaseId | string | no | rotateDatabasePassword, deleteDatabase: database id from GET ?section=databases. |
uuid | string (uuid) | no | restoreBackup, deleteBackup, lockBackup: backup uuid from GET ?section=backups. |
truncate | boolean | no | restoreBackup: delete all server files before restoring. Default false. |
key | string | no | setVariable: name of the startup variable (uppercase letters, digits, underscore). |
value | string | no | setVariable: new value, up to 4096 characters, no control characters. |
allocationId | integer | no | removeAllocation: port id from GET ?section=network. The primary port cannot be removed. |
locale | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Sample body
{
"action": "setVariable",
"key": "MAX_PLAYERS",
"value": "32"
}Sample response
{
"ok": true,
"value": "32"
}Possible errors
- 400 Invalid request body or database name.
- 400
CONTROL_CHARSThe variable value contains control characters. - 403 Missing permission for the action; reinstall also needs Files and Power.
- 403
SECRETS_REQUIREDA secret variable can only be changed by the owner or a member with the Secrets permission. - 403
FILES_REQUIREDThe Discord bot code source can only be changed with the Files permission. - 404 The backup does not exist.
- 409
MANAGED_VARIABLEThe variable is managed by StormHost and cannot be changed. - 409
MC_MANAGED_VARIABLEFor Minecraft this variable is set in the Software tab. - 409
PROTECTED_ARGUMENTFree arguments must not override protected parameters (e.g. RCON). - 409
JOB_RUNNINGA software change is running on the Minecraft server. - 409
PORT_LIMITThe package has no free port left. - 409
NO_FREE_PORTThere is no free port on the node right now. - 412 The server is not available in the game panel yet.
GET/services/{id}/game/backup-downloadDownload a game server backup
Downloads the backup archive (.tar.gz) of a game server or Discord bot. The archive contains all files including secrets, so members also need the Files permission.
Permission: services:backups
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
uuid | string (uuid) | yes | Backup uuid from GET …/game?section=backups. |
locale | string | no | Language of access error messages: cs, en, sk, pl or de. |
Sample response
Binární soubor (Content-Type: application/gzip, Content-Disposition: attachment; filename="backup-….tar.gz")Possible errors
- 400 The uuid is missing or invalid.
- 403 Missing the Backups or Files permission.
- 404 The backup does not exist.
- 502 The backup could not be downloaded from the game panel.
- 429 At most 10 downloads per minute.
GET/services/{id}/schedulesSchedules of a game server or bot
Returns the scheduled tasks (restart, backup, command, start or stop) of a game server or Discord bot, their next and last run, limits and available backup slots.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of texts in the response: en, otherwise cs. |
Sample response
{
"schedules": [
{
"id": 41,
"name": "Denní restart",
"cron": {
"minute": "0",
"hour": "5",
"dom": "*",
"month": "*",
"dow": "*"
},
"target": {
"minute": "0",
"hour": "5",
"dom": "*",
"month": "*",
"dow": "*"
},
"active": true,
"onlyWhenOnline": false,
"processing": false,
"stuck": false,
"lastRunAt": "2026-11-07T04:00:00.000Z",
"nextRunAt": "2026-11-08T04:00:00.000Z",
"kind": "restart",
"preset": {
"kind": "restart",
"repeat": {
"kind": "daily",
"time": "05:00"
},
"countdown": {
"m5": true,
"m1": true,
"s10": true
}
},
"origin": "portal",
"author": "you",
"lastResult": "ok",
"lastResultCause": null,
"allowed": true
}
],
"panelZone": {
"expected": "Europe/Prague",
"detected": "Europe/Prague",
"ok": true,
"preview": "Europe/Prague"
},
"backupSlots": 1,
"canBackup": true,
"backupAddonPriceCzk": 120,
"game": "minecraft",
"countdown": true,
"mcVersion": "1.21.4",
"isOwner": true,
"permissions": {
"console": true,
"power": true,
"backups": true
},
"limits": {
"maxSchedules": 10,
"nameMax": 60,
"commandMax": 256,
"messageMax": 100,
"hourSteps": [
1,
2,
3,
4,
6,
8,
12
]
}
}Possible errors
- 403 Missing the Settings permission.
- 412 The server is not available in the game panel yet.
POST/services/{id}/schedulesCreate, edit, run or delete a schedule
Manages schedules, chosen by the action field. A server can have up to 10 schedules; a command at most every 15 minutes, restart and start or stop every hour, a backup once a day and only one backup schedule.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | create, update, delete, toggle (enable or disable), run (run now, at most once a minute) or unstick (release a stuck schedule). |
scheduleId | integer | no | All actions except create: schedule id from GET. |
preset | object | no | create, update: kind restart (repeat, countdown {m5, m1, s10}, message up to 100 characters), backup (repeat), command (repeat, command up to 256 characters, onlyWhenOnline) or power (repeat, signal start|stop). |
preset.repeat | object | no | Repeat: {kind: daily, time: "HH:MM"}, {kind: weekly, days: [0–6, 0 = Sunday], time}, {kind: hours, every: 1|2|3|4|6|8|12, minute: 0–59} or {kind: cron, expr}. |
name | string | no | create, update: schedule name, up to 60 characters. |
ack | boolean | no | create, update: must be true for a backup schedule (confirms the new backup replaces the older one). |
active | boolean | no | toggle: true enables, false disables. |
locale | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Sample body
{
"action": "create",
"name": "Noční restart",
"preset": {
"kind": "restart",
"repeat": {
"kind": "daily",
"time": "04:30"
},
"countdown": {
"m5": true,
"m1": true,
"s10": false
},
"message": "Server se za chvíli restartuje."
}
}Sample response
{
"ok": true,
"id": 42
}Possible errors
- 400
INVALIDThe schedule is not filled in correctly. - 400
GAPThe schedule would run too often. - 400
BACKUP_ACKack: true is missing for a backup schedule. - 400
COMMAND_FORBIDDENThe command would stop or restart the server; use the restart or power preset. - 403
FORBIDDENMissing permission for the schedule (Console, Power or Backups depending on tasks). - 404
NOT_FOUNDThe schedule does not exist. - 409
LIMITThe server already has 10 schedules. - 409
BACKUP_SLOTSScheduled backups need the backup add-on. - 409
BACKUP_EXISTSA backup schedule already exists. - 409
NOT_EDITABLEThe schedule was created outside the portal; it can only be disabled or deleted. - 409
PANEL_TZSchedules at a specific time cannot be set right now, only hourly repeats. - 409
SUSPENDEDThe server is suspended. - 409
JOB_RUNNINGA software change is running on the Minecraft server. - 429
RATEA schedule can be run at most once a minute. - 502
PARTIALThe schedule was not saved completely and stays disabled.
GET/services/{id}/vip-shopVIP shop of a Minecraft server
Returns the VIP shop of a Minecraft server: shop details, items with a price breakdown, current rates and a monthly sales summary. Owner only.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Sample response
{
"view": {
"state": {
"mode": "live",
"open": true,
"checkoutOpen": true
},
"usable": true,
"shop": {
"id": "shp_3k9d",
"slug": "muj-server",
"title": "Můj server",
"status": "active",
"sellerName": "Jan Novák",
"sellerIco": null,
"sellerEmail": "[email protected]",
"sellerAddress": null,
"playerTerms": null,
"termsVersion": "2026-10-29",
"termsAcceptedAt": "2026-11-01T09:00:00.000Z",
"termsCurrent": true,
"suspendedReason": null,
"createdAt": "2026-11-01T09:00:00.000Z"
},
"items": [
{
"id": "itm_8f2a",
"title": "VIP na 30 dní",
"description": "Barevné jméno a /fly",
"priceCzk": 99,
"category": "Ranky",
"durationDays": 30,
"commandsBuy": [
"lp user {player} parent addtemp vip 30d"
],
"commandsExpire": [],
"active": true,
"sortOrder": 0,
"breakdown": null
}
],
"rates": null,
"termsVersion": "2026-10-29",
"summary": [
{
"month": "2026-11",
"count": 3,
"priceHaler": 29700,
"commissionHaler": 1485,
"feeHaler": 1020,
"creditHaler": 27195
}
]
}
}Possible errors
- 400
not_minecraftThe VIP shop is only for Minecraft servers. - 404
disabledThe VIP shop is not available for the account right now. - 503
disabledThe VIP shop is not ready yet. - 500
failedThe action failed. - 403 Only for the service owner.
POST/services/{id}/vip-shopEdit the VIP shop and its items
Creates or edits the VIP shop, pauses it and manages items, chosen by the action field. Shop details are always sent in full; terms: true is required when creating and after the terms change. Owner only.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | saveShop, setStatus, saveItem or deleteItem. |
slug | string | no | saveShop: shop address, 3–32 characters a–z, 0–9 and hyphen. |
title | string | no | saveShop: shop name (2–60 characters). saveItem: item name (2–60 characters). |
sellerName | string | no | saveShop: seller name or company (2–120 characters). |
sellerIco | string | no | saveShop: seller's Czech company ID (optional). |
sellerEmail | string | no | saveShop: seller contact e-mail. |
sellerAddress | string | no | saveShop: seller address, 5–200 characters (optional). |
playerTerms | string | no | saveShop: your own terms for players, up to 20,000 characters (optional). |
terms | boolean | no | saveShop: acceptance of the shop operator terms. |
status | string | no | setStatus: active or paused. |
id | string | no | saveItem: item id to edit (without id a new one is created, up to 50). deleteItem: item id (required). |
description | string | no | saveItem: description, up to 500 characters. |
priceCzk | integer | no | saveItem: price in CZK, 10–10,000 (required). |
category | string | no | saveItem: category, up to 40 characters. |
durationDays | integer | null | no | saveItem: duration in days 1–3650, null = permanent. |
commandsBuy | string[] | no | saveItem: 1–10 commands after purchase, each up to 300 characters; the only variable is {player}; commands such as stop, restart, op are not allowed. |
commandsExpire | string[] | no | saveItem: up to 10 commands when the item expires. |
active | boolean | no | saveItem: offer the item in the shop. Default true. |
sortOrder | integer | no | saveItem: sort order 0–10,000. |
Sample body
{
"action": "saveItem",
"title": "VIP na 30 dní",
"priceCzk": 99,
"category": "Ranky",
"durationDays": 30,
"commandsBuy": [
"lp user {player} parent addtemp vip 30d"
],
"commandsExpire": [],
"active": true
}Sample response
{
"ok": true,
"item": {
"id": "itm_8f2a",
"title": "VIP na 30 dní",
"description": null,
"priceCzk": 99,
"category": "Ranky",
"durationDays": 30,
"commandsBuy": [
"lp user {player} parent addtemp vip 30d"
],
"commandsExpire": [],
"active": true,
"sortOrder": 0,
"breakdown": null
}
}Possible errors
- 400
badInvalid request or unknown action. - 400 Invalid field; the code and field say which, e.g. slug, title, seller_email, terms, item_price, command_denied.
- 404
no_shopThe service has no shop yet. - 404
item_not_foundThe item does not exist. - 409
slug_takenThe shop address is taken. - 409
suspendedThe shop is suspended by the operator. - 409
items_maxThe shop already has 50 items. - 400
not_minecraftThe VIP shop is only for Minecraft servers. - 500
failedThe action failed. - 403 Only for the service owner.
GET/services/{id}/vip-shop/salesVIP shop sales and deliveries
Returns the last 50 VIP shop sales with command delivery state, commission, fee and credited amount. Amounts are in hellers (1/100 CZK). Works for inactive services too. Owner only.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
before | string (ISO 8601) | no | Sales older than this time (paging). |
Sample response
{
"sales": [
{
"id": "sal_51c0",
"number": "VIP-2026-000123",
"createdAt": "2026-11-05T19:02:11.000Z",
"paidAt": "2026-11-05T19:03:40.000Z",
"state": "paid",
"refundState": null,
"test": false,
"itemTitle": "VIP na 30 dní",
"durationDays": 30,
"playerName": "Steve_42",
"buyerEmail": "[email protected]",
"priceHaler": 9900,
"commissionHaler": 495,
"feeHaler": 340,
"creditHaler": 9065,
"reversedHaler": null,
"deliveries": [
{
"id": "dlv_77aa",
"kind": "purchase",
"state": "done",
"attempts": 1,
"nextAttemptAt": null,
"doneAt": "2026-11-05T19:03:52.000Z",
"lastErrorCode": null,
"sent": 1,
"total": 1
}
]
}
]
}Possible errors
- 404
no_shopThe service has no shop yet. - 400
not_minecraftThe VIP shop is only for Minecraft servers. - 500
failedThe action failed. - 403 Only for the service owner.
POST/services/{id}/vip-shop/salesRetry delivering commands for a sale
Immediately retries sending the commands of one delivery to the server. At most 10 times a minute per service. Owner only.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | retry. |
deliveryId | string | yes | Delivery id from GET (deliveries[].id). |
Sample body
{
"action": "retry",
"deliveryId": "dlv_77aa"
}Sample response
{
"ok": true,
"state": "done",
"code": null
}Possible errors
- 400
badInvalid request. - 404
no_shopThe service has no shop yet. - 404
delivery_not_foundThe delivery does not exist. - 409
delivery_stateThe delivery cannot be retried right now. - 429
rateAt most 10 attempts per minute. - 400
not_minecraftThe VIP shop is only for Minecraft servers. - 500
failedThe action failed. - 403 Only for the service owner.
Minecraft
GET/services/{id}/minecraft/datapacksDatapacks of the active world
Without parameters it returns the datapacks of the active world. With `q` it searches Modrinth for the server version, with `project` it lists versions of one datapack.
Permission: services:files
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
q | string | no | Search text (up to 80 characters). |
offset | integer | no | Offset in search results, default 0. |
project | string | no | Modrinth project ID or slug, returns its versions with a file. |
Sample response
{
"version": "1.21.4",
"world": "world",
"worldValid": true,
"worldExists": true,
"power": "running",
"busy": null,
"items": [
{
"name": "example-pack.zip",
"kind": "zip",
"size": 48211,
"mtime": 1730970000,
"source": "modrinth",
"title": "Example Pack",
"versionName": "1.2.0",
"projectId": "AbCdEf12",
"enabled": true,
"compat": "ok"
}
],
"evidenceBroken": false,
"canConsole": true,
"maxUploadMb": 50
}Possible errors
- 409
NOT_IMPORTEDThe server software is not set yet, use minecraft/software first. - 412
NO_PANELThe server is not available in the game panel right now. - 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/datapacksInstall, remove and reload datapacks
`install` downloads a datapack from Modrinth into the active world; on a running server it takes effect after `reload`. `remove` disables and deletes it, `reload` runs the `reload` command (running server only, on a shared service with the Console permission). Uploading your own ZIP is not available via the API.
Permission: services:files
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `install`, `remove` or `reload`. |
projectId | string | no | `install` only: Modrinth project ID (1–64 characters). |
versionId | string | no | `install` only: version ID from `GET ?project=` (1–16 characters). |
name | string | no | `remove` only: datapack file or folder name from the listing (up to 120 characters). |
Sample body
{
"action": "install",
"projectId": "AbCdEf12",
"versionId": "XyZ98765"
}Sample response
{
"ok": true,
"file": "example-pack.zip",
"replaced": null,
"needsReload": true,
"packFormat": "61",
"compat": "ok"
}Possible errors
- 400
INVALIDInvalid request body. - 403
FORBIDDEN`reload` without the Console permission on a shared service. - 404
NO_SUCH_PACKNo datapack with this name in the world. - 409
NAME_TAKENA datapack with the same name already exists, remove it first. - 409
TOO_MANYThe world already has 100 datapacks. - 409
NOT_RUNNINGThe server is not running, start it and try again. - 409
JOBAnother change or add-on installation is running on the server. - 422
INCOMPATIBLEThis datapack version does not match the server version. - 422
NO_PACK_MCMETAThe file is not a valid datapack (pack.mcmeta missing). - 429
RATE_LIMITEDLimit: install and remove 20 per 10 minutes, `reload` 10 per 10 minutes.
GET/services/{id}/minecraft/dnsCustom server address
Returns the custom address state (A/AAAA and SRV records), the default server port and the direct address. With `check` it only tests whether a name is free. If the server IP or port changed, the records are fixed on read (`resynced`).
Permission: services:network
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
check | string | no | Name to check for availability, returns `{ name, fqdn, available, code?, error? }`. |
locale | string | no | Language of access check error messages: cs, en, sk, pl, de. |
Sample response
{
"enabled": true,
"zone": "play.example.com",
"ttl": 300,
"nameMin": 3,
"nameMax": 32,
"current": {
"name": "mujserver",
"fqdn": "mujserver.play.example.com",
"zone": "play.example.com",
"ip": "203.0.113.10",
"port": 25565,
"recordType": "A",
"srvName": "_minecraft._tcp.mujserver.play.example.com",
"createdAt": "2026-11-07T10:15:00.000Z",
"updatedAt": null,
"records": {
"address": "ok",
"srv": "ok"
}
},
"allocation": {
"ip": "203.0.113.10",
"alias": null,
"port": 25565
},
"directAddress": "203.0.113.10:25565",
"allocationError": null,
"allocationErrorCode": null,
"recordsError": null,
"recordsErrorCode": null,
"resynced": false,
"canManage": true
}Possible errors
- 400
NOT_MINECRAFTThe service is not a Minecraft server. - 503
PANELServer management is temporarily unavailable. - 500
SETUP_FAILEDCould not complete, try again later.
POST/services/{id}/minecraft/dnsCreate, rename or delete address
Creates the address `<name>.<zone>`, renames or deletes it, or rewrites the records for the current IP and port (`resync`). The service must be active; the address can change at most 5 times per hour and 20 times per day.
Permission: services:network
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of access check error messages: cs, en, sk, pl, de. |
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `create`, `rename`, `delete` or `resync`. |
name | string | no | For `create` and `rename`: 3–32 characters, lowercase a–z, digits and hyphens (not at the start or end). |
Sample body
{
"action": "create",
"name": "mujserver"
}Sample response
{
"ok": true,
"current": {
"name": "mujserver",
"fqdn": "mujserver.play.example.com",
"zone": "play.example.com",
"ip": "203.0.113.10",
"port": 25565,
"recordType": "A",
"srvName": "_minecraft._tcp.mujserver.play.example.com",
"createdAt": "2026-11-07T10:15:00.000Z",
"updatedAt": null,
"records": {
"address": "ok",
"srv": "ok"
}
}
}Possible errors
- 400
INVALIDInvalid request body. - 400
INVALID_NAMEThe name does not meet the rules. - 400
RESERVEDThe name is reserved. - 400
SAME_NAMEThe server already has this address. - 404
NONEThe server has no custom address. - 409
TAKENThe address is taken. - 409
EXISTSThe server already has a custom address, use `rename`. - 409
NO_ALLOCATIONThe server has no default port. - 409
NOT_PUBLICThe default server address is not public. - 412
NO_PANEL_LINKThe server is not available in the game panel right now. - 429
RATE_LIMITEDChange limit exceeded, try again later. - 503
DISABLEDCustom addresses are not available right now. - 502
DNSWriting the DNS records failed.
GET/services/{id}/minecraft/eulaEULA acceptance state
Returns whether the Minecraft licence is accepted in eula.txt. `accepted: null` means the file does not exist yet.
Permission: services:settings
Sample response
{
"accepted": false,
"acceptedAt": null,
"url": "https://aka.ms/MinecraftEULA"
}Possible errors
- 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/eulaAccept the EULA
Writes `eula=true` to eula.txt. With `start: true` it also starts the server if it is offline (on a shared service with the Power permission). Limit 10 per 10 minutes.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
accept | boolean | yes | Must be `true`. |
start | boolean | no | Start the server after accepting if it is offline. |
Sample body
{
"accept": true,
"start": true
}Sample response
{
"ok": true,
"accepted": true,
"acceptedAt": "2026-11-07T10:15:00.000Z",
"started": true,
"startError": null,
"startErrorMsg": null
}Possible errors
- 400 `accept: true` is missing.
- 403 `start` without the Power permission.
- 409 A change (job) is running on the server.
- 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
GET/services/{id}/minecraft/gamerulesGame rules of the active world
Returns the game rules valid for the server version with current and default values. On a shared service the Console permission is required.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of rule labels: `cs` (default) or `en`. |
Sample response
{
"version": "1.21.4",
"world": "world",
"worldValid": true,
"power": "running",
"busy": null,
"perDimension": true,
"source": "file",
"fileAt": "2026-11-07T10:15:00.000Z",
"pending": [],
"rules": [
{
"id": "minecraft:keep_inventory",
"name": "keepInventory",
"type": "bool",
"min": null,
"max": null,
"group": "players",
"common": true,
"label": "Keep inventory after death",
"value": false,
"default": false
}
]
}Possible errors
- 409
NOT_IMPORTEDThe server software is not set yet, use minecraft/software first. - 412
NO_PANELThe server is not available in the game panel right now. - 413
FILE_TOO_LARGEThe rules file is too large. - 422
FILE_CORRUPTThe rules file is corrupted. - 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/gamerulesChange game rules
Sets the rules with the `gamerule` command on the running server; they apply immediately without a restart. Up to 30 rules at once, limit 30 requests per 10 minutes.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
rules | object | yes | Map of rule `id` (from GET) to a `boolean` or integer value. |
allDimensions | boolean | no | On Paper and its forks, set in all dimensions, default `true`. |
Sample body
{
"rules": {
"minecraft:keep_inventory": true,
"minecraft:spawn_radius": 0
},
"allDimensions": true
}Sample response
{
"ok": true,
"world": "world",
"results": [
{
"id": "minecraft:keep_inventory",
"status": "set"
},
{
"id": "minecraft:spawn_radius",
"status": "unchanged"
}
]
}Possible errors
- 400
INVALIDInvalid request body. - 400
NO_CHANGES`rules` is empty. - 400
UNKNOWN_RULEThe rule does not exist in the server version (field `rule`). - 400
INVALID_VALUEInvalid rule value (field `rule`). - 409
NOT_RUNNINGThe server is not running, start it and try again. - 409
NOT_IMPORTEDThe server software is not set yet, use minecraft/software first. - 409
JOBAnother change or add-on installation is running on the server. - 429
CONSOLE_BUSYThe server console is busy. - 412
NO_PANELThe server is not available in the game panel right now.
GET/services/{id}/minecraft/jobState of the running server change
Returns the last job (software change, reinstall, modpack, world) with step, log and progress. After starting a job, poll roughly every 2 s until `status` is `done` or `failed`; a stuck job is marked `failed` with `interrupted: true`.
Permission: services:settings
Sample response
{
"job": {
"id": "k3j9x2m1q8",
"kind": "switch",
"status": "running",
"step": "install",
"log": [
{
"at": "2026-11-07T10:15:00.000Z",
"key": "job.log.installing",
"params": {
"label": "Paper 1.21.4"
}
}
],
"startedAt": "2026-11-07T10:15:00.000Z",
"updatedAt": "2026-11-07T10:16:10.000Z",
"error": null,
"errorMsg": null,
"progress": {
"done": 42,
"total": 168,
"unit": "files",
"bytesDone": null,
"bytesTotal": null
},
"from": {
"type": "paper",
"version": "1.21.1",
"build": "130"
},
"target": {
"type": "paper",
"version": "1.21.4",
"build": "232"
},
"destructive": true,
"finishedAt": null
},
"active": true
}Possible errors
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/jobDismiss a finished job
Hides a finished or failed job so GET no longer returns it. A running job cannot be dismissed.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | Always `dismiss`. |
jobId | string | yes | Job `id` from GET (8–40 lowercase letters and digits). |
Sample body
{
"action": "dismiss",
"jobId": "k3j9x2m1q8"
}Sample response
{
"ok": true
}Possible errors
- 400 Invalid request body (message key in `msg`).
- 409 The job is still running or is no longer the latest one.
GET/services/{id}/minecraft/modpacksModpacks: state, search and preview
Without parameters it returns the installed modpack, set-aside files and the queue. `search` searches Modrinth, `project` returns a project with versions, `preview` shows what an install would do (changes nothing), `serverFiles=1` lists .mrpack files in the server root.
Permission: services:files
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
search | string | no | Search text on Modrinth. |
mc | string | no | With `search`: Minecraft version, e.g. `1.21.1`. |
loader | string | no | With `search`: `fabric`, `forge` or `neoforge`. |
sort | string | no | With `search`: `downloads`, `relevance` or `newest`. |
offset | integer | no | With `search`: offset in results. |
project | string | no | Project ID or slug; with `preview=modrinth` the ID only (8 characters). |
preview | string | no | `modrinth` (with `project` and `version`) or `upload` (with `upload`). |
version | string | no | With `preview=modrinth`: version ID (8 characters). |
upload | string | no | With `preview=upload`: `id` from the `stageServerFile` action (24 hex characters). |
mode | string | no | With `preview`: `all` (default), `keep-world` or `wipe`. |
serverFiles | string | no | `1` lists .mrpack files in the server root. |
Sample response
{
"installed": null,
"aside": null,
"recovering": false,
"server": {
"type": "fabric",
"version": "1.21.1",
"memoryMib": 8192
},
"perms": {
"install": true,
"rollback": true,
"deleteAside": true,
"wipe": true,
"replaceBackup": true
},
"queue": {
"active": 0,
"waiting": 0,
"limit": 2
},
"limits": {
"uploadMaxMb": 95,
"serverFileMaxMb": 1024,
"serverMb": 3072,
"files": 30000
}
}Possible errors
- 400 Invalid project, version, upload or mode (message key in `msg`).
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/modpacksInstall or roll back a modpack
`install` and `rollback` start a background job (response 202), follow it via minecraft/job. The server is stopped during install; `all` sets old files aside (undo with `rollback`), `wipe` deletes everything (owner only). Limit 3 per hour per service (shared with software changes) and 10 per day per customer; upload your own .mrpack via SFTP and stage it with `stageServerFile`.
Permission: services:files
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `install`, `rollback`, `deleteAside` or `stageServerFile`. |
origin | string | no | `install`: `modrinth` or `mrpack` (own file). |
projectId | string | no | `install` from Modrinth: project ID (8 characters). |
versionId | string | no | `install` from Modrinth: version ID (8 characters). |
uploadId | string | no | `install` with `mrpack`: `id` from `stageServerFile` (24 hex characters). |
mode | string | no | `install`: `all` (replace everything, set old aside), `keep-world` (keep world and server.properties) or `wipe` (delete everything, owner only). |
replaceBackup | boolean | no | If backup slots are full, overwrite the oldest backup (owner or Backups permission). |
acceptEula | boolean | no | Accept the Minecraft licence (EULA) as part of the install. |
confirmDowngrade | boolean | no | Confirm installing an older Minecraft version than the world has. |
name | string | no | `stageServerFile`: name of the .mrpack file in the server root (up to 1 GB). |
Sample body
{
"action": "install",
"origin": "modrinth",
"projectId": "AbCdEf12",
"versionId": "XyZ98765",
"mode": "all",
"acceptEula": true
}Sample response
{
"ok": true,
"job": {
"id": "k3j9x2m1q8",
"kind": "modpack",
"status": "queued",
"step": "queued",
"log": [],
"startedAt": "2026-11-07T10:15:00.000Z",
"updatedAt": "2026-11-07T10:15:00.000Z",
"error": null,
"errorMsg": null,
"progress": null
},
"warnings": []
}Possible errors
- 400 Invalid request body (message key in `msg`).
- 403 Settings, Files and Power permissions are missing (message key in `msg`).
- 403
WIPE_OWNEROnly the owner may use `wipe`. - 403
BACKUP_PERMISSION`replaceBackup` without the Backups permission. - 409
JOB_RUNNINGAnother job is already running on the server. - 409
ADDON_RUNNINGA plugin or mod installation is in progress. - 409
QUEUE_FULLThe install queue is full, try again later. - 409
DISKNot enough disk space on the server (`details.freeMb`, `details.needMb`). - 409
DOWNGRADEThe modpack uses an older Minecraft version, confirm with `confirmDowngrade`. - 422
LOADERThe modpack loader cannot be installed right now. - 429 Install limit exceeded (hourly per service or daily per customer).
GET/services/{id}/minecraft/player-statsPlayer count chart
Returns chart points of online players for the chosen range with average, peak and record. If collection is off, `enabled` is `false` and `points` is empty.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
range | string | no | `24h` (default, 5 min points), `7d` (hourly) or `30d` (every 4 h). |
Sample response
{
"enabled": true,
"collecting": true,
"range": "24h",
"canToggle": true,
"bucketSec": 300,
"points": [
{
"t": 1730973600000,
"peak": 4,
"avg": 2.5,
"slots": 20,
"state": "up"
}
],
"average": 2.1,
"peak": {
"online": 6,
"at": "2026-11-07T10:15:00.000Z"
},
"record": {
"online": 14,
"at": "2026-10-20T19:40:00.000Z"
},
"last": {
"at": "2026-11-07T10:15:00.000Z",
"online": 3,
"max": 20
}
}Possible errors
- 503
unavailableThe player chart is not available yet. - 502
ERRORThe chart could not be loaded. - 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/player-statsTurn the player chart on or off
Turns player count collection on or off. Turning it off deletes all collected data of the service (`deleted`). Limit 10 per 10 minutes.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | `true` to enable, `false` to disable and delete data. |
Sample body
{
"enabled": false
}Sample response
{
"ok": true,
"enabled": false,
"deleted": 2016
}Possible errors
- 400
INVALIDInvalid request body. - 503
unavailableThe player chart is not available yet. - 429 Too many requests, try again shortly.
GET/services/{id}/minecraft/playersPlayers, whitelist, operators and bans
Returns whitelist, operators, bans and IP bans from the server files, settings from server.properties and online players (Console permission only). With `lookup` it checks a nickname with Mojang.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lookup | string | no | Player nickname to check, returns `{ lookup: { query, found, name, uuid } }` (Console permission). |
Sample response
{
"power": "running",
"busy": null,
"canConsole": true,
"version": "1.21.4",
"settings": {
"whitelist": true,
"enforceWhitelist": false,
"onlineMode": true,
"opLevel": 4,
"propsMissing": false
},
"propsInvalid": false,
"floodgatePrefix": null,
"online": {
"online": 1,
"max": 20,
"players": [
{
"name": "Steve",
"uuid": "0f3c1e2a-4b5d-4c6e-8f70-1a2b3c4d5e6f"
}
],
"truncated": false,
"source": "console",
"at": "2026-11-07T10:15:00.000Z"
},
"onlineError": null,
"onlineErrorMsg": null,
"lists": {
"whitelist": {
"status": "ok",
"entries": [
{
"uuid": "0f3c1e2a-4b5d-4c6e-8f70-1a2b3c4d5e6f",
"name": "Steve"
}
]
},
"ops": {
"status": "ok",
"entries": [
{
"uuid": "0f3c1e2a-4b5d-4c6e-8f70-1a2b3c4d5e6f",
"name": "Steve",
"level": 4,
"bypassesPlayerLimit": false
}
]
},
"bans": {
"status": "absent",
"entries": []
},
"ipBans": {
"status": "absent",
"entries": []
}
},
"known": [],
"owner": true
}Possible errors
- 400
INVALID_NICKInvalid nickname in `lookup`. - 403
FORBIDDEN`lookup` without the Console permission. - 503
MOJANG_BUSYMojang lookup is unavailable now, try again in a minute. - 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/playersWhitelist, operators, bans and kick
On a running server the change is made by command, on a stopped one by editing the file (`mode`). On a shared service the Console permission is required; limit 30 changes per 10 minutes per service and 60 per customer.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `whitelist-add`, `whitelist-remove`, `whitelist-toggle`, `op`, `deop`, `ban`, `pardon`, `pardon-ip`, `kick`, `bedrock-whitelist-add` or `bedrock-whitelist-remove`. |
nick | string | no | Player nickname (up to 40 characters); for `bedrock-*` the Bedrock player name. |
uuid | string | no | UUID from the list, for removal on a stopped server even without a valid nickname. |
level | integer | no | `op`: operator level 1–4. |
bypassesPlayerLimit | boolean | no | `op`: the operator can join a full server. |
reason | string | no | `ban` and `kick`: reason (up to 200 characters). |
ip | string | no | `pardon-ip`: the banned IP address. |
enabled | boolean | no | `whitelist-toggle` (required): turn the whitelist on or off. |
Sample body
{
"action": "op",
"nick": "Steve",
"level": 4
}Sample response
{
"ok": true,
"mode": "command",
"result": "done",
"player": {
"name": "Steve",
"uuid": "0f3c1e2a-4b5d-4c6e-8f70-1a2b3c4d5e6f"
}
}Possible errors
- 400
INVALIDInvalid request body. - 400
INVALID_NICKInvalid nickname or UUID. - 400
INVALID_REASONInvalid reason. - 400
INVALID_IPInvalid IP address. - 409
NOT_RUNNINGThe action needs a running server (e.g. `kick`). - 409
PLAYER_OFFLINEThe player is not online. - 409
NO_FLOODGATE`bedrock-*` actions need Floodgate. - 409
CONFLICTThe file changed in the meantime, try again. - 409
JOBAnother change is running on the server. - 422
PLAYER_UNKNOWNNo player with this nickname exists. - 429
RATE_LIMITEDChange limit exceeded, try again later. - 503
MOJANG_BUSYMojang lookup is unavailable now.
GET/services/{id}/minecraft/pluginsServer plugins and mods
Without parameters it lists installed plugins or mods and `restartRequired`. The switches `search`, `versions`, `plan` and `updates` need no value (e.g. `?search&source=modrinth&q=maps`); `op` returns the state of a background install.
Permission: services:files
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
search | flag | no | Catalogue search (with `source`, `q`, `offset`). |
versions | flag | no | Compatible versions of a project (with `source`, `project`). |
plan | flag | no | Install preview with dependencies, nothing is written (with `source`, `project`, `version`, `deps`). |
updates | flag | no | Newer versions of files installed via the portal. |
source | string | no | `modrinth` or `hangar` (Hangar for plugins only). |
q | string | no | Search text. |
offset | integer | no | Offset in search results. |
project | string | no | Project ID or slug (1–64 characters). |
version | string | no | With `plan`: version ID, otherwise the newest compatible one. |
deps | string | no | With `plan`: `0` without dependencies. |
op | string | no | Operation ID (UUID) from a POST 202 response. |
Sample response
{
"context": {
"type": "paper",
"kind": "plugins",
"gameVersion": "1.21.4",
"versionKnown": true,
"versionSource": "metadata",
"dir": "/plugins"
},
"sources": [
"modrinth",
"hangar"
],
"maxBytes": 104857600,
"items": [
{
"file": "ExamplePlugin-1.2.0.jar",
"filename": "ExamplePlugin-1.2.0.jar",
"enabled": true,
"size": 512000,
"modifiedAt": 1730970000,
"managed": true,
"source": "modrinth",
"projectId": "AbCdEf12",
"title": "Example Plugin",
"icon": null,
"url": "https://modrinth.com/plugin/example-plugin",
"versionId": "XyZ98765",
"versionName": "1.2.0",
"publishedAt": "2026-11-07T10:15:00.000Z",
"installedAt": "2026-11-07T10:15:00.000Z",
"installedByYou": true,
"dependencyOf": null,
"unverified": true,
"fromModpack": null,
"dataFolder": "ExamplePlugin"
}
],
"dataFolders": [
"ExamplePlugin"
],
"folderExists": true,
"changedAt": "2026-11-07T10:15:00.000Z",
"restartRequired": true,
"serverState": "running",
"jobRunning": false,
"op": null
}Possible errors
- 400
BAD_REQUESTUnknown source, project or version. - 400
UNSUPPORTED_TYPEThe server type supports neither plugins nor mods (Vanilla). - 404
OP_NOT_FOUNDThe operation does not exist or has expired. - 429
RATE_LIMITEDCatalogue query limit: 60 per minute. - 502
SOURCE_ERRORThe catalogue is not responding.
POST/services/{id}/minecraft/pluginsInstall and manage plugins and mods
`install` and `update` run in the background: the final response arrives within 40 s, otherwise 202 with `pending: true` and `op`; then read the state via `GET ?op=`. Changes take effect after a server restart; limit 20 changes per 10 minutes per service.
Permission: services:files
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `install`, `update`, `remove` or `toggle`. |
source | string | no | `install`: `modrinth` or `hangar`. |
project | string | no | `install`: project ID or slug. |
version | string | no | `install` and `update`: version ID, otherwise the newest compatible one. |
dependencies | boolean | no | `install`: also install required dependencies, default `true`. |
skip | string[] | no | `install`: dependency IDs to skip (up to 50). |
replace | boolean | no | `install`: replace conflicting files, default `false`. |
file | string | no | `update`, `remove`, `toggle`: file name from the listing (up to 255 characters). |
dataFolder | string | no | `remove`: also delete the plugin data folder (name from `dataFolders`). |
enabled | boolean | no | `toggle` (required): enable or disable the file. |
Sample body
{
"action": "install",
"source": "modrinth",
"project": "example-plugin",
"dependencies": true
}Sample response
{
"ok": true,
"op": {
"id": "3f1c2a7e-9b4d-4e21-8c3a-5d6e7f809a1b",
"action": "install",
"status": "done",
"step": "Hotovo",
"stepMsg": {
"key": "plugin.step.done"
},
"startedAt": "2026-11-07T10:15:00.000Z",
"finishedAt": "2026-11-07T10:15:12.000Z",
"result": {
"installed": [
{
"file": "ExamplePlugin-1.2.0.jar",
"title": "Example Plugin",
"versionName": "1.2.0",
"role": "target"
}
],
"removed": [],
"skipped": [],
"missing": []
},
"error": null,
"errorMsg": null,
"code": null,
"data": null
},
"result": {
"installed": [
{
"file": "ExamplePlugin-1.2.0.jar",
"title": "Example Plugin",
"versionName": "1.2.0",
"role": "target"
}
],
"removed": [],
"skipped": [],
"missing": []
}
}Possible errors
- 400
BAD_REQUESTInvalid request body or project/version reference. - 404
NOT_FOUNDFile or project not found. - 404
NO_VERSIONNo compatible version. - 409
JOB_RUNNINGAnother change is running on the server. - 409
INSTALLINGThe server is being installed. - 409
EXISTSThe file already exists, use `replace`. - 409
UP_TO_DATEThe file is already up to date. - 413
TOO_LARGEThe file is larger than 100 MB. - 422
INCOMPATIBLEThe version does not fit this server. - 422
EXTERNALThe project can only be downloaded from the author's page. - 429
RATE_LIMITEDLimit of 20 changes per 10 minutes. - 502
SOURCE_ERRORThe catalogue is not responding.
GET/services/{id}/minecraft/propertiesContents of server.properties
Returns server.properties as text and as a key list with an `etag` for saving. Secret values (RCON and Management Protocol passwords) are masked; `reveal` shows one, only with the Secrets permission.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
reveal | string | no | `rcon.password`, `management-server-secret` or `management-server-tls-keystore-password`; returns `{ key, value }`. |
Sample response
{
"locked": [
"server-ip",
"server-port",
"query.port"
],
"secretKeys": [
"rcon.password",
"management-server-secret",
"management-server-tls-keystore-password"
],
"privilegedKeys": [
"enable-rcon",
"rcon.port",
"management-server-enabled",
"management-server-host",
"management-server-port",
"management-server-tls-enabled",
"management-server-tls-keystore",
"management-server-allowed-origins"
],
"allocationPorts": [
25565,
25566
],
"canSecrets": true,
"canPower": true,
"jobActive": false,
"exists": true,
"etag": "9b1f0c2d4e6a8b0c1d2e3f4a5b6c7d8e9f0a1b2c",
"raw": "motd=A Minecraft Server\nmax-players=20\nrcon.password=********\n",
"entries": [
{
"key": "motd",
"value": "A Minecraft Server"
},
{
"key": "max-players",
"value": "20"
},
{
"key": "rcon.password",
"value": "********"
}
]
}Possible errors
- 400 Unknown key in `reveal`.
- 403 `reveal` without the Secrets permission.
- 422 The file is too large or not text.
- 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/propertiesSave server.properties
Saves single keys (`fields`) or the whole file (`raw`), always with the `etag` from GET. Locked keys (IP and ports) stay unchanged; secrets and remote management need the Secrets permission. Changes apply after a restart; `restart: true` restarts right away (Power permission).
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
etag | string | yes | `etag` from GET; `absent` if the file does not exist. |
fields | object | no | Key to value (string, number or boolean). Not together with `raw`. |
raw | string | no | Whole file content (up to 80,000 characters, result up to 64 KB). Locked and secret keys are reset to their stored value. |
create | boolean | no | Create the file from defaults if it does not exist yet. |
restart | boolean | no | Restart the running server after saving. |
Sample body
{
"etag": "9b1f0c2d4e6a8b0c1d2e3f4a5b6c7d8e9f0a1b2c",
"fields": {
"motd": "Vítejte",
"max-players": 30
},
"restart": true
}Sample response
{
"ok": true,
"unchanged": false,
"etag": "0c1d2e3f4a5b6c7d8e9f0a1b2c9b1f0c2d4e6a8b",
"changed": [
"motd",
"max-players"
],
"restored": [],
"restarted": true,
"restartError": null,
"restartErrorMsg": null
}Possible errors
- 400 Invalid request body (message key in `msg`).
- 400
UNKNOWN_KEYThe key cannot be changed via `fields` (field `key`). - 400
LOCKEDThe key is locked (field `key`). - 400
INVALIDInvalid key value (field `key`). - 400
RAW_INVALIDThe file text is too long or contains forbidden characters. - 403
SECRETSChanging a secret or remote management without the Secrets permission. - 403 `restart` without the Power permission.
- 409
CONFLICTThe file changed meanwhile, fetch a new `etag`. - 409
NO_FILEThe file does not exist, send `create: true`. - 409 A change (job) is running on the server.
- 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
GET/services/{id}/minecraft/softwareServer software and available versions
Without parameters it returns the server type and version, change history, running job, backup slots, EULA and permissions. `versions` and `builds` list versions and builds of a type, `preview` shows the impact of a change (Java, image, disk, warnings) without writing.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
versions | string | no | Server type: `paper`, `purpur`, `folia`, `vanilla`, `fabric`, `forge` or `neoforge`; returns up to 250 versions. |
prerelease | string | no | With `versions`: `1` includes pre-releases. |
builds | string | no | Server type; with `version` returns up to 100 builds. |
preview | string | no | Server type for the change preview (with `version`, `build`). |
version | string | no | Minecraft version (for `preview` default `latest`). |
build | string | no | With `preview`: a specific build or loader version. |
unstable | string | no | `1` allows unstable builds (`builds`, `preview`). |
Sample response
{
"state": {
"type": "paper",
"version": "1.21.4",
"build": "232",
"loaderVersion": null,
"javaMajor": 21,
"launch": {
"kind": "jar",
"file": "server.jar"
},
"history": [],
"importedAt": null,
"installedAt": "2026-11-07T10:15:00.000Z"
},
"job": null,
"jobActive": false,
"server": {
"memoryMib": 8192,
"diskMib": 40960,
"javaMajor": 21,
"imageOfficial": true,
"busy": null
},
"panelError": null,
"clientError": null,
"backups": {
"used": 1,
"limit": 3
},
"diskFreeMb": 35200,
"diskFreeUnknown": false,
"eula": {
"accepted": true,
"acceptedAt": "2026-11-07T10:15:00.000Z"
},
"importSuggestion": null,
"perms": {
"change": true,
"import": true,
"eula": true,
"power": true,
"skipBackup": true
},
"types": [
"paper",
"purpur",
"folia",
"vanilla",
"fabric",
"forge",
"neoforge"
]
}Possible errors
- 400 Unknown type, version or build (message key in `msg`).
- 422 The version or build cannot be found.
- 502 The version source is not responding.
- 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/softwareChange server type and version
`switch` and `reinstall` start a job (202), follow it via minecraft/job: the server is stopped, backed up, the new software is installed and a server that was running is started again. A free backup slot is needed (or `skipBackup`, owner only); at most 3 per hour. `import` only records which software is already on the server and changes nothing.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `switch`, `reinstall` or `import`. |
type | string | no | `switch` and `import`: `paper`, `purpur`, `folia`, `vanilla`, `fabric`, `forge` or `neoforge`. |
version | string | no | `switch` and `import`: Minecraft version (1–40 characters); `switch` also accepts `latest`. |
build | string | no | Build or loader version, otherwise the newest stable one. |
allowUnstable | boolean | no | `switch`: allow an unstable build. |
mode | string | no | `switch`: `keep` (default, delete nothing), `clean` (delete everything except world, settings and player lists) or `wipe` (delete everything). |
acceptEula | boolean | no | `switch` and `reinstall`: accept the EULA. |
confirmDowngrade | boolean | no | `switch`: confirm switching to an older or unknown version. |
skipBackup | boolean | no | `switch` and `reinstall`: without a backup, owner only. |
Sample body
{
"action": "switch",
"type": "paper",
"version": "1.21.4",
"mode": "keep",
"acceptEula": true
}Sample response
{
"ok": true,
"job": {
"id": "k3j9x2m1q8",
"kind": "switch",
"status": "queued",
"step": "queued",
"log": [],
"startedAt": "2026-11-07T10:15:00.000Z",
"updatedAt": "2026-11-07T10:15:00.000Z",
"error": null,
"errorMsg": null,
"progress": null
},
"warnings": []
}Possible errors
- 400 Invalid request body (message key in `msg`).
- 403 A change needs the Settings, Files and Power permissions.
- 403
SKIP_BACKUP_OWNEROnly the owner may use `skipBackup`. - 409
JOB_RUNNINGAnother job is already running on the server. - 409
ADDON_RUNNINGA plugin or mod installation is in progress. - 409
BACKUP_SLOTBackup slots are full. - 409
BACKUP_DISABLEDThe service has no backups; the change only works with `skipBackup`. - 409
DISKNot enough disk space (`details.freeMb`, `details.needMb`). - 409
DOWNGRADEOlder version, confirm with `confirmDowngrade`. - 409
UNKNOWN_VERSIONThe current version is unknown, confirm with `confirmDowngrade`. - 409
NOT_IMPORTED`reinstall` without known software, use `import` or `switch` first. - 409
INSTALLINGThe server is installing or restoring a backup. - 422
NEED_BUILD`import`: for Forge and NeoForge provide `build`. - 429 At most 3 changes per hour.
GET/services/{id}/minecraft/startupStartup command and memory
Returns the current startup command, heap size, available JVM presets with heap preview, stored state and the out-of-memory risk. With `java=1` it also lists the Java versions the server offers.
Permission: services:settings
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
java | string | no | `1` adds `javaOptions` (Java versions the egg offers). |
Sample response
{
"type": "paper",
"imported": true,
"javaMajor": 21,
"arch": "x86_64",
"envPrefix": true,
"memoryMib": 8192,
"launch": {
"kind": "jar",
"file": "server.jar"
},
"command": "java -Xms6656M -Xmx6656M -XX:+UseG1GC -jar server.jar nogui",
"heapMb": 6656,
"addons": 12,
"modpack": false,
"presets": [
{
"preset": "standard",
"heapMb": 6656,
"isDefault": true,
"vectors": true
},
{
"preset": "zgc",
"heapMb": 5888,
"isDefault": false,
"vectors": true
},
{
"preset": "none",
"heapMb": 6656,
"isDefault": false,
"vectors": true
}
],
"defaultPreset": "standard",
"vectors": true,
"memory": null,
"stored": {
"preset": "standard",
"heapMb": 6656,
"memoryMib": 8192,
"vectors": true,
"extraFlags": [],
"appliedAt": "2026-11-07T10:15:00.000Z",
"custom": false,
"hasPrevious": true,
"check": null
},
"lastSync": null,
"managed": true,
"matchesStored": true,
"drifted": false,
"needsSync": false,
"busy": null,
"jobActive": false,
"canPower": true,
"canExtraFlags": true,
"canJava": true
}Possible errors
- 502 The game panel is not responding.
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/startupChange startup command or Java
Without `action` (or with `apply`) it applies a JVM preset, `recalculate` recomputes the heap for the current memory, `revert` restores the previous command, `java` switches the image to another Java without reinstalling. The change applies from the next start; `restart: true` restarts now (Power permission).
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | no | `apply` (default), `recalculate`, `revert` or `java`. |
preset | string | no | `apply` (required): `standard`, `zgc` (Java 21+), `proxy` or `none`. |
extraFlags | string | string[] | no | `apply`: custom JVM flags (text up to 4000 characters or up to 60 items); new flags only with the Power permission. |
dryRun | boolean | no | `apply`: only build the command and save nothing. |
major | integer | no | `java` (required): Java major version 8–99. |
restart | boolean | no | Restart after the change (start an offline server). |
Sample body
{
"preset": "standard",
"extraFlags": [],
"restart": true
}Sample response
{
"ok": true,
"command": "java -Xms6656M -Xmx6656M -XX:+UseG1GC -jar server.jar nogui",
"heapMb": 6656,
"preset": "standard",
"restarted": true,
"restartError": null,
"restartErrorMsg": null
}Possible errors
- 400 Invalid request body (message key in `msg`).
- 403
EXTRA_FLAGS_PERMISSIONNew custom flags without the Power permission (field `token`). - 403 `restart` or `java` without the required permissions.
- 409
NEED_IMPORTThe server software is not set. - 409
NO_MEMORY_LIMITThe server has no memory limit. - 409
CHANGEDThe startup changed meanwhile. - 409 A software change or install is running.
- 422
PRESETThe preset does not fit this Java. - 422
EXTRA_FLAGSForbidden flag (field `token`). - 422
TOO_OLD`java`: the version is too old for the Minecraft version. - 422
NO_IMAGE`java`: no image for this Java. - 429 Limit: preset changes 10 per hour, Java switches 3 per hour.
GET/services/{id}/minecraft/textColoured nick and prefix: options
Returns whether the server is running, the text syntax era for its version and which plugins (EssentialsX, LuckPerms, TAB) are installed. On a shared service the Console permission is required.
Permission: services:settings
Sample response
{
"running": true,
"state": "running",
"version": "1.21.4",
"era": "components",
"eraKnown": true,
"plugins": {
"essentials": true,
"luckperms": true,
"tab": false
}
}Possible errors
- 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
- 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/textApply coloured text on the server
Builds a command on the server from `spans` or `doc` (EssentialsX nick, LuckPerms or TAB prefix, name tag, tellraw) and sends it to the running server console. Limit 20 per 10 minutes.
Permission: services:settings
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `essentials-nick`, `lp-prefix`, `tab-prefix`, `give-nametag` or `tellraw`. |
target | string | yes | Player or group (1–36 characters); for `tellraw` also `@a`. |
targetKind | string | no | `lp-prefix` and `tab-prefix`: `group` (default) or `player`. |
priority | integer | null | no | `lp-prefix`: priority 0–1000. |
prefixFormat | string | no | `lp-prefix`: `ampHex` (default), `amp`, `minimessage` or `bungeeHex`. |
tabField | string | no | `tab-prefix`: `tabprefix` (default) or `tagprefix`. |
tabFormat | string | no | `tab-prefix`: `tabGradient` (default), `ampHex` or `minimessage`. |
era | string | no | Syntax era (`legacy`, `json`, `jsonHex`, `components`, `snbt`), used only when the server version is unknown. |
spans | object[] | no | Spans `{ text, color: "#rrggbb" | null, bold?, italic?, underlined?, strikethrough?, obfuscated? }`, up to 200. Send either `spans` or `doc`. |
doc | object | no | `{ segments: [{ text, fill, … }] }` where `fill` is `{ kind: "solid", color }`, `{ kind: "gradient", colors: [2–5 colours] }` or `{ kind: "rainbow" }`. |
Sample body
{
"action": "lp-prefix",
"target": "vip",
"targetKind": "group",
"priority": 50,
"doc": {
"segments": [
{
"text": "[VIP] ",
"fill": {
"kind": "solid",
"color": "#ffaa00"
},
"bold": true
}
]
}
}Sample response
{
"ok": true,
"command": "lp group vip meta setprefix 50 \"&#FFAA00&l[VIP] \"",
"warnings": [],
"delivered": null
}Possible errors
- 400
INVALIDInvalid request body. - 400
TARGETInvalid player or group. - 400
PRIORITYPriority must be an integer 0–1000. - 400
EMPTYThe text is empty. - 400
TOO_LONGThe text is too long (nick up to 64 characters, others 256). - 400
TOO_MANY_PARTSThe text has too many coloured parts. - 409
JOBA change (job) is running on the server. - 409 The server is not running.
- 429
RATE_LIMITEDChange limit exceeded, try again later. - 412 The server is not available in the game panel right now (message `panel.unavailable` in `msg`).
GET/services/{id}/minecraft/worldsServer worlds and prepared downloads
Returns the active and saved worlds (up to 5), prepared downloads, the running world job, backup slots and server state.
Permission: services:files
Sample response
{
"active": "world",
"activeValid": true,
"worlds": [
{
"name": "world",
"active": true,
"exists": true,
"extraFolders": [],
"version": "1.21.4",
"mtime": 1730970000,
"seed": null,
"createdAt": null
},
{
"name": "creative",
"active": false,
"exists": true,
"extraFolders": [],
"version": "1.21.4",
"mtime": 1730800000,
"seed": "12345",
"createdAt": "2026-11-01T18:00:00.000Z"
}
],
"unchecked": 0,
"maxWorlds": 5,
"exports": [
{
"id": "a1b2c3d4e5",
"world": "world",
"status": "ready",
"createdAt": "2026-11-07T10:15:00.000Z",
"expiresAt": "2026-11-07T12:15:00.000Z",
"bytes": 104857600,
"error": null,
"errorMsg": null
}
],
"job": null,
"jobOther": false,
"backups": {
"used": 1,
"limit": 3,
"free": true
},
"power": "running",
"busy": null,
"version": "1.21.4",
"uploadMaxMb": 95,
"canPower": true
}Possible errors
- 412
NO_PANELThe server is not available in the game panel right now. - 429 Too many requests, try again shortly.
POST/services/{id}/minecraft/worldsExport, switch, delete or reset a world
`export` prepares a world archive in the background (202), download it via worlds/download, at most 3 per hour. `switch` and `reset` start a job (202, progress via minecraft/job); the server is stopped and started again; `delete` permanently deletes a saved world. Except `export`, the Power permission is also needed; at most 10 changes per hour.
Permission: services:files
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | `export`, `switch`, `delete` or `reset`. |
world | string | no | `export`, `switch`, `delete`: world name from the listing (1–64 characters). |
old | string | no | `reset` (required): `delete` the old world or `keep` it as a saved world. |
keepAs | string | no | `reset` with `keep`: new name for the old world (1–32 characters A–Z, a–z, 0–9, _ and -). |
seed | string | no | `reset`: seed of the new world (up to 128 characters), empty means random. |
levelType | string | no | `reset`: `normal` (default), `flat`, `large_biomes` or `amplified`. |
structures | boolean | no | `reset`: generate structures, default `true`. |
confirmNoBackup | boolean | no | `reset` with `delete`: confirm deleting without a backup when no backup slot is free. |
Sample body
{
"action": "reset",
"old": "keep",
"keepAs": "stary_svet",
"seed": "",
"levelType": "normal",
"structures": true
}Sample response
{
"ok": true,
"job": {
"id": "k3j9x2m1q8",
"kind": "world-reset",
"status": "queued",
"step": "queued",
"log": [],
"startedAt": "2026-11-07T10:15:00.000Z",
"updatedAt": "2026-11-07T10:15:00.000Z",
"error": null,
"errorMsg": null,
"progress": null
}
}Possible errors
- 400
INVALIDInvalid request body. - 400
BAD_NAMEInvalid world name. - 400
RESERVED_NAMEThe name belongs to a server folder. - 400
BAD_SEEDInvalid seed. - 403
FORBIDDENThe Power permission is missing. - 404
NO_SUCH_WORLDThe world is not on the server. - 409
ACTIVE_WORLDThe world is currently active. - 409
NAME_TAKENA folder with this name already exists. - 409
TOO_MANY_WORLDSA server can have at most 5 worlds. - 409
NEEDS_CONFIRMThe old world would be deleted without a backup, send `confirmNoBackup: true`. - 409
JOB_RUNNINGAnother change is already running on the server. - 409
EXPORT_RUNNINGA world download is already being prepared. - 409
DISKNot enough disk space on the server. - 409
STATEThe server is starting or stopping. - 429
RATE_LIMITEDChange limit exceeded, try again later. - 412
NO_PANELThe server is not available in the game panel right now.
GET/services/{id}/minecraft/worlds/downloadDownload a prepared world
Downloads an archive prepared by the `export` action (ZIP or tar.gz). After a complete download the archive is deleted from the server; one download per service at a time, at most 10 per hour.
Permission: services:files
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | Download `id` from `exports` (10 hex characters). |
format | string | no | `zip` (default) or `tar.gz`. |
Sample response
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="world-2026-11-07.zip"
(binary archive)Possible errors
- 400
INVALID`id` is missing or invalid. - 404
EXPORT_GONEThe download is no longer prepared, prepare it again. - 429
DOWNLOAD_BUSYA download is already in progress. - 429
RATE_LIMITEDToo many downloads. - 412
NO_PANELThe server is not available in the game panel right now.
Domains
POST/services/{id}/domain/auth-infoSend AUTH-ID for domain transfer
Asks the registrar to e-mail the AUTH-ID (the code for transferring the domain to another registrar) to the domain holder. The code isn't returned in the response. Owner only, at most 3 times per hour. The services:secrets scope can only be given to a key restricted to IP addresses.
Permission: services:secrets
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"locale": "cs"
}Sample response
{
"ok": true
}Possible errors
- 400
invalidThe request body contains an unknown field. - 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 409
inactiveThe domain isn't active yet. - 429 Limit of 3 requests per hour exceeded.
- 503
unavailableSending is temporarily unavailable. Try again later. - 422
failedThe registrar rejected the request.
GET/services/{id}/domain/connectPlan connecting a domain to a service
Works out the DNS records that point the domain or a subdomain to your VPS or game server (Minecraft, FiveM) and shows what will be added and replaced in the zone. Changes nothing. For a domain not on our DNS it returns mode manual with records to set up yourself.
Permission: domains:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
service | string | yes | ID of your active VPS or game server (Minecraft, FiveM). Shared services aren't allowed. |
host | string | yes | Name in the domain: @ for the domain itself, or a subdomain (letters, digits, hyphens, no * or _). |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample response
{
"mode": "zone",
"domain": "example.com",
"host": "@",
"kind": "vps",
"records": [
{
"type": "A",
"host": "@",
"fqdn": "example.com",
"value": "203.0.113.25",
"ttl": 300
},
{
"type": "AAAA",
"host": "@",
"fqdn": "example.com",
"value": "2001:db8::25",
"ttl": 300
}
],
"notes": [],
"add": [
{
"type": "AAAA",
"host": "@",
"fqdn": "example.com",
"value": "2001:db8::25",
"ttl": 300
}
],
"replace": [
{
"type": "A",
"host": "@",
"fqdn": "example.com",
"value": "198.51.100.7",
"ttl": 3600
}
],
"unchanged": false,
"withinLimit": true,
"planHash": "3f9a0c1b2d4e5f60718293ab"
}Possible errors
- 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 409
inactiveThe domain isn't active yet. - 400
hostInvalid name: use @ for the domain itself, or a subdomain made of letters, digits and hyphens. - 404
serviceThe service doesn't exist, isn't yours, isn't active or can't be connected. - 409
no_addressThe service has no public address to point the domain to. - 503
unavailableThe service address or DNS state can't be read right now. - 429 Limit of 60 requests per 10 minutes exceeded.
POST/services/{id}/domain/connectConnect a domain to a service
Creates the planned records in the zone (A, AAAA and for Minecraft also SRV _minecraft._tcp, TTL 300). Send the planHash from GET; if the plan replaces existing records, confirm: true is required. Only for a domain on our DNS.
Permission: domains:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
serviceId | string | yes | ID of your active VPS or game server, same as in GET. |
host | string | yes | Name in the domain, same as in GET (@ or a subdomain). |
planHash | string | yes | Plan fingerprint from GET (24 hex characters). If the zone has changed in the meantime, you get 409 changed with a new plan. |
confirm | boolean | no | Confirms that the existing records in replace will be replaced. Required when replace isn't empty. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"serviceId": "clx9k2m4p0000svc01abcdef",
"host": "@",
"planHash": "3f9a0c1b2d4e5f60718293ab",
"confirm": true
}Sample response
{
"ok": true,
"mode": "zone",
"domain": "example.com",
"host": "@",
"kind": "vps",
"records": [
{
"type": "A",
"host": "@",
"fqdn": "example.com",
"value": "203.0.113.25",
"ttl": 300
},
{
"type": "AAAA",
"host": "@",
"fqdn": "example.com",
"value": "2001:db8::25",
"ttl": 300
}
],
"notes": [],
"add": [],
"replace": [],
"unchanged": true,
"withinLimit": true,
"planHash": "3f9a0c1b2d4e5f60718293ab"
}Possible errors
- 400
invalidA required field is missing or malformed. - 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 409
inactiveThe domain isn't active yet. - 400
hostInvalid name: use @ for the domain itself, or a subdomain made of letters, digits and hyphens. - 404
serviceThe service doesn't exist, isn't yours, isn't active or can't be connected. - 409
no_addressThe service has no public address to point the domain to. - 503
unavailableThe service address or DNS state can't be read right now. - 409
manualThe domain doesn't use our DNS. Set the records up with your DNS provider. - 409
changedThe zone has changed since GET. The response contains the new plan in the plan field. - 409
confirmThe plan replaces existing records and confirm: true is missing. The response contains the plan in the plan field. - 409
limitThe zone would exceed the 200-record limit. - 429 Limit of 60 DNS changes per 10 minutes (shared with DNS record writes) exceeded.
GET/services/{id}/domain/dnsList DNS records
Returns the records of the domain's zone on our DNS (A, AAAA, CNAME, MX, TXT, SRV, CAA), the nameservers and the record count against the 200 limit. delegated: false means the zone is ready but the domain doesn't point to our DNS yet.
Permission: domains:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample response
{
"zone": "example.com",
"unicode": "example.com",
"records": [
{
"id": "a1b2c3d4e5f60718293a",
"type": "A",
"host": "@",
"ttl": 3600,
"value": "203.0.113.25",
"fields": {
"host": "@",
"ttl": "3600",
"address": "203.0.113.25"
}
},
{
"id": "b2c3d4e5f60718293a4b",
"type": "MX",
"host": "@",
"ttl": 3600,
"value": "10 mail.example.com",
"fields": {
"host": "@",
"ttl": "3600",
"preference": "10",
"exchange": "mail.example.com"
}
},
{
"id": "c3d4e5f60718293a4b5c",
"type": "TXT",
"host": "@",
"ttl": 3600,
"value": "v=spf1 mx -all",
"fields": {
"host": "@",
"ttl": "3600",
"text": "v=spf1 mx -all"
}
}
],
"nameservers": [
"ns1.stormhost.eu",
"ns2.stormhost.eu"
],
"count": 3,
"limit": 200,
"delegated": true
}Possible errors
- 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 400 The service isn't a domain.
- 409
inactiveThe domain isn't active yet. - 409
no_zoneThe domain has no zone on our DNS. Create it via domain/zone or switch the nameservers to ours. - 503
unavailableThe DNS server isn't responding right now. Try again later. - 422
failedThe change couldn't be made.
POST/services/{id}/domain/dnsAdd a DNS record
Adds a record to the domain's zone on our DNS. All values in record are sent as strings. The response contains the new record's ID and the full updated zone listing.
Permission: domains:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
record | object | yes | The new record. |
record.type | string | yes | Record type: A, AAAA, CNAME, MX, TXT, SRV or CAA. |
record.host | string | no | Name: @ or empty for the domain itself, a relative name (www, _dmarc, *.dev) or a full name ending with the domain. |
record.ttl | string | no | TTL in seconds as a string, 60 to 86400. Empty means 3600. |
record.address | string | no | A: IPv4 address, AAAA: IPv6 address. |
record.target | string | no | CNAME and SRV: target name (for SRV also . meaning no service). A CNAME can't be on the domain itself or next to another record with the same name. |
record.preference | string | no | MX: priority 0 to 65535 as a string. |
record.exchange | string | no | MX: mail server name (not an IP address). |
record.text | string | no | TXT: text up to 2000 characters, no control characters. |
record.priority | string | no | SRV: priority 0 to 65535 as a string. The SRV name must look like _service._protocol (tcp, udp, tls, sctp). |
record.weight | string | no | SRV: weight 0 to 65535 as a string. |
record.port | string | no | SRV: port 0 to 65535 as a string. |
record.flags | string | no | CAA: flag 0 or 128 as a string. |
record.tag | string | no | CAA: issue, issuewild or iodef. |
record.value | string | no | CAA: for issue and issuewild the certificate authority's domain, for iodef a mailto: or https: address. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"record": {
"type": "MX",
"host": "@",
"ttl": "3600",
"preference": "10",
"exchange": "mail.example.com"
}
}Sample response
{
"ok": true,
"id": "b2c3d4e5f60718293a4b",
"zone": "example.com",
"unicode": "example.com",
"records": [
{
"id": "a1b2c3d4e5f60718293a",
"type": "A",
"host": "@",
"ttl": 3600,
"value": "203.0.113.25",
"fields": {
"host": "@",
"ttl": "3600",
"address": "203.0.113.25"
}
},
{
"id": "b2c3d4e5f60718293a4b",
"type": "MX",
"host": "@",
"ttl": 3600,
"value": "10 mail.example.com",
"fields": {
"host": "@",
"ttl": "3600",
"preference": "10",
"exchange": "mail.example.com"
}
},
{
"id": "c3d4e5f60718293a4b5c",
"type": "TXT",
"host": "@",
"ttl": 3600,
"value": "v=spf1 mx -all",
"fields": {
"host": "@",
"ttl": "3600",
"text": "v=spf1 mx -all"
}
}
],
"nameservers": [
"ns1.stormhost.eu",
"ns2.stormhost.eu"
],
"count": 3,
"limit": 200,
"delegated": true
}Possible errors
- 400 The record failed validation. The code field says why (e.g. type, host, host_zone, cname_apex, srv_host, ipv4, ipv6, target, text, ttl, caa_flags, caa_tag, caa_value, cname_conflict, duplicate, limit) and field says which field is affected.
- 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 400 The service isn't a domain.
- 409
inactiveThe domain isn't active yet. - 409
no_zoneThe domain has no zone on our DNS. Create it via domain/zone or switch the nameservers to ours. - 503
unavailableThe DNS server isn't responding right now. Try again later. - 422
failedThe change couldn't be made. - 429 Limit of 60 changes per 10 minutes exceeded.
PATCH/services/{id}/domain/dnsUpdate a DNS record
Updates a record by its ID from the listing. Omitted or empty fields stay unchanged; the record type can't be changed. The ID is derived from the record's value, so after changing the name or value the response returns a new ID.
Permission: domains:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | Record ID from the listing (20 hex characters). |
record | object | yes | Fields to change (same as when adding, without type). |
record.host | string | no | Name: @ or empty for the domain itself, a relative name (www, _dmarc, *.dev) or a full name ending with the domain. |
record.ttl | string | no | TTL in seconds as a string, 60 to 86400. Empty means 3600. |
record.address | string | no | A: IPv4 address, AAAA: IPv6 address. |
record.target | string | no | CNAME and SRV: target name (for SRV also . meaning no service). A CNAME can't be on the domain itself or next to another record with the same name. |
record.preference | string | no | MX: priority 0 to 65535 as a string. |
record.exchange | string | no | MX: mail server name (not an IP address). |
record.text | string | no | TXT: text up to 2000 characters, no control characters. |
record.priority | string | no | SRV: priority 0 to 65535 as a string. The SRV name must look like _service._protocol (tcp, udp, tls, sctp). |
record.weight | string | no | SRV: weight 0 to 65535 as a string. |
record.port | string | no | SRV: port 0 to 65535 as a string. |
record.flags | string | no | CAA: flag 0 or 128 as a string. |
record.tag | string | no | CAA: issue, issuewild or iodef. |
record.value | string | no | CAA: for issue and issuewild the certificate authority's domain, for iodef a mailto: or https: address. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"id": "a1b2c3d4e5f60718293a",
"record": {
"address": "203.0.113.26",
"ttl": "600"
}
}Sample response
{
"ok": true,
"id": "d4e5f60718293a4b5c6d",
"zone": "example.com",
"unicode": "example.com",
"records": [
{
"id": "a1b2c3d4e5f60718293a",
"type": "A",
"host": "@",
"ttl": 3600,
"value": "203.0.113.25",
"fields": {
"host": "@",
"ttl": "3600",
"address": "203.0.113.25"
}
},
{
"id": "b2c3d4e5f60718293a4b",
"type": "MX",
"host": "@",
"ttl": 3600,
"value": "10 mail.example.com",
"fields": {
"host": "@",
"ttl": "3600",
"preference": "10",
"exchange": "mail.example.com"
}
},
{
"id": "c3d4e5f60718293a4b5c",
"type": "TXT",
"host": "@",
"ttl": 3600,
"value": "v=spf1 mx -all",
"fields": {
"host": "@",
"ttl": "3600",
"text": "v=spf1 mx -all"
}
}
],
"nameservers": [
"ns1.stormhost.eu",
"ns2.stormhost.eu"
],
"count": 3,
"limit": 200,
"delegated": true
}Possible errors
- 400 The record failed validation. The code field says why (e.g. type, host, host_zone, cname_apex, srv_host, ipv4, ipv6, target, text, ttl, caa_flags, caa_tag, caa_value, cname_conflict, duplicate, limit) and field says which field is affected.
- 404
not_foundNo record with this ID in the zone (it may have changed in the meantime). - 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 400 The service isn't a domain.
- 409
inactiveThe domain isn't active yet. - 409
no_zoneThe domain has no zone on our DNS. Create it via domain/zone or switch the nameservers to ours. - 503
unavailableThe DNS server isn't responding right now. Try again later. - 422
failedThe change couldn't be made. - 429 Limit of 60 changes per 10 minutes exceeded.
DELETE/services/{id}/domain/dnsDelete a DNS record
Deletes a record by its ID from the listing. The ID is sent in the JSON request body. The response contains the updated zone listing.
Permission: domains:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | Record ID from the listing (20 hex characters). |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"id": "c3d4e5f60718293a4b5c"
}Sample response
{
"ok": true,
"zone": "example.com",
"unicode": "example.com",
"records": [
{
"id": "a1b2c3d4e5f60718293a",
"type": "A",
"host": "@",
"ttl": 3600,
"value": "203.0.113.25",
"fields": {
"host": "@",
"ttl": "3600",
"address": "203.0.113.25"
}
},
{
"id": "b2c3d4e5f60718293a4b",
"type": "MX",
"host": "@",
"ttl": 3600,
"value": "10 mail.example.com",
"fields": {
"host": "@",
"ttl": "3600",
"preference": "10",
"exchange": "mail.example.com"
}
},
{
"id": "c3d4e5f60718293a4b5c",
"type": "TXT",
"host": "@",
"ttl": 3600,
"value": "v=spf1 mx -all",
"fields": {
"host": "@",
"ttl": "3600",
"text": "v=spf1 mx -all"
}
}
],
"nameservers": [
"ns1.stormhost.eu",
"ns2.stormhost.eu"
],
"count": 3,
"limit": 200,
"delegated": true
}Possible errors
- 400
invalidThe id is missing or malformed. - 404
not_foundNo record with this ID in the zone. - 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 400 The service isn't a domain.
- 409
inactiveThe domain isn't active yet. - 409
no_zoneThe domain has no zone on our DNS. Create it via domain/zone or switch the nameservers to ours. - 503
unavailableThe DNS server isn't responding right now. Try again later. - 422
failedThe change couldn't be made. - 429 Limit of 60 changes per 10 minutes exceeded.
POST/services/{id}/domain/nameserversChange the domain's nameservers
Changes the nameservers at the registry: ours = our DNS (the zone is created if missing), custom = your own servers (2 to 6), wedos = the registrar's default DNS. The zone on our DNS isn't deleted when you switch away, so you can switch back. The change takes effect worldwide only after the registry TTL expires.
Permission: domains:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
mode | string | yes | ours, custom or wedos. |
nameservers | array | no | Only for custom: 2 to 6 servers as { name, ipv4?, ipv6? }. |
nameservers[].name | string | yes | Server name, e.g. ns1.example.net. |
nameservers[].ipv4 | string | no | IPv4 address. Only for a server inside the domain itself (e.g. ns1.example.com for example.com), where at least one address is required. Ignored otherwise. |
nameservers[].ipv6 | string | no | IPv6 address, same rules as ipv4. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"mode": "custom",
"nameservers": [
{
"name": "ns1.example.com",
"ipv4": "203.0.113.53"
},
{
"name": "ns2.example.net"
}
]
}Sample response
{
"ok": true,
"mode": "custom",
"nsset": null,
"nameservers": [
"ns1.example.com",
"ns2.example.net"
]
}Possible errors
- 400
invalidInvalid request body (e.g. unknown mode). - 400
ns_countcustom needs 2 to 6 distinct servers. - 400
ns_nameInvalid server name. The index field says which one. - 400
ns_glueA server inside the domain needs an IPv4 or IPv6 address. - 400
ns_ipInvalid server IP address. - 400
ns_oursThe custom servers are only ours. Use mode ours. - 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 409
inactiveThe domain isn't active yet. - 429 Limit of 10 changes per 10 minutes exceeded.
- 503
unavailableThe registry or DNS server is unavailable right now. Try again later. - 422
failedThe registry rejected the change.
POST/services/{id}/domain/zonePrepare a zone on our DNS
Creates an empty zone for the domain on our DNS without changing the nameservers. Useful after a domain transfer: fill in the records via domain/dns first, then switch the NS. Records from the previous DNS aren't copied. If the zone already exists, it returns ok: true and existed: true.
Permission: domains:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"locale": "cs"
}Sample response
{
"ok": true
}Possible errors
- 400
invalidThe request body contains an unknown field. - 403 Only the domain owner can do this, not a member of a shared service.
- 404 The service doesn't exist, isn't yours, or the key has no access to it.
- 409
inactiveThe domain isn't active yet. - 429 Limit of 10 requests per 10 minutes exceeded.
- 503
not_readyDNS for domains isn't ready right now. Try again later. - 422
failedThe zone couldn't be created.
Billing and invoices
GET/creditCredit balance and history
Returns the credit balance in CZK, split into bonus, non-refundable and refundable parts, pending top-ups and the transaction history (50 per page, newest first). If credit isn't available yet, it returns only available: false.
Permission: billing:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page | integer | no | History page, starting at 1. Default is 1. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample response
{
"available": true,
"balanceCzk": 450,
"bonusCzk": 50,
"nonRefundableCzk": 0,
"refundableCzk": 400,
"pending": [
{
"orderId": "clx9k2m4p0001ord02abcdef",
"invoiceId": "clx9k2m4p0001inv02abcdef",
"number": "DK-2026-0007",
"amountCzk": 200,
"dueAt": "2026-11-14T23:59:59.000Z",
"createdAt": "2026-11-06T12:00:00.000Z",
"paymentStarted": false
}
],
"history": {
"items": [
{
"id": "clx9k2m4p0000ctx01abcdef",
"seq": 2,
"type": "SPEND",
"kind": null,
"amountCzk": -249,
"balanceAfterCzk": 450,
"createdAt": "2026-11-02T10:15:00.000Z",
"invoiceId": "clx9k2m4p0000inv01abcdef",
"invoiceNumber": "2026-0042",
"orderId": "clx9k2m4p0000ord01abcdef",
"note": null,
"refundRequestId": null
},
{
"id": "clx9k2m4p0000ctx00abcdef",
"seq": 1,
"type": "TOPUP",
"kind": null,
"amountCzk": 699,
"balanceAfterCzk": 699,
"createdAt": "2026-10-20T08:00:00.000Z",
"invoiceId": "clx9k2m4p0000inv00abcdef",
"invoiceNumber": "DK-2026-0005",
"orderId": "clx9k2m4p0000ord00abcdef",
"note": null,
"refundRequestId": null
}
],
"page": 1,
"pages": 1,
"total": 2
}
}Possible errors
- 429
rate_limitedLimit of 60 requests per minute exceeded.
POST/credit/payPay an invoice from credit
Pays an unpaid order invoice (e.g. a new service or an upgrade) from the account credit. The credit is deducted right away; setting up the service or applying the upgrade finishes after the response. Credit top-up invoices can't be paid this way.
Permission: billing:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
invoiceId | string | yes | ID of the unpaid invoice. |
expectedAmountCzk | number | yes | The invoice amount in CZK you expect. It must match the order price exactly, otherwise you get 409 amount_changed with the current amount. |
ackOpenGateway | boolean | no | Confirms you want to pay with credit even though a payment gateway payment is open for the invoice. Default is false. |
locale | string | no | Language of error messages: cs, en, sk, pl or de. Default is cs. |
Sample body
{
"invoiceId": "clx9k2m4p0000inv01abcdef",
"expectedAmountCzk": 249
}Sample response
{
"ok": true,
"orderId": "clx9k2m4p0000ord01abcdef",
"amountCzk": 249,
"balanceCzk": 201,
"next": "/cs/order/clx9k2m4p0000ord01abcdef?credit=paid"
}Possible errors
- 400
invalidA required field is missing or malformed. - 403
lockedThe account is temporarily locked. - 404
invoice_not_foundThe invoice doesn't exist or isn't yours. - 409
insufficientNot enough credit. The response contains balanceCzk and neededCzk. - 409
amount_changedThe amount has changed. The current one is in amountCzk. - 409
closedThe invoice is already paid or cancelled. - 409
expiredThe proforma invoice expired after its due date. - 409
upgrade_expiredThe upgrade offer was valid only until the end of the service period and has been cancelled. - 409
gateway_openA payment gateway payment is open for the invoice. Send ackOpenGateway: true. - 409
payment_in_progressA gateway payment is in progress. - 409
paid_unreconciledA payment has already arrived but isn't matched yet. - 409
topup_not_payableA credit top-up invoice can't be paid with credit. - 409
not_payableThe invoice doesn't belong to a payable order. - 409
capacityThere's no free capacity for the ordered service right now. - 409
billing_profile_incompleteThe account's billing details are missing. The response contains settingsUrl. - 409
foreign_eu_vatOrders from businesses in other EU countries are currently handled individually. - 429
rate_limitedLimit of 10 attempts per minute exceeded. - 503
unavailableCredit isn't available right now. - 503
gateway_check_failedThe gateway payment status can't be checked right now. Try again later. - 503
capacity_busyCapacity can't be checked right now. Try again shortly.
GET/invoicesList invoices
Returns the account's invoices, newest first, including line items. Amounts are in CZK.
Permission: billing:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | string | no | Filter by status: PENDING, PAID, CANCELLED or REFUNDED. Any other value is ignored. |
limit | integer | no | Number of invoices, 1 to 200. Default is 50. |
Sample response
{
"data": [
{
"id": "clx9k2m4p0000inv01abcdef",
"number": "2026-0042",
"status": "PAID",
"totalCzk": 249,
"currency": "CZK",
"items": [
{
"description": "VPS Start (1 měsíc)",
"priceCzk": 249,
"quantity": 1
}
],
"dueAt": "2026-11-14T23:59:59.000Z",
"paidAt": "2026-11-02T10:15:00.000Z",
"createdAt": "2026-11-01T08:00:00.000Z"
}
]
}GET/invoices/{id}/pdfInvoice PDF
Downloads the PDF of a paid or refunded invoice. Unpaid documents have no PDF.
Permission: billing:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | en gives an English file name, otherwise Czech. |
Sample response
application/pdf (Content-Disposition: attachment; filename="faktura-2026-0042.pdf")Possible errors
- 404
not_foundThe invoice doesn't exist, isn't yours or isn't paid. - 409
preparingThe invoice is still being issued. Try again later. - 429
rate_limitedLimit of 20 downloads per minute exceeded. - 503
not_readyThe PDF is being prepared. Try again in a few seconds (Retry-After header). - 503
unavailableThe PDF can't be loaded right now. Try again later.
GET/services/{id}/external-invoices/{key}/pdfPDF of a recurring-billing invoice
Downloads the PDF of an invoice for a service you pay by recurring invoice outside the portal. {key} is the 22-character invoice key from the service's payment overview in the portal. Service owner only (VPS, game server, web hosting).
Permission: billing:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language of the file name: cs, en, sk, pl or de. Default is cs. |
Sample response
application/pdf (Content-Disposition: attachment; filename="faktura-2026-0042.pdf")Possible errors
- 404
not_foundThe invoice or service doesn't exist, isn't yours, or you aren't the owner. The response is always the same. - 429
rate_limitedLimit of 20 downloads per minute exceeded. - 503
not_readyThe PDF is being prepared. Try again in a few seconds (Retry-After header). - 503
unavailableThe PDF can't be loaded right now. Try again later.
GET/services/{id}/game-upgradePlan change options for a game or bot
Returns the current package of a game server or Discord bot, bigger and smaller packages with prices and the extra charge, the backup add-on and pending or scheduled changes. Owner only.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
view | string | no | restart = returns only { restartPending } (whether the server waits for a restart after a plan change); also works for members. |
locale | string | no | Language of texts in the response: en, otherwise cs. |
Sample response
{
"product": "minecraft",
"productName": "Minecraft",
"current": {
"planLabel": "Minecraft 4 GB",
"packageId": "mc-4gb",
"legacy": false,
"memoryMib": 4096,
"diskMib": 30720,
"cpu": 200,
"databases": 1,
"allocations": 2,
"backups": 0
},
"backups": {
"included": 0,
"addon": 0,
"grandfathered": 0,
"addonAvailable": true,
"addonPriceCzk": 120,
"addonUnitCzk": 0,
"maxAddon": 1
},
"monthlyCzk": 249,
"period": {
"start": "2026-11-01T00:00:00.000Z",
"end": "2026-12-01T00:00:00.000Z",
"remainingDays": 24,
"periodDays": 30
},
"packages": [
{
"id": "mc-6gb",
"name": "Minecraft 6 GB",
"cores": 3,
"cpuUnlimited": false,
"ramGb": 6,
"diskGb": 45,
"ports": 3,
"databases": 2,
"includedBackups": 0,
"priceCzk": 349,
"recommended": true,
"fits": true,
"message": null
}
],
"options": [
{
"packageId": "mc-6gb",
"addon": 0,
"monthlyCzk": 349,
"chargeCzk": 80,
"after": {
"memoryMib": 6144,
"diskMib": 46080,
"cpu": 300,
"databases": 2,
"allocations": 3,
"backups": 0
}
}
],
"effectiveAt": "2026-12-01T00:00:00.000Z",
"lowerPackages": [],
"downgradeOptions": [],
"removeBackup": {
"available": false
},
"downgradeBlocker": null,
"customStartup": false,
"scheduled": null,
"lastChange": null,
"nextPeriodPriceCzk": 249,
"pendingOrder": null,
"upgradeInProgress": false,
"blocker": null,
"restartPending": false,
"recurringMaxCzk": null,
"billingIncomplete": false,
"customerType": "consumer"
}Possible errors
- 403 Only for the service owner.
- 409
unavailableThe plan cannot be changed right now (unknown package or service state). - 503 The game panel is not responding right now.
POST/services/{id}/game-upgradeOrder a bigger or schedule a smaller plan
Without action (or with action order) it creates an order for a bigger package or the backup add-on with a proforma invoice; the change is applied after payment. A smaller package is scheduled from the next period, nothing is charged or refunded. Owner only.
Permission: billing:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | no | order (default), cancelPending (cancel the unpaid order), scheduleDowngrade (smaller plan from the next period) or cancelScheduled (cancel the scheduled change). |
packageId | string | null | no | order, scheduleDowngrade: target package id from GET (packages, lowerPackages). Without it the current package stays. |
backups | integer | no | order, scheduleDowngrade: number of backup add-ons, 0 or 1. |
immediateStartRequested | boolean | no | order: a consumer requests the change to start immediately (terms art. 9.8). Must be true for accounts without a company ID. |
businessPurpose | boolean | no | order: declaration that the order is for business purposes. |
expectedChargeCzk | number | null | no | order: the extra charge you saw. If it differs, 409 price_changed is returned and nothing is created. |
expectedMonthlyCzk | number | null | no | order, scheduleDowngrade: the new monthly price you saw. If it differs, 409 price_changed is returned. |
changeId | string | no | cancelScheduled: id of the scheduled change (scheduled.id from GET); if it does not match, 409 changed is returned. |
locale | string | no | Language of messages: cs, en, sk, pl or de. Default cs. |
Sample body
{
"packageId": "mc-6gb",
"backups": 0,
"immediateStartRequested": true,
"expectedChargeCzk": 80,
"expectedMonthlyCzk": 349,
"locale": "cs"
}Sample response
{
"orderId": "ord_7Hq2mX9pLk",
"chargeCzk": 80,
"monthlyToCzk": 349,
"scheduledCancelled": false
}Possible errors
- 400 Invalid request or a package that cannot be chosen.
- 400
immediate_start_requiredA consumer must send immediateStartRequested: true. - 403 Only for the service owner.
- 404
nonecancelScheduled: there is no scheduled change. - 409
billing_profile_incompleteThe account billing details are incomplete. - 409
pending_orderAn unpaid upgrade order already exists (orderId in the response). - 409
upgrade_in_progressA paid upgrade is still being applied. - 409
price_changedThe price has changed; the response includes newChargeCzk and newMonthlyCzk. - 409
capacityThe bigger package does not fit on the server. - 409
does_not_fitscheduleDowngrade: the data does not fit into the smaller package (see reasons). - 409
transfer_pendingThe service is waiting for a transfer. - 409
refund_openA withdrawal or refund is open for the service. - 409
renewal_paidThe next period is paid at the price of the scheduled smaller package. - 409
change_applyingThe smaller package is being applied right now. - 409
too_latecancelScheduled: the change has already taken effect. - 503
price_list_unavailableThe price list cannot be loaded right now; nothing is charged.
GET/services/{id}/order-upgradeVPS upgrade offer
Returns the current VPS configuration, monthly price, rates, the highest possible values based on free capacity, the billing period and any unpaid upgrade order. Owner only.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | en for English texts, otherwise Czech. |
Sample response
{
"tier": "vps-performance",
"tierName": "VPS Performance",
"current": {
"cores": 2,
"ramGb": 4,
"diskGb": 60,
"ipv4": 1
},
"monthlyCzk": 390,
"rates": {
"perCore": 0,
"perGbRam": 95,
"perGbDisk": 0,
"perIpv4": 60
},
"priceList": {
"fp": "a1b2c3d4",
"rates": {
"perCore": 0,
"perGbRam": 95,
"perGbDisk": 0,
"perIpv4": 60
},
"packages": [],
"diskAddon": {
"perGb": 2,
"stepGb": 10
}
},
"limits": {
"max": {
"cores": 16,
"ramGb": 24,
"diskGb": 500,
"ipv4": 5
},
"staticMax": {
"cores": 16,
"ramGb": 32,
"diskGb": 500,
"ipv4": 5
}
},
"capacity": "limited",
"unavailable": null,
"upgradeInProgress": false,
"period": {
"start": "2026-11-01T00:00:00.000Z",
"end": "2026-12-01T00:00:00.000Z",
"remainingDays": 24,
"periodDays": 30
},
"pendingOrder": null,
"pendingRestart": false,
"ipv4Pending": 0,
"recurringMaxCzk": null,
"billingIncomplete": false,
"customerType": "consumer"
}Possible errors
- 400 The service is not a VPS.
- 403 You are not the service owner.
- 409 The service isn't active.
POST/services/{id}/order-upgradeOrder a VPS upgrade
Creates a VPS upgrade order with a proforma invoice for the prorated charge until the end of the period; the upgrade runs only after payment. The server prices by RAM, disk can only grow; action cancelPending cancels an unpaid order.
Permission: billing:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | no | order (default) or cancelPending. |
ramGb | number | no | New RAM in GB (1–32); vCPU is derived from RAM. Missing = unchanged. |
diskGb | number | no | Disk in GB (20–500), only upwards. |
ipv4 | number | no | Number of IPv4 addresses (1–5). |
cores | number | no | vCPU (1–16); derived from RAM under RAM-based pricing. |
locale | string | no | Language of messages and order description: cs, en, sk, pl or de. |
immediateStartRequested | boolean | no | Consumer: true = request to carry out the change immediately; required to place the order. |
businessPurpose | boolean | no | Account with a company ID: true = business purpose declaration (then immediate start isn't required). |
expectedChargeCzk | number | no | The charge you displayed; different from the computed one = 409 price_changed. |
expectedMonthlyCzk | number | no | The new monthly price you displayed; different = 409 price_changed. |
Sample body
{
"ramGb": 8,
"diskGb": 100,
"locale": "cs",
"immediateStartRequested": true,
"expectedChargeCzk": 312,
"expectedMonthlyCzk": 780
}Sample response
{
"orderId": "clx9o4ord0001",
"chargeCzk": 312,
"monthlyToCzk": 780
}Possible errors
- 400
immediate_start_requiredThe immediate start request is missing. Without a code: invalid values (field) or nothing moved up. - 409
pending_orderAn open order already exists (orderId). Other codes: billing_profile_incomplete, foreign_eu_vat, unavailable, transfer_pending, upgrade_in_progress, refund_open, renewal_payment, renewal_overdue, capacity. - 409
price_changedThe price changed; new values in newChargeCzk and newMonthlyCzk, nothing was created. - 503
capacity_unknownFree capacity can't be checked right now. Code price_list_unavailable: the price list can't be loaded right now.
GET/services/{id}/renewalService renewal and payment status
Returns the service renewal status, end of the paid period, next payment, automatic payments and the history of payment requests. For VPS, games, web and domains; a member of a shared service gets only managed, status and paidUntil.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | en for English texts, otherwise Czech. |
Sample response
{
"serviceId": "clx9svc000001",
"managed": true,
"status": "awaiting_payment",
"paidUntil": "2026-12-01T00:00:00.000Z",
"owner": {
"nextPayment": {
"amountCzk": 390,
"periodStart": "2026-12-01T00:00:00.000Z",
"periodEnd": "2027-01-01T00:00:00.000Z",
"issueAt": "2026-11-17T00:00:00.000Z",
"dueAt": "2026-12-01T00:00:00.000Z",
"method": "manual",
"invoice": {
"orderId": "clx9o4ord0002",
"invoiceId": "clx9i5inv0002",
"number": "ZF2026110042",
"status": "PENDING",
"payUrl": "/cs/order/clx9o4ord0002"
}
},
"suspendAt": null,
"terminateAt": null,
"autoPay": {
"credit": {
"enabled": true,
"globallyAllowed": true,
"held": false,
"balanceCzk": 120,
"sufficient": false
},
"card": {
"enabled": false,
"available": false,
"last4": null,
"maxAmountCzk": null,
"unavailableReason": null
}
},
"endAtTermEnd": null,
"canPay": true,
"canExtendEarly": false,
"canEndAtTermEnd": true,
"history": []
}
}Possible errors
- 400 This service type has no renewal in the portal (e.g. rackhousing).
- 404
not_foundService not found.
POST/services/{id}/renewalPay or renew early
Returns the open payment request, or issues a request for the next period early (at most one period ahead), with a payment link. A new request returns 201; owner only, limit 20 per hour, body as application/json.
Permission: billing:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | Always pay. |
locale | string | no | Language of messages and the request: cs, en, sk, pl or de. |
Sample body
{
"action": "pay",
"locale": "cs"
}Sample response
{
"orderId": "clx9o4ord0003",
"payUrl": "/cs/order/clx9o4ord0003",
"created": true
}Possible errors
- 400
invalidInvalid body. - 409
not_allowedRenewal isn't possible right now. Other codes: not_managed, terminated, term_end, payment_started, failed. - 429
rate_limitedMore than 20 requests per hour. - 503
failedThe request couldn't be issued right now.
PATCH/services/{id}/renewalAutomatic payments and termination at term end
Turns payment from credit and by card on or off and files or withdraws termination at the end of the paid period. Termination cancels open payment requests without payment, the service ends with the period and a confirmation e-mail is sent. Owner only, body as application/json.
Permission: billing:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
autoCredit | boolean | no | Pay the renewal automatically from credit. |
autoCard | boolean | no | Pay the renewal automatically with the saved card. |
endAtTermEnd | boolean | no | true = terminate at the end of the paid period, false = withdraw termination. |
locale | string | no | Message language: cs, en, sk, pl or de. |
Sample body
{
"endAtTermEnd": true,
"locale": "cs"
}Sample response
{
"serviceId": "clx9svc000001",
"managed": true,
"status": "ending",
"paidUntil": "2026-12-01T00:00:00.000Z",
"owner": {
"nextPayment": null,
"suspendAt": null,
"terminateAt": null,
"autoPay": {
"credit": {
"enabled": true,
"globallyAllowed": true,
"held": false,
"balanceCzk": 120,
"sufficient": null
},
"card": {
"enabled": false,
"available": false,
"last4": null,
"maxAmountCzk": null,
"unavailableReason": null
}
},
"endAtTermEnd": {
"requestedAt": "2026-11-07T10:00:00.000Z",
"by": "customer",
"termEnd": "2026-12-01T00:00:00.000Z"
},
"canPay": false,
"canExtendEarly": false,
"canEndAtTermEnd": false,
"history": []
}
}Possible errors
- 400
invalidInvalid body (fields other than allowed). - 404
not_allowedThe service doesn't belong to your account. - 409
not_managedThe service has no billing period set. Other codes: terminated, not_active, period_over, too_late, payment_started. - 415 The body isn't application/json.
GET/services/{id}/web-upgradeWeb hosting plan upgrade offer
Returns the current web hosting plan and higher plans with the charge until the end of the period. For now it returns 409 coming_soon, web hosting can't be changed online yet; owner only.
Permission: services:read
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
locale | string | no | en for English texts, otherwise Czech. |
Sample response
{
"current": {
"tier": "starter",
"name": "Starter",
"storageGb": 10,
"sites": 1
},
"monthlyCzk": 59,
"period": {
"start": "2026-11-01T00:00:00.000Z",
"end": "2026-12-01T00:00:00.000Z",
"remainingDays": 24,
"periodDays": 30
},
"options": [
{
"tier": "business",
"name": "Business",
"storageGb": 30,
"sites": 5,
"priceCzk": 119,
"monthlyCzk": 119,
"chargeCzk": 48
}
],
"pendingOrder": null,
"upgradeInProgress": false,
"blocker": null,
"recurringMaxCzk": null,
"billingIncomplete": false,
"customerType": "consumer"
}Possible errors
- 409
coming_soonA web hosting plan change can't be ordered online yet. Code unavailable: the plan can't be determined or the service doesn't allow an upgrade. - 400 The service isn't web hosting.
- 403 You are not the service owner.
POST/services/{id}/web-upgradeOrder a higher web hosting plan
Creates an order for a higher plan with a proforma invoice for the prorated charge; the change runs after payment, a lower plan isn't possible. For now it returns 409 coming_soon; action cancelPending cancels an unpaid order.
Permission: billing:write
Request body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
action | string | no | order (default) or cancelPending. |
tier | string | yes | For order: business or pro (only higher than the current one). |
locale | string | no | Language of messages and order description: cs, en, sk, pl or de. |
immediateStartRequested | boolean | no | Consumer: true = request to carry out the change immediately; required to place the order. |
businessPurpose | boolean | no | Account with a company ID: true = business purpose declaration. |
expectedChargeCzk | number | no | The charge you displayed; different = 409 price_changed. |
expectedMonthlyCzk | number | no | The new monthly price you displayed; different = 409 price_changed. |
Sample body
{
"tier": "business",
"locale": "cs",
"immediateStartRequested": true
}Sample response
{
"orderId": "clx9o4ord0004",
"chargeCzk": 48,
"monthlyToCzk": 119
}Possible errors
- 409
coming_soonA plan change can't be ordered online yet. Other codes: price_changed, pending_order, billing_profile_incomplete, unavailable, unknown_current. - 400
downgradeA lower or the same plan isn't possible. Other codes: unknown_tier, immediate_start_required. - 503
price_list_unavailableThe price list can't be loaded right now, nothing is charged.
Security
- Give every application and environment its own key with the smallest possible permissions. If one leaks, you revoke only that one.
- Revoke keys you don't need. The key list shows when and from where each key was last used.
- For sensitive actions (reinstall, passwords, domain transfer) use a key restricted to your server's IP address.
- Revoke a leaked key in your customer account right away and create a new one. If you suspect abuse, contact us.
What the API can't do
- sign-in, password, two-factor authentication, passkeys and signed-in devices
- sharing a service with other accounts and transferring a service
- withdrawal from the contract and refunds
- creating further API keys
- uploading files (use SFTP, you get the access details via the API)