Expanse API
Sign up
Reseller API

VM lifecycle

Resize, rescue, stats, backups, power and suspension: acceptance is not completion.

Resize a VM

POST /api/reseller/v1/vms/{id}/resize requires a dedicated rk_ key with reseller.vms.write (account sk_ keys and Clerk sessions are rejected).

Example
{ "vcpu": 4, "ramMiB": 8192, "diskGiB": 100, "restart": true }

All fields are optional, but at least one resource must change. Final sizes use custom-create bounds: vCPU 1–32, RAM 1024–262144 MiB in steps of 1024, and disk 10–1024 GiB. Disks can only grow. The current location settings do not have resource-specific limits. Account caps are checked using resource deltas under the account lock; the reservation precedes provider calls. Provider rejection rolls back the reservation. A partial provider acceptance requires reconciliation. A successful resize closes the current per-second allocation segment and opens one at the new size. The catalog package is cleared because the size is now custom.

CPU and RAM changes apply on the next boot. Set restart: true to queue a restart after resizing; otherwise restart the VM yourself. Disk growth may also require resizing the guest partition and filesystem.

Returns HTTP 202:

Example
{ "data": { "accepted": true, "vm": { "id": "vm-id", "vcpu": 4, "ramMiB": 8192, "diskGiB": 100 } } }

The vm uses the standard VM representation (abbreviated above). Emits vm.resized. A restart failure returns provider_error but does not undo an already accepted resize.

Rescue mode

  • POST /api/reseller/v1/vms/{id}/rescue: enable rescue, scope reseller.vms.write.
  • DELETE /api/reseller/v1/vms/{id}/rescue: disable rescue, scope reseller.vms.write.
  • GET /api/reseller/v1/vms/{id}/rescue: read state, scope reseller.vms.read.

Mutations return HTTP 202 with { "data": { "accepted": true } } and emit vm.rescue_enabled or vm.rescue_disabled. Enabling rescue restarts the server into the provider's default rescue image, with provider email disabled. If the platform returns a root password immediately, only the enable response adds data.password. It is never logged, stored, sent in a webhook, or included in GET. A queued session may not return credentials immediately. GET returns { "data": { "enabled": true } }; provider hypervisor data and credentials are not exposed. These operations do not change the panel VM status.

VM stats

GET /api/reseller/v1/vms/{id}/stats requires reseller.vms.read.

Example
{
  "data": {
    "power": "running",
    "cpuPercent": 12.5,
    "memory": { "totalMiB": 2048, "usedMiB": 1024 },
    "traffic": {
      "periodStart": "2026-10-01T00:00:00.000Z",
      "periodEnd": "2026-11-01T00:00:00.000Z",
      "rxBytes": 1024,
      "txBytes": 2048
    }
  }
}

CPU, memory and power come from the live remoteState read. KiB memory counters are converted to MiB; used memory is total minus available. Traffic is the current provider monthly period. Results are cached in memory per account/VM for 10 seconds. Provider errors degrade the affected fields to null rather than returning 500. Unavailable or invalid counters are also null.

Power, suspension and credentials

Power (start/stop/restart/poweroff), suspend, unsuspend, rebuild and IPv4 actions return immediately after provider acceptance: 202 { "data": { "accepted": true, "taskId": 123 } }. taskId is null if the provider does not supply a queue id. Acceptance does not mean completion. Read the VM again for live state. Suspend/unsuspend update local status and suspension reasons optimistically. IPv4 remains reserved until reconciled. A provider lock (HTTP 423) maps to 409 vm_busy with Retry-After: 30; other provider failures map to 502 provider_error. Reset-password returns expectedPassword from the accept response without polling.

New VMs belong to one internal platform owner per reseller. End-customer names and emails remain in CorePanel only. Provider mail is disabled. POST /vms accepts optional hostname: a lowercase RFC 1123 FQDN, at most 253 characters, labels at most 63 characters. The default is the VM name slug plus .local. VM responses include hostname and initialPasswordAvailable.

New VMs remain building until a detail read or hourly sweep confirms commissioning. The post-build reset uses sendMail: false; its expectedPassword is encrypted at rest. Failed resets retry on later reads/sweeps. No password is passed to build. GET /vms/{id}/initial-password requires reseller.vms.write and returns { "data": { "password": "..." } } exactly once, clearing the stored ciphertext. While building it returns 409 vm_busy; unavailable, consumed or expired passwords return 404 not_available. Other accounts receive 404 not_found. Unfetched passwords expire after seven days and are purged by the hourly traffic cron.

Power accepts start, stop, restart, poweroff. POST /vms/{id}/suspend and /unsuspend require write scope and return 202. Poll VM detail with read scope. Statuses: creating, building, active, failed, suspended, delete_requested, deleted. Pending DELETE retains VM fields plus accepted/taskId.

Backups

Check GET /account → data.features.backups before offering backups. GET /vms/{id}/backups?page=1 requires read scope and returns data.backups, page, lastPage; timestamps are Unix seconds. POST /vms/{id}/backups creates a backup; POST /vms/{id}/backups/{backupId}/restore restores one. Both require write scope and return 202 with accepted, nullable taskId and legacy jobId. Poll backup lists or VM detail as appropriate; no public task lookup exists. See request/response examples and errors.