API: servers

Cloud VPS and dedicated servers are both “services”, but they sit behind different infrastructure — cloud on our own Proxmox clusters, dedicated through our provisioning platform. The paths differ accordingly.

Listing your servers

curl https://my.server.express/api/v1/cloud \
  -H "Authorization: Bearer $SE_TOKEN"

Returns your cloud services, newest first, each with its product. /dedicated does the same for dedicated servers. Both need services:read — see authentication and scopes if your token doesn’t have it.

For one service:

GET /cloud/:id
GET /dedicated/:id

The id is the service ID from the list — it’s the identifier for everything below.

Cloud VPS

Current state

GET /cloud/:id/servers        # VMs belonging to this service
GET /cloud/:id/:vmid          # live status of one VM
GET /cloud/:id/network        # IP assignments

All services:read. The vmid comes from /cloud/:id/servers.

Metrics

curl "https://my.server.express/api/v1/cloud/$ID/stats?timeframe=day" \
  -H "Authorization: Bearer $SE_TOKEN"

timeframe is one of hour, day, week, month, defaulting to hour. Anything else falls back to hour rather than erroring.

You get current state plus a usage series for CPU, memory, disk and network. Requires services:stats.

The response also carries an estimated power draw and carbon figure, derived from vCPU load, datacenter PUE and the grid intensity of the country the node sits in. It’s a decent estimate for reporting. It is not metered, and it isn’t what you’re billed on.

Power

curl -X POST https://my.server.express/api/v1/cloud/$ID/$VMID \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"reboot"}'

action is start, stop or reboot. Requires services:power.

stop is a hard power-off, not a graceful shutdown. Treat it like pulling the plug, because that’s what it is.

The response returns the hypervisor task ID:

{ "success": true, "data": { "upid": "UPID:node:..." } }

That’s a handle on an async job. The call returning doesn’t mean the VM has finished booting.

Reinstall

curl -X POST https://my.server.express/api/v1/cloud/$ID/reinstall \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"os":"ubuntu-24.04"}'

os is a template slug — fetch the valid list from GET /cloud/templates. Requires services:reinstall.

This wipes the disk. There is no confirmation step and no undo. See Reinstalling your server for what survives (your IPs and rDNS) and what doesn’t (everything else).

You’ll get 409 if a reinstall is already running, and 422 if the VM was never provisioned.

Credentials

GET /cloud/:id/credentials

Returns the current root credentials. Requires services:rescue.

Handle the response like the secret it is — don’t log it, don’t put it in CI output.

Dedicated servers

GET /dedicated/:id/overview     # spec and status summary
GET /dedicated/:id/network      # IPs and rDNS
GET /dedicated/:id/metadata     # hardware detail
GET /dedicated/:id/activity     # recent actions

All services:read.

Power

curl -X POST https://my.server.express/api/v1/dedicated/$ID/power \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"reset"}'

Four actions here, not three: start, stop, reboot, reset. reset is the physical reset line — use it when the OS has stopped answering and reboot won’t take. Requires services:power.

Rescue mode

curl -X POST https://my.server.express/api/v1/dedicated/$ID/rescue \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"osId":"rescue-linux"}'

Boots a rescue environment instead of the installed OS, so you can mount the disk and fix whatever broke. Requires services:rescue.

Reinstall

curl -X POST https://my.server.express/api/v1/dedicated/$ID/reinstall \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"osId":"ubuntu-24.04","hostname":"web-01"}'

Both osId and hostname are required. Valid OS IDs come from GET /dedicated/:id/profiles. Requires services:reinstall. Same warning as above — the disk goes.

Cancelling

curl -X POST https://my.server.express/api/v1/cloud/$ID/cancel \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason":"project finished","immediate":false}'

/dedicated/:id/cancel works the same way. Requires services:cancel.

immediate is the field that matters:

  • false (the default) schedules cancellation for the end of your paid period. The server keeps running until then.
  • true terminates it now. The service stops and the data goes.

Defaulting to false is deliberate. If you’re passing immediate from a variable, make sure you know what’s in it.

Billing carries on until the period ends, which is the invoice side of the same thing.

reason is optional and shows up in our records. It’s genuinely read — if something didn’t work, saying so here is useful.

A worked example

Reboot every running cloud VPS on the account:

#!/usr/bin/env bash
set -euo pipefail

API="https://my.server.express/api/v1"
AUTH="Authorization: Bearer $SE_TOKEN"

curl -sf "$API/cloud" -H "$AUTH" | jq -r '.data[].id' | while read -r id; do
  curl -sf "$API/cloud/$id/servers" -H "$AUTH" | jq -r '.data[].vmid' | while read -r vmid; do
    echo "rebooting $id/$vmid"
    curl -sf -X POST "$API/cloud/$id/$vmid" -H "$AUTH" \
      -H 'Content-Type: application/json' -d '{"action":"reboot"}' | jq -r '.data.upid'
  done
done

That needs a token with services:read and services:power — and nothing else. Which is the whole argument for scopes.

Last updated Invalid Date. Something wrong or missing? Tell us and we'll fix it.