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/docson your panel hostname.
Authentication
Section titled “Authentication”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).
Scopes
Section titled “Scopes”| 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
Section titled “Errors”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.
Pagination
Section titled “Pagination”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.
Idempotency
Section titled “Idempotency”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.
Example
Section titled “Example”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"}Useful endpoints to know about
Section titled “Useful endpoints to know about”| 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.