Skip to content

API reference

Everything the operator console and customer portal do goes through the same public, versioned API. If the UI can do it, a script can too.

  • Base path: /api/v1, JSON in and out.
  • Interactive documentation: a full OpenAPI 3.1 document, generated directly from the server’s handlers, is published at /api/docs on your panel hostname.

Two ways to authenticate:

Method Header Use for
Session cookie Cookie + X-CSRF-Token The web UI itself
API token Authorization: Bearer opk_... Scripts, integrations, external billing systems

Create an API token under Account → API tokens or POST /api/v1/api-tokens. A token is scoped to a set of permissions and, optionally, to a single customer; it expires (90 days by default if you don’t set one, never more than a year).

Scope Grants
customer:read, customer:manage Read / change customer details
members:manage Manage a customer’s team
billing:read, billing:manage Read / change billing and subscriptions
site:read, site:create, site:update, site:delete Site lifecycle
site:files, site:shell File manager/SFTP, and shell/exec/WP-CLI
database:read, database:manage Databases and database users
certificate:read, certificate:manage Custom TLS certificates
job:read Job status and progress
audit:read The audit log
* Every permission the creating user has

Platform and brand staff have additional scopes for their own administrative areas (nodes, plans, brands, and so on) — see the OpenAPI document at /api/docs for the full, current list.

Errors follow RFC 9457 (application/problem+json):

{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"detail": "One field failed validation.",
"errors": [
{ "location": "body.limits.diskMB", "message": "Must be at least 0" }
]
}

A refusal that names a specific reason also carries a stable machine-readable code per item in errors, so clients can branch on it instead of parsing detail.

List endpoints use cursor pagination: pass cursor from a previous response’s nextCursor to fetch the next page. Lists are always filtered to what the caller may see — an API token scoped to one customer never sees another’s rows, whatever it asks for.

Send an Idempotency-Key header on POST requests that create something (a site, a job, a checkout) to make retries safe: repeating the same request with the same key returns the original result instead of creating a duplicate.

Terminal window
curl https://panel.example.com/api/v1/sites \
-H "Authorization: Bearer opk_your_token_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"customerId": "018f...",
"name": "my-site",
"type": "wordpress",
"domain": "example.com"
}'
{
"id": "018f...",
"handle": "mysite",
"type": "wordpress",
"status": "provisioning",
"customerId": "018f...",
"createdAt": "2026-09-24T12:00:00Z"
}
Endpoint Purpose
GET /api/v1/capabilities What this installation supports, the caller’s effective permissions, and current limits
GET /api/v1/usage Cheap dashboard counts: customers, sites, domains, databases, recent traffic
GET /api/v1/audit The audit log, filterable by actor, action, subject and time range
GET /api/docs The full OpenAPI 3.1 document

Outgoing webhooks (site.created, job.finished, subscription.suspended, and more) are available for integrations that want to react to events rather than poll — configure them under Admin → Integrations.