# 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.

Endpoints

GET/me

Your account: profile, role, per-user limits, and current wallet balance. Not rate-limited.

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"
}
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 }
  }
]
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.089
  },
  "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.089 },
    "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.089 },
  "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"
}
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": 45.00, "hourly": 0.0625 },
  "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

$ servers

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+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). PayPal auto top-up requires a saved PayPal vault (see hasPaypalVault).

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

β†’ { "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",
    "paypalInvoiceNumber": "INV2-XXXX-XXXX"
  }
]

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
  }
]

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.