Skip to content

Automation (REST API)

Everything you script against NexoVirt goes through the REST API under /api/v1. It is smaller and friendlier than the raw Proxmox API: one token covers every connected host, and one call creates a complete guest. This page walks through the typical workflow; the interactive REST API reference lists every endpoint with a try-it panel.

Open Settings → API tokens and fill in the Create token form:

FieldWhat it does
NameA label for the token, for example ci-bot or terraform.
Allowed IPsOne IP or CIDR per line (for example 203.0.113.5 or 198.51.100.0/24). Leave blank to allow any source.
Rate limitRequests per minute. Leave blank (or 0) for no limit.
Connection scopeComma-separated connection ids (the API’s host ids, one node or one cluster each) the token may access. Leave blank for all nodes.

The token is shown once, right after you create it: copy it then. Only a hash is stored. The Tokens table shows each token’s allowed IPs, scope, rate, last use and status; Revoke disables a token immediately.

Send the token as a bearer header on every request:

Terminal window
curl -s https://panel.example.com/api/v1/hosts \
-H "Authorization: Bearer $NEXOVIRT_TOKEN"

The token check answers with a short {"error": "..."} body:

StatusMeaning
401Missing, unknown or revoked token.
403The source IP is not in Allowed IPs, or the path names a host outside the token’s scope.
429Rate limit exceeded. The Retry-After header says how many seconds until the next minute window.

Host-less endpoints (GET /hosts, POST /guests, GET /placement/suggest) only ever see the hosts in the token’s scope.

Every endpoint answers with the same envelope:

{ "success": true, "data": { }, "error": null }

On failure success is false, data is null and error holds a readable message (404 not found, 409 conflict, 422 validation, 502 Proxmox failure). List endpoints accept ?page= and ?perPage= (default 50, at most 200) and add meta: { page, perPage, total }.

Before creating a guest, look up the identifiers the create call takes. None of these return secrets.

EndpointReturns
GET /hosts/{id}/plansActive plans (cores, memory, disk, traffic, runtime).
GET /hosts/{id}/os-templatesQEMU OS templates, each with a ref like tpl:3.
GET /hosts/{id}/ct-templates?node=LXC templates: the managed library plus the node’s template files.
GET /hosts/{id}/ip-zonesIP zones, each with a ref like zone:2.
GET /hosts/{id}/next-vmidThe next free VMID.
GET /hosts/{id}/storages, /networks, /nodesStorage names, bridges and nodes.
GET /users/{id}/ssh-keysA user’s SSH keys (name and fingerprint only).
Terminal window
curl -s -X POST https://panel.example.com/api/v1/hosts/1/guests \
-H "Authorization: Bearer $NEXOVIRT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "qemu",
"node": "auto",
"name": "web-01",
"plan_id": 2,
"os_template": "tpl:3",
"ip_source": "zone:1",
"owner_id": 14,
"password": "change-me-please"
}'
  • type is qemu or lxc. QEMU takes os_template (tpl:<id>); LXC takes lxc_template_id or an os_template volume id.
  • plan_id sets cores, memory and disk from a plan. Without a plan, send cores, memory_mb and disk_gb.
  • node: "auto" lets placement pick the node and storage; vmid may then be left out.
  • Give a password or SSH keys (ssh_keys, plus the stored keys of owner_id).
  • ip_source is dhcp (default) or zone:<id> to assign the next free address from an IP zone.

The request returns at once with a queued job:

{ "success": true, "data": { "queued": true, "job_id": 381, "vmid": 112,
"placement": { "host_id": 1, "node": "pve-2", "storage": "local-zfs", "explanation": "..." } }, "error": null }

A 409 means the host is in maintenance, the VMID is taken, the IP zone is full or no node has room.

Terminal window
curl -s https://panel.example.com/api/v1/hosts/1/jobs/381 \
-H "Authorization: Bearer $NEXOVIRT_TOKEN"

status moves from queued to running to done (or failed, with the reason in error). Poll every few seconds until it is done or failed. The same job id is shown in the panel, where the guest’s live provisioning page tracks it step by step.

Placement scores every eligible node on free RAM, CPU load and free storage and skips nodes that are offline, in maintenance, without a healthy agent or without room.

  • node: "auto" on POST /hosts/{id}/guests picks the best node on that host.
  • POST /guests (no host in the path) picks across every host in the token’s scope; node must be auto there.
  • GET /placement/suggest?type=qemu&memory_mb=2048&cores=2&disk_gb=20 previews the ranking without creating anything: the best target, every ranked node with a score and explanation, and every dropped node with the reason.

Weights are set under Settings → Placement.

The Request log on Settings → API tokens records every API request, including refused ones: time, token, source IP, method, path, status and duration. Entries are kept for 30 days.