# api

API Reference

Provision and manage dedicated servers from your terminal, CI, or any HTTP client. Everything in the UI is scriptable.

Authentication

All requests require a Bearer token in the Authorization header, except GET /status, which is public. Generate tokens from your dashboard under Profile β†’ Developer β†’ API token.

bash
curl https://bashrack.com/api/v1/orders \
  -H "Authorization: Bearer $BASHRACK_API_TOKEN" \
  -H "Content-Type: application/json"
Base URL: https://bashrack.com/api/v1
Rate limit: 60 requests / minute per token. Burst: 10.
Idempotency: POST /orders accepts an X-Idempotency-Key header β€” safe retries replay the first response for 24h.
IP allowlist: if you restrict your token to specific IPs, requests from other IPs get 403 ip_not_allowed.

MCP

The Streamable HTTP endpoint at https://bashrack.com/mcp provides read-only infrastructure discovery and planning tools. Public tools list the VPS catalog, estimate VPS, hosting, and database costs, and report platform status. The private server inventory tool uses an active same-origin bashrack browser session and omits IP addresses, API tokens, SSH keys, cloud-init data, and payment data.

Protocol: MCP 2026-07-28 with 2025-11-25 compatibility.
Transport: Streamable HTTP JSON responses.
Safety: MCP cannot order, modify, restart, reinstall, or delete resources.
Discovery: /.well-known/mcp.json
bash
curl https://bashrack.com/mcp \
  -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

MCP resources are available through resources/list and resources/read, including platform and billing guidance plus current managed-service pricing catalogs.

Endpoints

$ account & catalog

GET+PATCH/me

Your account: profile, role, per-user limits, and current wallet balance. GET is not rate-limited. PATCH updates allowedIps, maxServers, and monthlySpendLimit β€” API-token rotation and webhook URL/secret rotation stay console-only, since they require password re-authentication a Bearer token can't stand in for.

json
{
  "id": "usr_9f2...",
  "email": "[email protected]",
  "username": "you",
  "role": "user",
  "emailVerified": true,
  "allowedIps": null,
  "maxServers": 5,
  "monthlySpendLimit": 100.0,
  "balance": 12.34,
  "billingExempt": false,
  "webhookUrl": "https://your-app.com/webhooks/bashrack",
  "createdAt": "2026-05-30T08:12:00Z"
}

PATCH body β€” all fields optional: { "allowedIps": ["1.2.3.4"], "maxServers": 10, "monthlySpendLimit": 200 } β†’ returns the updated account (same shape as GET).

GET/status

Platform health: overall state, per-component status (platform, datacenter locations, monitored services) and any unresolved incidents. Public β€” no authentication required. Checks run every minute; responses are cached ~30s. Human-readable version at bashrack.com/status.

json
{
  "overall": "operational",
  "checkedAt": "2026-07-02T13:06:47.856Z",
  "components": [
    { "component": "db",      "label": "Database",     "group": "platform",  "status": "operational", "latencyMs": 12 },
    { "component": "queue",   "label": "Job queue",    "group": "platform",  "status": "operational", "latencyMs": 8 },
    { "component": "loc:fra", "label": "πŸ‡©πŸ‡ͺ Frankfurt", "group": "locations", "status": "operational", "latencyMs": 254 }
  ],
  "activeIncidents": []
}

overall is one of operational / degraded / outage / unknown; component status additionally allows down and maintenance.

GET/locations

List all data-center locations with region codes.

json
[
  { "code": "fra", "city": "Frankfurt", "country": "Germany", "flag": "πŸ‡©πŸ‡ͺ" },
  { "code": "ams", "city": "Amsterdam", "country": "Netherlands", "flag": "πŸ‡³πŸ‡±" },
  { "code": "nyc", "city": "New York", "country": "USA", "flag": "πŸ‡ΊπŸ‡Έ" },
  { "code": "lax", "city": "Los Angeles", "country": "USA", "flag": "πŸ‡ΊπŸ‡Έ" },
  { "code": "sgp", "city": "Singapore", "country": "Singapore", "flag": "πŸ‡ΈπŸ‡¬" },
  { "code": "tyo", "city": "Tokyo", "country": "Japan", "flag": "πŸ‡―πŸ‡΅" }
]
GET/os

List available operating systems. All images support cloud-init.

json
[
  { "id": "ubuntu-24.04", "label": "Ubuntu 24.04 LTS", "family": "linux" },
  { "id": "ubuntu-22.04", "label": "Ubuntu 22.04 LTS", "family": "linux" },
  { "id": "debian-13",    "label": "Debian 13 (Trixie)", "family": "linux" },
  { "id": "debian-12",    "label": "Debian 12 (Bookworm)", "family": "linux" },
  { "id": "rocky-9",      "label": "Rocky Linux 9", "family": "linux" },
  { "id": "almalinux-9", "label": "AlmaLinux 9", "family": "linux" },
  { "id": "fedora-42",   "label": "Fedora 42", "family": "linux" },
  { "id": "alpine-3.20", "label": "Alpine Linux 3.20", "family": "linux" }
]
GET/tiers

List VPS tier categories (shared, dedicated, performance) with pricing multipliers and default specs.

json
[
  {
    "id": "shared",
    "name": "Shared",
    "config": { "cores": 2, "ram": 4, "storageGb": 80, "bandwidth": 1 }
  },
  {
    "id": "dedicated",
    "name": "Dedicated",
    "config": { "cores": 8, "ram": 32, "storageGb": 320, "bandwidth": 10 }
  },
  {
    "id": "performance",
    "name": "Performance",
    "config": { "cores": 16, "ram": 64, "storageGb": 800, "bandwidth": 10, "unmetered": true }
  }
]

$ orders

POST/orders

Create a new server order. Returns the order object with provisioning status.

Request body
json
{
  "category": "dedicated",
  "cores": 4,
  "ram": 16,
  "disk": 240,
  "storage": "nvme",
  "bw": "5g",
  "loc": "fra",
  "os": "ubuntu-24.04",
  "billing_cycle": "hourly",
  "cloud_init": {
    "ci_user": "deploy",
    "ssh_keys": "ssh-ed25519 AAAA...",
    "password": "mysecretpass"
  }
}
Response 201
json
{
  "id": "ord_2vR9kP...",
  "server_id": "srv_4xKm9Q...",
  "status": "provisioning",
  "config": {
    "cores": 4,
    "ram": 16,
    "disk": 240,
    "storage": "nvme",
    "bw": "5g",
    "loc": "fra",
    "os": "ubuntu-24.04"
  },
  "price": {
    "hourly": 0.145833
  },
  "created_at": "2026-06-12T09:41:00Z"
}

category is one of shared, dedicated, or performance (see GET /tiers) β€” defaults to dedicated if omitted. Use bw format <number>g or <number>g-unmetered. Storage can be ssd or nvme. All orders are billed hourly β€” no other billing cycles exist. Send an X-Idempotency-Key header to make retries safe β€” the first response is replayed for 24h; a duplicate key that is still in flight returns 409 concurrent_request.

cloud_init is optional. All three sub-fields are optional: ci_user (lowercase, max 32 chars), ssh_keys (one or more public keys, newline-separated, max 16 KB), and password (8–128 chars, stored encrypted). If omitted, the OS image default credentials apply.

GET/orders

List your active server orders. Supports ?status=active|terminated|provisioning.

json
[
  {
    "id": "ord_2vR9kP...",
    "status": "active",
    "ipv4": "185.203.114.12",
    "config": { "cores": 4, "ram": 16, "loc": "fra", "os": "ubuntu-24.04" },
    "price": { "hourly": 0.145833 },
    "created_at": "2026-06-12T09:41:00Z"
  }
]
GET/orders/{id}

Get a single order by ID. Includes live status and assigned IP addresses.

json
{
  "id": "ord_2vR9kP...",
  "status": "active",
  "ipv4": "185.203.114.12",
  "config": { "cores": 4, "ram": 16, "loc": "fra", "os": "ubuntu-24.04" },
  "price": { "hourly": 0.145833 },
  "created_at": "2026-06-12T09:41:00Z",
  "ready_at": "2026-06-12T09:52:00Z"
}
DELETE/orders/{id}

Terminate a server. Billing stops immediately. This action is irreversible.

json
{
  "id": "ord_2vR9kP...",
  "status": "terminated",
  "terminated_at": "2026-06-12T14:22:00Z"
}

$ servers

GET/servers/{id}

Get live server details including status, IP address, and resource config. Use server_id from the order response as the ID.

tip β€” get server_id from POST /orders or GET /orders/{id} response

json
{
  "id": "srv_...",
  "hostname": "srv-lt1-a3f9c2",
  "status": "running",
  "ipv4": "185.203.114.12",
  "os": "ubuntu-24.04",
  "location": "lt1",
  "config": {
    "cores": 4,
    "ram": 16,
    "disk": 240,
    "storage": "nvme",
    "bandwidth": 10
  },
  "cost": { "monthly": 120.00, "hourly": 0.166667 },
  "created_at": "2026-06-20T10:00:00Z",
  "terminated_at": null
}
POST/servers/{id}/action

Control a running or stopped server. Use server_id from the order response as the ID. Actions are applied asynchronously β€” poll GET /servers/{id} for the final status.

Request body
json
{
  "action": "reboot"
}
startstoppedBoot a stopped server
shutdownrunningGraceful OS shutdown
rebootrunningGraceful restart
resetrunningHard reset (no OS flush)
stoprunningForce power-off
Response 200
json
{
  "id": "srv_...",
  "status": "rebooting"
}

Status transitions: starting β†’ running, stopping β†’ stopped, rebooting / resetting β†’ running

POST/servers/{id}/reinstall

Reinstall the server's OS from a fresh image. Pass osId (see GET /os) to switch images, or omit it to reinstall the current one. All data on disk is destroyed. Async β€” poll GET /servers/{id} for status.

Request body (optional)
json
{
  "osId": "debian-12"
}
Response 200
json
{
  "ok": true
}

409 terminated if the server is terminated, 409 server_provisioning / server_terminating / server_queued while another operation is in flight.

POST/servers/{id}/rename

Set the server's hostname.

Request body
json
{
  "hostname": "web-prod-01"
}
Response 200
json
{
  "ok": true
}

hostname: 3–80 chars, lowercase letters, digits, dots and dashes (^[a-z0-9][a-z0-9.-]*[a-z0-9]$).

POST/servers/{id}/star

Toggle the starred flag on a server (pins it in your dashboard). No request body.

json
{
  "starred": true
}
POST/servers/{id}/console/ticket

Mint a short-lived VNC ticket for the web console. The server must be running. Connect any RFB/noVNC client over the WebSocket proxy β€” the ws endpoint authenticates with your dashboard session cookie (browser), not the API token.

Response 200
json
{
  "ws_path": "/api/v1/console/ws",
  "ticket": "PVE:VNC:6A2F...",
  "port": 5900,
  "node": "fra-r12-b07"
}
Connect
bash
wss://bashrack.com/api/v1/console/ws?serverId=<id>&ticket=<urlencoded ticket>&port=5900&node=<node>
# pass the ticket again as the RFB password

409 not_running if the server is stopped, 409 not_provisioned before first deploy finishes.

GET+PATCH/servers/{id}/firewall

Per-VM firewall via Proxmox's own firewall API. GET returns options, rules, and network interfaces. PATCH updates options. POST /servers/{id}/firewall/rules adds a rule; PATCH/DELETE /servers/{id}/firewall/rules/{pos} update or remove one by position. POST /servers/{id}/firewall/net/{iface} toggles the firewall on one NIC (e.g. net0).

GET response 200
json
{
  "options": { "enable": 1, "policy_in": "DROP", "policy_out": "ACCEPT" },
  "rules": [
    {
      "pos": 0,
      "type": "in",
      "action": "ACCEPT",
      "enable": 1,
      "dport": "22",
      "proto": "tcp",
      "comment": "SSH"
    }
  ],
  "netDevices": [{ "iface": "net0", "firewall": true }]
}
POST /firewall/rules body β†’ 201
json
{
  "type": "in",
  "action": "ACCEPT",
  "dport": "80,443",
  "proto": "tcp",
  "comment": "web"
}

β†’ { "ok": true }
GET+PATCH/servers/{id}/cloud-init

Change SSH keys, network config (static or DHCP), nameserver, and hostname after initial provisioning. Pure cloud-init config write β€” no guest agent, no command execution. Proxmox derives the cloud-init instance-id from a hash of this config, so changing it makes cloud-init treat the VM's next reboot as a first boot and re-apply it β€” reliably, across every distro. This only writes config; call POST /servers/{id}/action {"action":"reboot"} afterward to actually apply it.

GET response 200
json
{
  "sshKeys": "ssh-ed25519 AAAA...",
  "ipConfig": "ip=dhcp",
  "nameserver": null,
  "hostname": "my-server"
}
PATCH body β†’ 200
json
{
  "hostname": "web-prod-01",
  "ipConfig": { "mode": "static", "ip": "203.0.113.10", "cidr": 24, "gateway": "203.0.113.1" },
  "sshKeys": "ssh-ed25519 AAAA...",
  "nameserver": "1.1.1.1 8.8.8.8"
}

β†’ { "ok": true, "rebootRequired": true }

ipConfig is either { "mode": "dhcp" } or { "mode": "static", "ip", "cidr", "gateway" }. Root password reset (see the console) uses the QEMU guest agent instead of cloud-init, for an immediate effect that doesn't need a reboot.

GET+POST/servers/{id}/snapshots

List and create disk snapshots. Create requires a positive wallet balance. Names: 1–40 chars, letters, digits, _ and -. Deleting: DELETE /servers/{id}/snapshots/{snapshotId}. Rolling back: POST /servers/{id}/snapshots/{snapshotId}/rollback (snapshot must be active; the server reboots).

GET response 200
json
[
  {
    "id": "snp_7Qw...",
    "name": "pre-upgrade",
    "description": "before ubuntu 26.04",
    "sizeGb": 12,
    "status": "active",
    "createdAt": "2026-07-01T10:20:00Z"
  }
]
POST body β†’ 201
json
{
  "name": "pre-upgrade",
  "description": "optional, max 255 chars"
}

β†’ {
  "id": "snp_7Qw..."
}

Snapshot status is one of creating, active, rolling_back, deleting, failed. Creating on an empty wallet β†’ 402 insufficient_balance.

$ billing

GET/wallet

Current balance plus paginated wallet transactions. Query params: ?page=0 (default 0), ?limit=50 (1–200).

json
{
  "balance": 12.34,
  "transactions": [
    {
      "id": "txn_3Kd...",
      "type": "topup",
      "amount": 10.0,
      "balanceAfter": 22.34,
      "provider": "paypal",
      "status": "completed",
      "createdAt": "2026-07-10T12:00:00Z"
    },
    {
      "id": "txn_9Lw...",
      "type": "hourly_charge",
      "amount": -0.061,
      "balanceAfter": 12.34,
      "provider": null,
      "status": "completed",
      "createdAt": "2026-07-11T13:00:00Z"
    }
  ],
  "hasMore": false,
  "page": 0
}

type is one of topup, hourly_charge, order_charge, admin_adjustment.

POST/wallet/topups

Start a wallet top-up. Returns a checkout URL β€” redirect the user there to pay. Amount: 5–10,000 USD. Provider: paypal or nowpayments (crypto).

Request body
json
{
  "amount": 25,
  "provider": "paypal"
}
Response 200
json
{
  "transactionId": "txn_3Kd...",
  "provider": "paypal",
  "approveUrl": "https://www.paypal.com/checkoutnow?token=..."
}

nowpayments returns invoiceUrl instead. The balance is credited by the payment webhook once the payment clears.

GET+POST/wallet/auto-topup

Automatic top-up when the balance drops below a threshold. GET returns the current config; POST updates it (threshold 0–10,000, amount 5–10,000). Only the NOWPayments provider is supported β€” crypto auto top-up emails an invoice rather than charging silently.

GET response 200
json
{
  "enabled": true,
  "threshold": 5,
  "amount": 20,
  "provider": "nowpayments"
}
POST body β†’ 200
json
{
  "enabled": true,
  "threshold": 5,
  "amount": 20,
  "provider": "nowpayments"
}

β†’ { "ok": true }
GET/wallet/payment-methods

Which payment providers are enabled on this deployment. Not rate-limited.

json
{
  "paypal": true,
  "nowpayments": true
}
GET/invoices

List your invoices, newest first. GET /invoices/{id} returns a single invoice (404 if not yours).

json
[
  {
    "id": "inv_5Rt...",
    "amount": 25.0,
    "description": "Wallet top-up",
    "status": "paid",
    "creditWallet": true,
    "dueDate": "2026-07-15",
    "sentAt": "2026-07-08T09:00:00Z",
    "paidAt": "2026-07-08T09:04:00Z",
    "createdAt": "2026-07-08T09:00:00Z",
    "invoiceNumber": "BRK-20260708-A1B2C3D4",
    "paymentProvider": "nowpayments",
    "paymentUrl": "https://nowpayments.io/payment/...",
    "paypalInvoiceNumber": null
  }
]

status is one of draft, sent, paid, cancelled.

$ support

GET+POST/tickets

List and create support tickets. Create: subject 5–200 chars, body 10–10,000 chars, category billing | technical | general (default) | abuse. GET /tickets/{id} includes the full message thread, POST /tickets/{id}/reply appends a message (reopens pending/resolved tickets), POST /tickets/{id}/close closes it.

GET response 200
json
[
  {
    "id": "tkt_2Fd...",
    "subject": "Cannot SSH",
    "status": "open",
    "priority": "normal",
    "category": "technical",
    "messageCount": 3,
    "createdAt": "2026-07-12T08:00:00Z",
    "updatedAt": "2026-07-12T09:30:00Z"
  }
]
POST body β†’ 201
json
{
  "subject": "Cannot SSH",
  "body": "Connection refused since 08:00 UTC …",
  "category": "technical"
}

β†’ { "id": "tkt_2Fd..." }

Replying to a closed ticket β†’ 409 ticket_closed.

GET/sessions

List active dashboard sessions for your account (newest first). DELETE /sessions/{sid} revokes one β€” use the sid from this list. Note: API-token calls carry no session cookie, so current is always false here.

json
[
  {
    "sid": "sess_8Hm...",
    "current": false,
    "createdAt": "2026-07-14T19:22:00Z",
    "ip": "203.0.113.10",
    "ua": "Firefox 140 / Linux",
    "ttl": 604800
  }
]

$ organizations

GET/organizations

List the organizations you belong to.

json
[
  {
    "id": "org_4Nq...",
    "name": "Acme Inc",
    "slug": "acme-inc",
    "role": "owner",
    "isOwner": true
  }
]

role is one of owner, admin, member, billing, read_only.

GET/organizations/{id}

Organization detail with members. Pending invites are included only when your role is owner or admin.

json
{
  "id": "org_4Nq...",
  "name": "Acme Inc",
  "slug": "acme-inc",
  "ownerUserId": "usr_1Ab...",
  "role": "owner",
  "members": [
    {
      "userId": "usr_1Ab...",
      "username": "alice",
      "role": "owner",
      "isOwner": true,
      "joinedAt": "2026-06-01T10:00:00Z"
    }
  ],
  "invites": [
    {
      "id": "inv_9Zx...",
      "email": "[email protected]",
      "role": "member",
      "invitedBy": "usr_1Ab...",
      "expiresAt": "2026-07-08T10:00:00Z",
      "createdAt": "2026-07-01T10:00:00Z"
    }
  ]
}
POST+DELETE/organizations/{id}/invites

Invite a member (owner/admin only, 7-day expiry, emailed to the invitee). DELETE /organizations/{id}/invites/{inviteId} revokes a pending invite. POST /organizations/invites/accept joins the caller using the emailed token.

POST /organizations/{id}/invites β†’ 201
json
{
  "email": "[email protected]",
  "role": "member"
}

β†’ { "id": "inv_9Zx..." }
POST /organizations/invites/accept β†’ 200
json
{
  "token": "a1b2c3…"
}

β†’ { "organizationId": "org_4Nq..." }

An already-used invite β†’ 409 invite_already_used; a revoked or expired one β†’ 410 invite_revoked / invite_expired.

PATCH+DELETE/organizations/{id}/members/{userId}

Change a member's role (PATCH) or remove them (DELETE) β€” owner/admin only. POST /organizations/{id}/leave removes the caller. The organization's owner can't be role-changed, removed, or leave this way β€” ownership transfer is console-only.

PATCH body β†’ 200
json
{
  "role": "admin"
}

β†’ { "ok": true }
DELETE / leave β†’ 200
json
{ "ok": true }

$ hosting

GET+POST/hosting

List and create web hosting apps (Coolify-backed). appType is a discriminated union: dockerimage, nixpacks, static, or dockercompose. resourceTier (small/medium/large) selects a CPU/RAM tier; small is included, medium/large add a monthly surcharge. GET /hosting/{id} returns full details, PATCH /hosting/{id} changes the resource tier and/or domains, DELETE /hosting/{id} terminates (bills the final partial hour). POST /hosting/{id}/action starts/stops/restarts, POST /hosting/{id}/star toggles starred, GET /hosting/{id}/logs?lines=200 tails logs.

GET response 200
json
[
  {
    "id": "app_3Fk...",
    "coolifyUuid": "a1b2c3d4",
    "name": "my-app",
    "status": "running",
    "domains": ["https://my-app.example.com"],
    "costMonthly": 5.0,
    "starred": false,
    "createdAt": "2026-07-01T10:00:00Z",
    "managementUrl": "https://coolify.internal/project/.../application/a1b2c3d4"
  }
]
POST body β†’ 201
json
{
  "name": "my-app",
  "storageGb": 5,
  "appType": "nixpacks",
  "gitRepository": "https://github.com/me/my-app",
  "gitBranch": "main"
}

β†’ { "id": "app_3Fk..." }

Insufficient wallet balance β†’ 402 insufficient_balance. Web hosting not configured on this deployment β†’ 503 hosting_unavailable.

GET+POST/hosting/{id}/env

List (values redacted) or set an app's environment variables. POST /hosting/{id}/env/{envUuid}/reveal returns the real value; DELETE /hosting/{id}/env/{envUuid} removes it. Keys must be a valid env var name (letters, digits, underscore).

POST body β†’ 201
json
{
  "key": "DATABASE_URL",
  "value": "postgres://...",
  "isBuildTime": false
}
POST .../reveal β†’ 200
json
{ "value": "postgres://..." }

$ databases

GET+POST/databases

List and create managed databases (Coolify-backed). Self-service dbType: postgresql, mysql, mariadb, redis, mongodb, keydb, dragonfly, clickhouse. resourceTier (small/medium/large) selects a CPU/RAM tier; small is included, medium/large add a monthly surcharge. GET /databases/{id} includes connection credentials. PATCH /databases/{id} changes the resource tier. DELETE /databases/{id} terminates. POST /databases/{id}/action starts/stops/restarts, POST /databases/{id}/star toggles starred, GET /databases/{id}/logs?lines=200 tails logs, POST /databases/{id}/public-access { "enabled": true } opens a random public port (49200–59999).

POST body β†’ 201
json
{
  "dbType": "postgresql",
  "name": "my-db",
  "storageGb": 5
}

β†’ { "id": "db_9Qw..." }
GET /databases/{id} β†’ 200 (excerpt)
json
{
  "id": "db_9Qw...",
  "dbType": "postgresql",
  "status": "running",
  "isPublic": false,
  "credentials": {
    "user": "postgres",
    "password": "…",
    "database": "postgres",
    "host": "…",
    "port": 5432
  }
}

$ dedicated servers

GET/dedicated

Public, unauthenticated: list published bare-metal dedicated server listings. Fully white-label β€” provider identity never appears here. Addon key/groupKey/requiresKey are opaque per-listing tokens; echo the exact key back, don't construct your own.

GET response 200 (excerpt)
json
[
  {
    "id": "b3f1...-...-...-...",
    "displayName": "12 Core Server - AMD Ryzen 5900X",
    "locationLabel": "Miami, FL, USA",
    "specs": { "cpuModel": "AMD Ryzen 5900X @ 3.7GHz", "cpuCores": 12, "cpuThreads": 24, "ramGb": 64, "disks": [{ "type": "nvme", "capacityGb": 1000, "count": 2 }], "bandwidthGbps": 1 },
    "addons": [{ "key": "2ced763baa757a92174b", "label": "5TB Bandwidth @ 1 Gbps", "kind": "choice", "groupKey": "…", "providerPriceUsd": 0, "sellPriceUsd": 0 }],
    "monthlyPriceUsd": 147.15,
    "available": true
  }
]
GET+POST/dedicated/orders

List or place dedicated server orders. Ordering charges the first monthly cycle from your wallet immediately; fulfillment is manual (admin-driven against the wholesale provider) and can take up to 48 hours. GET /dedicated/orders/{id} returns one order β€” status moves queued β†’ active β†’ past_due β†’ suspended, or cancelled. Recurring billing charges the same wallet once per cycle; a failed charge gets a 7-day grace period before suspension.

POST body β†’ 201
json
{
  "stockId": "b3f1...-...-...-...",
  "addonKeys": ["2ced763baa757a92174b"]
}

β†’ { "id": "5bde...-...", "status": "queued", "price": { "monthly": 147.15 } }
GET /dedicated/orders/{id} β†’ 200 (excerpt)
json
{
  "id": "5bde...-...",
  "status": "active",
  "ip": "203.0.113.10",
  "credentials": null,
  "nextChargeAt": "2026-10-03T20:53:07Z",
  "capabilities": { "restart": true, "rescue": true, "reinstall": true }
}

Insufficient wallet balance β†’ 402 insufficient_balance. Listing bought by someone else first β†’ 409 no_longer_available. Bad or missing addon selection β†’ 400 invalid_request.

POST+GET/dedicated/orders/{id}/action

Once an order is active and fulfilled (check capabilities), POST .../action with { "action": "restart" | "rescue" | "reinstall" } to power-cycle, enter rescue mode, or reinstall the OS. reinstall needs dist (an entry from GET .../reinstall-options' distOptions/osOptions) or, when its flow is os_and_partitioning, both operatingSystemId and partitioningSchemeId β€” fetch those first from GET .../reinstall-options and GET .../partitioning-schemes?operatingSystemId={id}.

POST .../action body β†’ 200
json
{ "action": "reinstall", "dist": "ubuntu-24.04" }

β†’ { "ok": true }
GET .../reinstall-options β†’ 200
json
{ "flow": "os_only", "distOptions": ["ubuntu-24.04", "debian-12"] }

Not active, not fulfilled yet, or unsupported by this listing's provider β†’ 409 action_not_allowed.

Webhooks

Subscribe to events in your dashboard. We POST a signed JSON payload to your endpoint. Verify the signature using your webhook secret.

order.provisionedServer is online and reachable.
order.terminatedServer was deleted by you or billing failure.
json
{
  "event": "order.provisioned",
  "data": {
    "id": "ord_2vR9kP...",
    "ipv4": "185.203.114.12",
    "ready_at": "2026-06-12T09:52:00Z"
  },
  "signature": "sha256=abc123..."
}

Errors

Errors return a consistent JSON shape with an HTTP status and a machine-readable code.

CodeStatusMeaning
invalid_request400Missing or malformed parameters.
unauthenticated401Missing or invalid Bearer token.
insufficient_balance402Wallet balance too low for this operation.
monthly_spend_limit_reached402Your monthly spend cap is exhausted β€” raise it in your profile.
forbidden403Valid token, but not allowed for this resource.
ip_not_allowed403Token is restricted to an IP allowlist.
not_found404Order or resource does not exist.
insufficient_capacity409No free capacity in the selected location.
server_limit_reached409You hit your per-account server limit.
concurrent_request409Same idempotency key is already being processed β€” retry later.
terminated409The server is terminated; the operation is not allowed.
ticket_closed409The support ticket is closed.
rate_limited429Too many requests. Retry after the Retry-After header.
internal_error500Something went wrong on our side. Retry with idempotency key.