NexorPanel

Diese Dokumentation erscheint auf Englisch und Russisch; unten steht die englische Fassung. Русский

NexorPanel API

Everything the panel's interface does is done with the same requests: the interface is an ordinary client of this API and has no other way in. So there is no “possible in the panel, impossible over the API”.

Basics

The base address is the machine the panel is opened on:

https://panel.example.com/api/v1
  • Request and response bodies are JSON in UTF-8; send Content-Type: application/json when there is a body.
  • Times are UTC in RFC 3339: 2026-09-24T08:12:30Z.
  • The language of messages follows Accept-Language: English, Russian, Ukrainian, Belarusian, German or Polish.
  • A trailing slash does not matter: /api/v1/sites and /api/v1/sites/ are the same address, answered directly rather than redirected (a redirect after POST loses the body in some clients).
  • The only version is v1. Fields may be added inside it, but never change meaning; a breaking change gets v2.
  • GET /healthz needs no key and answers {"status":"ok","version":"…"} — handy to check that an update landed.

API keys

A program identifies itself with a key, not a login and password. A panel password opens the whole panel and requires a second factor; a key can be issued for one script, made read-only, given an expiry and revoked without touching anything else.

Create a key in the panel: Settings → API keys (or POST /api/v1/tokens from a signed-in session). The key is shown once — the panel keeps only its SHA-256 fingerprint. Send it in a header:

Authorization: Bearer nxp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  • Never in the query string: the panel does not look for it there, because URLs end up in web server logs, shell history and Referer.
  • A key inherits its account's rights and rereads them on every request. An administrator demoted to client loses admin rights over the key too; a disabled account's keys stop working.
  • A read-only key answers GET, HEAD and OPTIONS and refuses everything else with 403 read_only_key — enough for monitoring and exports.
  • A key cannot create a key — 403 key_by_key; otherwise a leaked key could mint a new one faster than anyone notices.
  • Changing the password does not revoke keys. If the password is changed because of a leak, revoke the keys separately.
  • Expiry is from one day to a year; 0 means no expiry. Up to 32 keys per account (409 too_many_keys).

Secret entrance

An installation may have its own path segment (NEXOR_ENTRANCE). Then every address below lives at /<segment>/api/v1/… instead of /api/v1/…, and everything else — including the API — gets an empty 404. Only /healthz stays outside. The machine's administrator tells you the segment; it cannot be guessed, which is the point.

URL rules

WhatHow
listGET /api/v1/<section>
one objectGET /api/v1/<section>/{id}
createPOST /api/v1/<section>
updatePUT /api/v1/<section>/{id}
deleteDELETE /api/v1/<section>/{id}
actionPOST /api/v1/<section>/{id}/<action>

The fields for create and update are the ones the panel's form shows: fill the form, and the browser's developer tools show exactly which JSON was sent. The form and the API are the same thing. Lists of choices for forms (servers, PHP versions, addresses, plans) come from the *-options endpoints.

Lists

All lists work the same way:

GET /api/v1/sites?limit=25&offset=0&search=example

{"items":[…],"total":42,"limit":25,"offset":0}

limit is at most 200; a bigger value falls back to the default instead of failing. search matches the name. Some lists narrow further: ?site=126 by site, ?domain=example.org by mail domain. A client's response contains only their own objects.

Errors

An error is JSON with a code, a message and, when it is about one field, the field's name:

{"code":"invalid_input","message":"The domain must contain at least one dot.","field":"domain"}

Parse code, not message. The message is written for people and changes with the language; the code does not. The most common codes:

HTTPCodeMeaning
400bad_requestthe request cannot be parsed
400invalid_inputa field failed validation; field names it
401unauthenticatedneither a key nor a session
401bad_keythe key is revoked, expired or unknown
402needs_planthe feature is not in this client's plan; the message names the plan needed
403forbiddenthe section is not for this role
403read_only_keythe key is read-only
403key_by_keya key cannot create a key
403account_disabledthe account is disabled
403bad_paththe path is outside the site
403bad_origina browser request came from another origin
404not_foundno such object — or it is not yours
409name_takenthe name is taken
409cannot_movea site cannot be moved to another machine by editing a field
409action_pendingthe previous change is still running
409too_many_keysthe account already has the maximum number of keys
413file_too_bigthe file is larger than the upload limit
422bad_archivethe archive cannot be unpacked
429rate_limitedtoo often; wait
500internala panel error; details are in its log
501not_implementedthe section does not work in this mode
501agent_not_toldthe agent was not given this job; the message names the missing setting
502agent_unreachablethe agent on the machine does not answer
502agent_refusedthe panel and the agent have different secrets
502agent_too_oldthe agent is older than the panel and does not know the request
502agent_failedthe agent refused; details are in the panel's log
503no_agentno agent is configured on the machine
503not_configuredthis installation was not told what to work with (e.g. NEXOR_IMAP)

Someone else's object and a missing one answer the same 404 not_found: a difference would let anyone enumerate other people's sites.

Examples

Keep the address and the key in variables:

export NX=https://panel.example.com/api/v1
export KEY=nxp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
curl -sS -H "Authorization: Bearer $KEY" $NX/version

Sites and their disk usage

curl -sS -H "Authorization: Bearer $KEY" "$NX/sites?limit=200" |
  jq -r '.items[] | "\(.domain)\t\(.usedMb) MB"'

A site's space is three numbers: usedMb (the folder), databasesMb and backupsMb. -1 means “not counted yet”, not zero.

Create a site

curl -sS -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"domain":"example.org","serverId":1,"active":true,"quotaMb":-1,
       "phpMode":"php-fpm","ssl":true,"letsEncrypt":true,"forceHttps":true,
       "wwwAlias":true,"backupInterval":"daily","backupCopies":7}' \
  $NX/sites

serverId is the machine; take it from GET /site-options (servers), together with that machine's PHP versions and addresses. Zero or no field means the panel's own machine; a client's site always goes there.

Pause a site and resume it

curl -sS -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"active":false}' $NX/sites/126/status

Create a client with a limit on sites

curl -sS -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"username":"lira","password":"correct horse battery","company":"Lira Ltd",
       "email":"petr@example.org","active":true,
       "limits":{"webDomain":5,"database":5,"cron":10,"ftpUser":5}}' \
  $NX/clients

Back up a site now

curl -sS -X POST -H "Authorization: Bearer $KEY" $NX/sites/126/backups

Restart a service on the machine — actions: start, stop, restart, reload.

curl -sS -X POST -H "Authorization: Bearer $KEY" $NX/servers/1/services/nginx/restart

See what takes the disk

curl -sS -H "Authorization: Bearer $KEY" $NX/servers/1/disk |
  jq -r '.parts[] | "\(.key)\t\(.bytes)"'

Check that everything is fine — a read-only key is enough for monitoring:

curl -sS -H "Authorization: Bearer $KEY" $NX/attention |
  jq -r '.items[] | select(.level != "ok") | .text'

Download a site file

curl -sS -H "Authorization: Bearer $KEY" \
  "$NX/sites/126/files/download?path=/web/index.php" -o index.php

Deploy a site from a repository

curl -sS -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"url":"https://github.com/owner/repo","ref":"main","webDir":"public"}' \
  $NX/sites/12/deploy

The machine fetches the repository and replaces the site folder with it as a whole — not a git pull, which stops at the first local difference and leaves a half-updated site. The answer is what was deployed: {"commit":"a1b2c3d","ref":"main","webDir":"public"}. webDir is the folder the web server serves, relative to the site folder.

For a private repository send the private deploy key in key and use an ssh URL; the panel does not store it — it lives in a temporary file on the machine for the duration of the clone. Refused URLs: ext::, file:// and local paths, unencrypted git:// and http://, and passwords inside the URL. Submodules are not fetched and .git never reaches the site folder. Files listed in the repository's .gitignore are left in place; anything else to keep can be listed in a .deployignore in the site folder on the server.

All endpoints

219 endpoints. Paths are relative to the panel's address. Unless marked otherwise, an endpoint is open to every signed-in role, and clients see only their own objects.

Sign-in and own account

auth/login and auth/2fa exist for the browser: they set a cookie, require the second factor and are rate-limited. Scripts use a key.

POST/api/v1/auth/loginno auth
POST/api/v1/auth/2fano auth
POST/api/v1/auth/logoutno auth
GET/api/v1/me
POST/api/v1/me/password
GET/api/v1/2fa
POST/api/v1/2fa/setup
POST/api/v1/2fa/enable
POST/api/v1/2fa/disable
POST/api/v1/2fa/recovery-codes

API keys

A key cannot create another key (403 key_by_key). Up to 32 keys per account.

GET/api/v1/tokens
POST/api/v1/tokens
DELETE/api/v1/tokens/{id}

Dashboard, search and health

/healthz stays reachable even behind a secret entrance and answers with the build version.

GET/healthzno auth
GET/api/v1/version
GET/api/v1/dashboard
GET/api/v1/search
GET/api/v1/attention
GET/api/v1/notifications
GET/api/v1/plan/features

Websites

A site is created on the machine named by serverId (from site-options) and cannot be moved by editing it: a different serverId answers 409 cannot_move. Deleting a site removes its configuration but keeps its files.

GET/api/v1/sites
POST/api/v1/sites
GET/api/v1/sites/{id}
PUT/api/v1/sites/{id}
DELETE/api/v1/sites/{id}
POST/api/v1/sites/{id}/status
POST/api/v1/sites/{id}/provision
POST/api/v1/sites/{id}/copy
POST/api/v1/sites/{id}/publish
GET/api/v1/sites/{id}/overview
GET/api/v1/sites/{id}/stats
PUT/api/v1/sites/{id}/stats
GET/api/v1/sites/{id}/shot
GET/api/v1/sites/{id}/logs
GET/api/v1/sites/{id}/logs/{name}
GET/api/v1/sites/children
GET/api/v1/sites/{id}/children
POST/api/v1/sites/{id}/children
PUT/api/v1/sites/{id}/children/{childId}
DELETE/api/v1/sites/{id}/children/{childId}
GET/api/v1/site-options
GET/api/v1/hosting-options

Deploy from a repository

deploy replaces the site folder with the repository as a whole. A private repository takes a private deploy key in the request; the panel does not store it.

POST/api/v1/sites/{id}/deploy
GET/api/v1/sites/{id}/deploy-key
POST/api/v1/sites/{id}/deploy-key
DELETE/api/v1/sites/{id}/deploy-key

Site files

Paths never leave the site's folder (403 bad_path). Deleting moves a file to the site's trash for 30 days. Large files are uploaded in parts.

GET/api/v1/sites/{id}/files
GET/api/v1/sites/{id}/file
PUT/api/v1/sites/{id}/file
GET/api/v1/sites/{id}/files/search
GET/api/v1/sites/{id}/files/size
POST/api/v1/sites/{id}/files/action
POST/api/v1/sites/{id}/files/upload
POST/api/v1/sites/{id}/files/upload/part
GET/api/v1/sites/{id}/files/download
GET/api/v1/sites/{id}/trash
POST/api/v1/sites/{id}/trash/restore
DELETE/api/v1/sites/{id}/trash

SSL certificates

Let's Encrypt is switched on in the site itself (letsEncrypt); these routes are for a certificate of your own.

GET/api/v1/sites/{id}/certificate
PUT/api/v1/sites/{id}/certificate
DELETE/api/v1/sites/{id}/certificate

Site backups

The site is part of the route: a backup number alone does not say whose it is.

GET/api/v1/sites/{id}/backups
POST/api/v1/sites/{id}/backups
POST/api/v1/sites/{siteId}/backups/{id}/action
POST/api/v1/sites/{siteId}/backups/{id}/offsite
POST/api/v1/sites/{siteId}/backups/{id}/offsite/fetch
GET/api/v1/sites/{siteId}/backups/{id}/download

Databases

temp-login opens phpMyAdmin with a half-hour account created for that one visit; database passwords are never stored by the panel.

GET/api/v1/databases
POST/api/v1/databases
GET/api/v1/databases/{id}
PUT/api/v1/databases/{id}
DELETE/api/v1/databases/{id}
POST/api/v1/databases/{id}/temp-login
GET/api/v1/database-users
POST/api/v1/database-users
GET/api/v1/database-users/{id}
PUT/api/v1/database-users/{id}
DELETE/api/v1/database-users/{id}

SFTP, FTP and WebDAV access

FTP users are the same records as shell users: FTP is switched on with the ftp field of a shell user, and DELETE ftp-users/{id} only closes FTP.

GET/api/v1/shell-users
POST/api/v1/shell-users
GET/api/v1/shell-users/{id}
PUT/api/v1/shell-users/{id}
DELETE/api/v1/shell-users/{id}
GET/api/v1/ftp-users
POST/api/v1/ftp-users
GET/api/v1/ftp-users/{id}
PUT/api/v1/ftp-users/{id}
DELETE/api/v1/ftp-users/{id}
GET/api/v1/webdav-users
POST/api/v1/webdav-users
GET/api/v1/webdav-users/{id}
PUT/api/v1/webdav-users/{id}
DELETE/api/v1/webdav-users/{id}

Protected folders

GET/api/v1/web-folders
POST/api/v1/web-folders
GET/api/v1/web-folders/{id}
PUT/api/v1/web-folders/{id}
DELETE/api/v1/web-folders/{id}
POST/api/v1/web-folders/{id}/users
DELETE/api/v1/web-folders/{id}/users/{userId}

Scheduled tasks

GET/api/v1/cron
POST/api/v1/cron
GET/api/v1/cron/{id}
PUT/api/v1/cron/{id}
DELETE/api/v1/cron/{id}

Mail

Lists narrow with ?domain=. Transports and relay recipients are administrator-only.

GET/api/v1/mail/domains
POST/api/v1/mail/domains
GET/api/v1/mail/domains/{id}
PUT/api/v1/mail/domains/{id}
DELETE/api/v1/mail/domains/{id}
POST/api/v1/mail/domains/{id}/status
GET/api/v1/mail/mailboxes
POST/api/v1/mail/mailboxes
GET/api/v1/mail/mailboxes/{id}
PUT/api/v1/mail/mailboxes/{id}
DELETE/api/v1/mail/mailboxes/{id}
GET/api/v1/mail/mailboxes/{id}/filters
POST/api/v1/mail/mailboxes/{id}/filters
PUT/api/v1/mail/mailboxes/{id}/filters/{filterId}
DELETE/api/v1/mail/mailboxes/{id}/filters/{filterId}
GET/api/v1/mail/forwards
POST/api/v1/mail/forwards
DELETE/api/v1/mail/forwards/{id}
GET/api/v1/mail/lists
POST/api/v1/mail/lists
DELETE/api/v1/mail/lists/{id}
GET/api/v1/mail/lists-mailman
POST/api/v1/mail/lists-mailman
DELETE/api/v1/mail/lists-mailman/{id}
GET/api/v1/mail/transportsadmin
POST/api/v1/mail/transportsadmin
DELETE/api/v1/mail/transports/{id}admin
GET/api/v1/mail/relay-recipientsadmin
POST/api/v1/mail/relay-recipientsadmin
DELETE/api/v1/mail/relay-recipients/{id}admin
GET/api/v1/mail/contacts
POST/api/v1/mail/contacts
PUT/api/v1/mail/contacts/{id}
DELETE/api/v1/mail/contacts/{id}
GET/api/v1/mail-options

Webmail

Webmail has its own sign-in: the mailbox password proves the rights and a sealed mail cookie keeps them, so there is no mailbox number in these routes. mail/mailboxes/{id}/webmail opens a mailbox from a panel session.

POST/api/v1/mail/mailboxes/{id}/webmail
GET/api/v1/webmail/sessionwebmail session
POST/api/v1/webmail/loginwebmail session
POST/api/v1/webmail/openwebmail session
POST/api/v1/webmail/logoutwebmail session
GET/api/v1/webmail/folderswebmail session
GET/api/v1/webmail/letterswebmail session
GET/api/v1/webmail/searchwebmail session
GET/api/v1/webmail/letters/{uid}webmail session
GET/api/v1/webmail/letters/{uid}/attachments/{index}webmail session
POST/api/v1/webmail/letters/{uid}/seenwebmail session
POST/api/v1/webmail/letters/{uid}/movewebmail session
POST/api/v1/webmail/sendwebmail session

DNS

GET/api/v1/dns/zones
POST/api/v1/dns/zones
GET/api/v1/dns/zones/{id}
PUT/api/v1/dns/zones/{id}
DELETE/api/v1/dns/zones/{id}
POST/api/v1/dns/zones/{id}/status
POST/api/v1/dns/zones/{id}/dnssec
GET/api/v1/dns/zones/{id}/records
POST/api/v1/dns/zones/{id}/records
PUT/api/v1/dns/records/{id}
DELETE/api/v1/dns/records/{id}
GET/api/v1/dns/zones/{id}/export
POST/api/v1/dns/zones/{id}/import
GET/api/v1/dns/slave-zones
POST/api/v1/dns/slave-zones
PUT/api/v1/dns/slave-zones/{id}
DELETE/api/v1/dns/slave-zones/{id}
GET/api/v1/dns-options

Clients and plans

Resellers see the Clients section too, but only their own clients.

GET/api/v1/clientsadmin, reseller
POST/api/v1/clientsadmin, reseller
GET/api/v1/clients/{id}admin, reseller
PUT/api/v1/clients/{id}admin, reseller
DELETE/api/v1/clients/{id}admin, reseller
POST/api/v1/clients/{id}/passwordadmin, reseller
GET/api/v1/client-optionsadmin, reseller
GET/api/v1/plansadmin
POST/api/v1/plansadmin
GET/api/v1/plans/{id}admin
PUT/api/v1/plans/{id}admin
DELETE/api/v1/plans/{id}admin

Servers and services

Service actions: start, stop, restart, reload. The console runs a command on the machine only where the agent has NEXOR_AGENT_CONSOLE=1.

GET/api/v1/serversadmin
GET/api/v1/monitoradmin
GET/api/v1/servers/{id}/metricsadmin
GET/api/v1/servers/{id}/agentadmin
GET/api/v1/servers/{id}/diskadmin
GET/api/v1/servers/{id}/servicesadmin
POST/api/v1/servers/{id}/services/{name}/{action}admin
POST/api/v1/servers/{id}/consoleadmin
GET/api/v1/servers/{id}/machine-certificateadmin
POST/api/v1/servers/{id}/machine-certificateadmin

Firewall and IP addresses

GET/api/v1/firewalladmin
POST/api/v1/firewalladmin
PUT/api/v1/firewall/{id}admin
DELETE/api/v1/firewall/{id}admin
GET/api/v1/ip-addressesadmin
GET/api/v1/server-ipsadmin
POST/api/v1/server-ipsadmin
PUT/api/v1/server-ips/{id}admin
DELETE/api/v1/server-ips/{id}admin

Panel backups, logs and updates

update reports whether the panel and every agent run the same version, and whether a newer release exists.

GET/api/v1/panel/backupsadmin
POST/api/v1/panel/backupsadmin
POST/api/v1/panel/backups/uploadadmin
GET/api/v1/panel/backups/planadmin
PUT/api/v1/panel/backups/planadmin
GET/api/v1/panel/backups/{name}/downloadadmin
POST/api/v1/panel/backups/{name}/restoreadmin
POST/api/v1/panel/backups/{name}/offsiteadmin
POST/api/v1/panel/backups/{name}/offsite/fetchadmin
DELETE/api/v1/panel/backups/{name}admin
GET/api/v1/auditadmin
GET/api/v1/panel-logadmin
GET/api/v1/updateadmin
GET/api/v1/notifications/outboundadmin
POST/api/v1/notifications/outbound/testadmin
GET/api/v1/migration/sourcesadmin
POST/api/v1/migration/inspectadmin