Open Source|All improvements go through Pull Requests with automated CI checks before review by@Leo-Galli|Join Discord|Contributing guide
rest-api
REST API reference

REST API reference

The Aetheris control panel exposes a JSON REST API. This page summarizes the platform endpoints; the machine-readable contract is the bundled OpenAPI 3.1 specification.

Conventions

  • Base URL: https://app.example.com/api.
  • Authentication: Authorization: Bearer <session> for platform endpoints; payment webhooks use provider signatures.
  • Content type: application/json on requests and responses.
  • Errors: { "error": { "code": string, "message": string, "details"?: object } } with appropriate HTTP status codes.

Endpoints

Whitelabel

MethodPathDescription
GET/whitelabelDynamic whitelabel configuration for the tenant (?organization=<slug>)
PUT/admin/whitelabelUpdate brand, theme, navigation and module toggles

Servers

MethodPathDescription
GET/serversList the caller's servers
POST/serversProvision a server (plan + node selection)
GET/servers/{id}Server detail with resources and state
DELETE/servers/{id}Terminate the workload
POST/servers/{id}/powerBody { "signal": "start" | "stop" | "restart" | "kill" }
GET/servers/{id}/telemetryLatest telemetry sample
GET/servers/{id}/consoleConsole session (WebSocket URL + one-time token)
GET/servers/{id}/backupsList backups
POST/servers/{id}/backupsCreate a backup, body { "name": string }
POST/servers/{id}/backups/{backupId}/restoreRestore a backup
DELETE/servers/{id}/backups/{backupId}Delete a backup

Billing

MethodPathDescription
GET/billing/summaryMRR, outstanding, overdue and collected amounts
GET/billing/invoicesList invoices with lines and payments
GET/billing/invoices/{id}Single invoice detail
POST/billing/invoicesCreate an invoice with lines, VAT and an optional coupon
POST/billing/invoices/{id}/paySettle an invoice (direct/demo payment)
POST/billing/invoices/{id}/refundRefund a paid invoice (admin)
GET/billing/couponsList coupons
POST/billing/couponsCreate a coupon (admin)
DELETE/billing/coupons/{id}Disable a coupon (admin)
POST/billing/webhooks/{provider}Idempotent payment webhook ingress (stripe, paypal, mollie)
POST/billing/dunning/runRun the dunning state machine (admin)
GET/billing/dunning/statusInvoice status counts and grace period

Invoice creation bodies use { "client", "currency", "due_days", "coupon_code", "lines": [{ "description", "quantity", "unit_cents", "tax_rate_pct" }] }. Webhook bodies use { "event", "payment_id", "invoice_number" | "invoice_id", "amount_cents", "currency" } with event one of payment.succeeded, payment.failed, payment.refunded.

Catalog (game hosting)

MethodPathDescription
GET/catalog/gamesGame catalog with resource presets and pricing
GET/catalog/games/{slug}Single game entry (metadata, image, presets)

See Game hosting for the full catalog and the provisioning flow.

Admin

MethodPathDescription
GET/admin/nodesNode list with utilization
POST/admin/hypervisorsRegister a hypervisor credential
POST/admin/hypervisors/{id}/syncSynchronize nodes and eggs from the backend
GET/admin/auditAudit log stream
PUT/admin/settingsPlatform-level settings

System

MethodPathDescription
GET/system/statusVersion, latest GitHub release and update availability
GET/system/cronList scheduled jobs (cron)
POST/system/cronCreate a scheduled job
PATCH/system/cron/{id}Update a scheduled job
DELETE/system/cron/{id}Delete a scheduled job
POST/system/cron/{id}/runTrigger a job immediately
GET/system/sftpList SFTP file-access users
POST/system/sftpCreate an SFTP user
PATCH/system/sftp/{id}Update an SFTP user
DELETE/system/sftp/{id}Delete an SFTP user

Cron job bodies use the shape { "name", "schedule", "task", "enabled" } where schedule is a five-field cron expression and task is one of backup, invoice.dunning, snapshot.prune, sync.pterodactyl, sync.proxmox, sync.virtfusion, report.daily. SFTP user bodies use { "server_id", "username", "home_path", "enabled" }.

// GET /system/status
{
  "version": "1.0.0",
  "latest_release": { "tag": "v1.1.0", "url": "...", "published_at": "2026-08-15T10:00:00Z" },
  "update_available": true,
  "environment": "production",
  "healthy": true
}

Webhooks

ProviderPathSignature header
Stripe/webhooks/stripeStripe-Signature
PayPal/webhooks/paypalPAYPAL-TRANSMISSION-SIG
Mollie/webhooks/mollieHMAC in Authorization

Webhook handlers verify signatures, enqueue billing jobs and return 200 quickly; processing happens in the aetheris.billing queue.

Status codes

CodeMeaning
200Success
201Resource created
202Accepted for background processing
400Validation failure
401Missing or invalid credentials
403Insufficient role
404Resource not found
409State conflict (e.g. suspend on a terminated server)
429Rate limited
500Backend error
502Upstream hypervisor error

Authentication

Sessions (interactive clients)

  1. POST /auth/login with { "email": string, "password": string }.
  2. The response contains an access token (15 minute lifetime) and a refresh token (rotation on every use, revocable).
  3. Send the access token as Authorization: Bearer <token> on every request. When it expires, exchange the refresh token at POST /auth/refresh.

API keys (machines and scripts)

Create an API key in the Admin Panel or via POST /admin/api-keys:

Authorization: Bearer <admin-jwt>
 
POST /admin/api-keys
Content-Type: application/json
 
{ "label": "deploy-bot", "scopes": ["servers:write", "billing:read"] }

API keys never expire by default but can be revoked at any time; the audit log records every use. Keep them in your secret manager.

Pagination and filtering

List endpoints are paginated. Query parameters:

ParameterDefaultDescription
page11-based page number
per_page50Page size, max 100
sortcreated_at:descfield:asc or field:desc
filter-key=value pairs, comma separated

Example:

GET /billing/invoices?page=2&per_page=25&sort=due_date:asc&filter=status=open

Responses include a pagination envelope:

{
  "data": [],
  "meta": { "page": 2, "per_page": 25, "total": 312, "total_pages": 13 }
}

Idempotency

Write operations accept an Idempotency-Key header. Retrying the same request with the same key returns the original result instead of executing again - essential for provisioning and payments from scripts with retries.

POST /servers
Idempotency-Key: deploy-2026-08-20-01
Content-Type: application/json

Rate limits

ScopeLimitWindow
Login5 attempts15 minutes per account
Public endpoints60 requests1 minute per IP
Authenticated API600 requests1 minute per key
Payment endpoints20 requests1 minute per account

Exceeding a limit returns 429 with a Retry-After header.

Error format

All errors use a consistent shape:

{
  "error": {
    "code": "node_unreachable",
    "message": "Node fra-01 did not respond",
    "details": { "node_id": "fra-01", "attempts": 3 }
  }
}

Machine-readable code values are stable across versions; message is human-facing and may change.

Example: provision a server

TOKEN=$(curl -sS -X POST http://app.example.com/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"ops@example.com","password":"secret"}' \
  | jq -r '.accessToken')
 
curl -sS -X POST http://app.example.com/api/servers \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Idempotency-Key: deploy-001' \
  -H 'Content-Type: application/json' \
  -d '{"plan":"vps-4","node":"fra-01","egg":"nodejs-20"}'

SDK clients

Generated clients can be produced from openapi.yaml with any OpenAPI generator (openapi-generator, orval, openapi-typescript). The typed driver contracts in src/lib/adapters/hypervisors/types.ts are the canonical TypeScript source for hypervisor-facing shapes.