> ## Documentation Index
> Fetch the complete documentation index at: https://docs.server.express/llms.txt
> Use this file to discover all available pages before exploring further.

# Servers

> List cloud and dedicated servers, read status and metrics, and drive power, rescue, snapshots, reinstall and cancellation from your own scripts.

Cloud VPS and dedicated servers are both "services", but they run on different infrastructure. Cloud runs on our own Proxmox clusters, and dedicated runs through our provisioning platform. That's why their paths differ.

Every call on this page needs a token with the scope shown. See [authentication](https://docs.server.express/authentication.md) if yours doesn't have it.

## Listing your servers

```bash theme={null}
curl https://my.server.express/api/v1/cloud \
  -H "Authorization: Bearer $SE_TOKEN"
```

This returns your cloud services, newest first. Servers shared with you through a team are included. `/v1/dedicated` does the same for dedicated servers, and `/v1/services` lists both kinds in one paged list. All three need `services:read`.

Each row already carries the last power state we polled, so a list never waits on a hypervisor. It answers just as fast for three servers as for three thousand.

### Paging and filtering

Without `limit`, you get every server. Pass `limit` (1 to 200) and the response gains a `nextCursor`. Send that back as `cursor` to get the next page. It's `null` on the last page.

```bash theme={null}
curl "https://my.server.express/api/v1/cloud?limit=100&tag=production" \
  -H "Authorization: Bearer $SE_TOKEN"
```

`tag` narrows the list to servers carrying that tag. `/v1/services` also takes `type` (`cloud` or `dedicated`), `status`, and `q`. `q` matches the label, hostname, primary IPv4 by prefix, service ID by prefix, and tag names.

### One server

```text theme={null}
GET /v1/cloud/{id}
GET /v1/dedicated/{id}
```

The `id` is the service ID from the list. It's the identifier for everything below.

## Cloud VPS

### State and network

```text theme={null}
GET /v1/cloud/{id}/servers     # the VM behind this service
GET /v1/cloud/{id}/network     # IP assignments
GET /v1/cloud/{id}/activity    # recent actions on the service
```

All of these need `services:read`.

### Metrics

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

`timeframe` is `hour`, `day`, `week` or `month`, and defaults to `hour`. Any other value falls back to `hour` rather than erroring. You get the current state plus a usage series for CPU, memory, disk and network. Requires `services:stats`.

The response also includes an estimated power draw and carbon figure. We work them out from vCPU load, datacenter PUE and the grid intensity of the country the node sits in. They're a fair estimate for reporting, but they aren't metered, and they aren't what you're billed on.

### Power

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

Requires `services:power`. `action` is one of:

| Action | What it does |
| - | - |
| `start` | Boots the VM. |
| `shutdown` | Asks the guest OS to shut down cleanly (ACPI). |
| `stop` | Hard power-off. It's like pulling the plug. |
| `reboot` | Asks the guest to restart. |
| `reset` | Hard reset, for a guest too wedged to answer `reboot`. |

If you want the machine off and its filesystem intact, use `shutdown`, not `stop`.

The call starts an async job on the hypervisor. When it returns, the VM hasn't necessarily finished booting.

> [!NOTE]
>
> The older `POST /v1/cloud/{id}/{vmid}` still works for clients that already store a vmid. New code should use `/power`, which never needs the hypervisor's internal number.

A suspended server (one with an unpaid invoice) answers `403` to power and every other management action until the invoice is settled.

### Credentials

```text theme={null}
GET /v1/cloud/{id}/credentials
```

This returns the root password and IP. It requires `services:rescue` and only works for the account that owns the server. A team role isn't enough. Every call is audited.

Treat the response like the secret it is. Don't log it, and don't put it in CI output.

### Snapshots

```bash theme={null}
# List
curl https://my.server.express/api/v1/cloud/$ID/snapshots \
  -H "Authorization: Bearer $SE_TOKEN"

# Take one
curl -X POST https://my.server.express/api/v1/cloud/$ID/snapshots \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"before-upgrade","description":"Pre PHP 8.4"}'
```

Listing needs `snapshots:read` and returns the snapshots newest first, plus your `limit` (how many a server may hold, usually 3). Taking, deleting and rolling back need `snapshots:write`.

A name starts with a letter and holds up to 40 letters, numbers, dashes and underscores.

```bash theme={null}
# Roll back. Everything written since the snapshot is gone.
curl -X POST https://my.server.express/api/v1/cloud/$ID/snapshots/before-upgrade \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"rollback"}'

# Delete
curl -X DELETE https://my.server.express/api/v1/cloud/$ID/snapshots/before-upgrade \
  -H "Authorization: Bearer $SE_TOKEN"
```

Rollback needs an explicit `{"action":"rollback"}` body, not a bare POST. Rollback is the most destructive thing you can do to a running server, so it shouldn't be something a mistyped request can trigger.

Taking, rolling back and deleting all answer `202` with a `taskId`. The job runs in the background, and a busy disk can take a while to freeze.

### Reinstall

```bash theme={null}
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. Get the valid list from `GET /v1/cloud/templates`, which is public and needs no auth. Requires `services:reinstall`.

> [!WARNING]
>
> **This wipes the disk.** There's no confirmation step and no undo. Your IPs and rDNS survive, and everything else goes.

A successful call answers `202` with a `taskId`, the template `os` and its `osName`. You'll get `409` if the server isn't active or something else (another reinstall, a snapshot) is already running on it, and `503` if the VM hasn't been built yet.

## Dedicated servers

```text theme={null}
GET /v1/dedicated/{id}/overview    # spec and status summary
GET /v1/dedicated/{id}/network     # IPs and rDNS
GET /v1/dedicated/{id}/metadata    # hardware detail
GET /v1/dedicated/{id}/activity    # recent actions
GET /v1/dedicated/{id}/rescue      # is it in rescue right now, and since when
```

All of these need `services:read`.

`GET /v1/dedicated/{id}/graphs?range=day` returns IPMI sensor readings (`day`, `week` or `month`) and requires `services:stats`. `traffic` is always `null`, because there's no switch-port monitoring behind our metal. We'd rather tell you that than draw a graph of zeros.

### Power

```bash theme={null}
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"}'
```

Requires `services:power`. `action` is `start`, `stop`, `reboot` or `reset`. `reset` is the physical reset line. Use it when the OS has stopped answering and `reboot` won't take.

### Rescue mode

```bash theme={null}
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"}'
```

This boots a rescue environment instead of the installed OS, so you can mount the disks and fix whatever broke. `DELETE /v1/dedicated/{id}/rescue` boots back into the installed system. Both need `services:rescue`, and so does `GET /v1/dedicated/{id}/credentials`.

### Reinstall

```bash theme={null}
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"}'
```

`osId` and `hostname` are both required. Requires `services:reinstall`. The same warning applies: the disks are wiped.

The list of valid OS IDs (`GET /v1/dedicated/{id}/profiles`) currently needs a browser session. Look the ID up once in the client area and store it in your config.

## Bulk power

```bash theme={null}
curl -X POST https://my.server.express/api/v1/services/power \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids":["cm4k2x9p10001","cm4k2x9p10002"],"action":"shutdown"}'
```

This works on cloud and dedicated servers in one call. `action` is `start`, `shutdown`, `stop`, `reboot` or `reset`, and the response carries a result for each ID. Requires `services:power`.

## Cancelling

```bash theme={null}
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}'
```

`/v1/dedicated/{id}/cancel` and `/v1/hosting/{id}/cancel` work the same way. All three require `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 the server now.** The service stops and the data is gone.

The default is `false` on purpose. If you pass `immediate` from a variable, make sure you know what's in it.

`reason` is optional and goes in our records. We do read it, so if something didn't work for you, this is a good place to say so.

## A worked example

Reboot every cloud server tagged `web`:

```bash theme={null}
#!/usr/bin/env bash
set -euo pipefail

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

curl -sf "$API/cloud?tag=web" -H "$AUTH" | jq -r '.data[].id' | while read -r id; do
  echo "rebooting $id"
  curl -sf -X POST "$API/cloud/$id/power" -H "$AUTH" \
    -H 'Content-Type: application/json' -d '{"action":"reboot"}' > /dev/null
done
```

That needs a token with `services:read` and `services:power`, and nothing else. That's the whole argument for scopes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.