Overview
One HTTP API behind every console. Agentic ready: every route self-documented, typed, scoped, danger-rated.
- Base
https://panel.example.com/api/v1, any node, same state.
- OpenAPI
/api/v1/openapi.json (3.0), generated from the route registry. Undocumented route = server refuses to start.
- Swagger UI
/docs/. Each operation: summary, schemas, x-min-role, scope, danger level.
- Planned routes
[PLANNED], x-status: planned, answer 501.
- Version
GET /version: release, node, implemented and planned route counts.
- Not availableMCP server · webhooks · flows · idempotency keys.
Authentication and tokens
| Session | API token |
| Get | POST /auth/login (+ /auth/2fa/verify) | POST /auth/tokens or ozpanel admin token |
| Send | Cookie + X-CSRF-Token on writes | Authorization: Bearer ozp_… |
| Authority | Full role | Role narrowed by scopes |
| Dangerous routes | By role | Only with allow_dangerous |
| Lifetime | 12 h sliding | Until expires_at or revoked |
POST /auth/tokens {"name":"billing","scopes":["tenants:write","provision:write","stats:read"],
"allowed_ips":["203.0.113.10"],"expires_at":"2027-01-01T00:00:00Z","allow_dangerous":false}
curl -H 'Authorization: Bearer ozp_…' https://panel.example.com/api/v1/ozpanel/health
| Field | Values | Default |
name | label | required |
scopes | <tag>:read|write|admin, <tag>:*, * | required |
allowed_ips | addresses | any |
expires_at | RFC 3339 | none |
allow_dangerous | bool; high and critical routes | false |
- RulesGET needs
:read, others :write. Narrow only. Checks: auth → admin restriction → role → scope → danger → CSRF.
- Storage
ozp_ + 64 hex, shown once, SHA-256 stored.
- Call log
GET /auth/tokens/{id}/calls; revoke DELETE /auth/tokens/{id}.
- WhoOperators, tenants and mailbox owners mint their own; tenant tokens stay in the tenant.
Access tokens (mail and SSO)
Separate type: ES256 JWT with subject, level (mailbox · tenant · admin · service) and scopes (mail:imap, mail:smtp). Cluster-wide revocation, checked every use. RFC 7662 introspection for Dovecot. Routes: /tokens/me, /tokens/tenant, /tokens. CLI: token-mint, token-revoke, token-check.
Other credentials
Recovery token (recovery routes only) · node certificates on 9601 (node routes only).
Conventions
{"ok": true, "data": {…}, "error": null}
{"ok": false, "data": null, "error": "specific message"}
| Code | Means |
| 200 / 201 / 207 | OK · created · partial fan-out |
| 400 / 401 / 403 / 404 | Bad field · no auth · not allowed · not found or not yours |
| 409 / 429 | Quota, exists, precondition · rate limited |
| 500 / 501 / 503 | Error with reason · planned · dependency down |
JSON bodies ≤64 KiB (uploads 512 MiB) · 60 s timeout · Cache-Control: no-store · secrets shown once · dry_run where a route says so · no global pagination (limit, offset, days, lines per route).
Jobs and events
| Kind | Calls |
| Jobs | GET /ozpanel/jobs, /ozpanel/jobs/{id}, /ozpanel/jobs/{id}/events (SSE), POST …/cancel |
| Scheduled tasks | GET /schedule?node=, /schedule/{task}/runs, POST …/ack; severity never · failing · late · hung · ok |
| Realtime | GET /realtime/subscribe?module=&category=&node=&min_priority=&backlog=20 (root-mounted SSE) |
| Log streams | /logs/agent/stream, /logs/service/{unit}/stream, /apps/{name}/logs/stream |
| Notifications | Panel feed, email, Web Push. See Notifications. |
Every service module also has GET/PUT /{module}/settings, /{module}/settings/verify, /{module}/config/live, /stored, /diff (unredacted).
Routes by area
| Area | Prefixes | Chapter |
| Install, cluster | /install, /cluster, /mesh | Install, Cluster |
| Web | /caddy, /certs, /phpfpm, /domains | Web |
| Apps | /apps, /app-port-requests, /recipes, /podman | Apps |
| Mail | /mail, /postfix, /dovecot, /rspamd, /mailgw | Mail |
| Webmail, push | /mail/me, /webmail, /oauth, /notify, /me | Webmail |
| DNS | /dns, /namecheap | DNS |
| Databases | /mariadb, /galera, /postgresql, /repmgrd, /pgbouncer, /redis | Databases |
| Tenants, storage, backups | /tenants, /plans, /provision, /files, /terminal, /backups, /juicefs, /garage, /storage | Tenants |
| Security | /nftables, /network-policy, /crowdsec, /ssh, /secrets, /auth | Security |
| Platform | /ozpanel (health, config-check, jobs, audit, modules), /schedule, /stats, /logs | here |
Route tables
Moved here from the chapters so the user path carries no routes. Paths are relative to /api/v1.
Web, PHP, domains
| Path | Role |
GET/POST /caddy/vhosts, GET/PUT/DELETE /caddy/vhosts/{domain}, …/cert, …/logs, …/dns, …/verify, …/mtls* | domain_admin |
/caddy/status, health, config, raw, settings, plugins, upstreams, tls, tls/audit, tls/recheck, metrics, live, verify, reload, reconfigure, storage*, welcome* | super_admin |
/certs, /certs/status, /certs/sync, /certs/import | super_admin |
POST /phpfpm/pools, DELETE /phpfpm/pools/{domain}, /phpfpm/pools/{domain}/php | domain_admin |
/phpfpm/status, health, versions, pools, policy, settings, raw, reload | super_admin |
/domains, /domains/{domain}, …/overview, …/provision | domain_admin |
Apps and containers
| Path | Role |
GET/POST /apps · GET/PUT/DELETE /apps/{name} · …/start /stop /restart /enable /disable · …/placement · …/run-on · …/image · …/logs · …/inspect · …/relink · …/events · …/ports · /app-events · /recipes · POST /terminal/app-sessions | domain_admin |
/app-port-requests, …/approve, …/deny · /podman/status, /podman/containers, /podman/prune | super_admin |
Mailbox console (self-service)
| Call | Does |
POST /mail/me/password | Mail and app sign-in together |
POST /mail/me/app-passwords {"name"} | Shown once; up to 5 |
GET/POST /tokens/me, …/{jti}/revoke, …/{jti}/apple-mail | Access tokens; Apple Mail profile |
GET/PUT /mail/me/filters, PUT /mail/me/vacation | Rules, away message |
PUT /mail/me/webmail-default {"client":"roundcube|sogo"} | Default roundcube |
GET /mail/me/diagnostics, /stats, /activity | Self-check and figures |
Files
| File call | Notes |
GET /files/list, /files/download | ?path= |
POST /files/upload | ≤512 MiB; disk limit checked |
POST /files/mkdir, /move, /chmod, /archive | tar.gz create/extract |
DELETE /files {path,recursive} | |
Console or CLI
| Task | Console | CLI / script |
| Install first node | — | scripts/install-node.sh or ozpanel install |
| Follow / resume install | /admin/install → Resume | ozpanel bootstrap |
| First admin | — | --admin-email or ozpanel admin create |
| Forced first-login password | — | --first-login or ozpanel first-login |
| Join a node | Cluster → Operations (generic form) | POST /cluster/join on the master |
| Evict / readmit | Evict button; readmit via generic form | POST/DELETE /cluster/nodes/{name}/evict |
| Floating IP | Cluster → Floating IP | --floating-ip |
| Module settings | Each module's Settings tab | API |
| Webmail | Support services: install, reconfigure, verify, DB migrate, password rotate | API |
| Storage | Garage/JuiceFS enable, fsck, metadata engine, data dirs | API |
| Deploy / update | Cluster → Updates | scripts/deploy-node.sh to the master |
| Recovery | /recovery (recovery token), grants | ozpanel recover, admin recovery-token, admin recovery-grant |
| Tokens, admin IPs | — | ozpanel admin token, allow-ip, console-restriction |
The ozpanel CLI
ozpanel <command> [flags]; --config defaults to /etc/ozpanel.json.
| Command | Key flags | Does |
version | | Release and bundled artifacts with sha256 |
serve | | The panel (systemd) |
migrate | | Apply schema migrations |
install | See Install; --floating-ip applied at the handover | Make this machine a node |
init-config | --floating-ip: "this node's floating IP — its public address when it sits behind NAT" | Write node config |
bootstrap | --pause-at-handover, --fresh | Run or resume steps |
first-login | --host, --port, --temp-password, --new-password | Provider password change |
recover | --apply, --force, --settle 75s, --config | Stranded node |
admin create | passwd | token | recovery-token | recovery-grant | allow-ip | console-restriction | See Install | Logins, tokens, access |
identity csr | sign | install | show | --allow-unregistered on sign | Offline enrolment |
token-mint | token-revoke | token-check | --subject --level --scopes --label --expires · --jti · --token | Access tokens |
usage-collect, backup-run --schedule, mail-backup-run --job, mail-digest | | Cron jobs |
pull-release, boot-gate, pgbouncer-follow-primary, galera-ready-gate, galera-position, galera-recover, juicefs-mount-gate, caddy-storage-gate, app-gate, app-ready, ssh-wait-listen, tinc-wait-bind, mailgw, mesh-join, service-ids, sync-mesh-peers | | Service hooks, run by systemd or daemons |
ozpanel admin token --email admin@example.com --name deploy --ttl 1h --dangerous
ozpanel token-mint --subject user@example.com --level mailbox --scopes "mail:imap mail:smtp" --label "Mail app"