Panel HTTP API (dual authentication)
This document describes only the HTTP routes that were migrated to dual authentication in the API-first implementation: panel API keys (sk_…) or a normal Clerk session behave the same on these paths. It does not describe the rest of the /api/* surface.
In scope (dual-auth routes)
The following path prefixes accept either a Clerk session (browser cookies) or Authorization: Bearer sk_… using a key from Settings → API keys in CorePanel:
| Prefix | Notes |
|---|---|
/api/services | Services CRUD and related operations. |
/api/teams | Team membership, SSH keys, transfers, monitoring, database catalog/instances, etc. |
/api/teams/.../backup/... | Backup subpaths under teams use backup scopes (backups.read / backups.write) instead of teams.* alone. |
/api/zones | DNS zones and records. |
/api/domains | Domain registration and contacts. |
/api/billing | Billing, invoices, top-up, portal, etc. Except /api/billing/config (see below). |
/api/tickets | Support tickets and messages. |
Keys are created in the UI (Settings → API keys). Key lifecycle is not exposed on the dual-auth surface documented here.
Out of scope (not covered here)
Do not treat the following as part of this dual-auth, API-key integration contract. They may use different auth, internal-only access, or no sk_ support:
/api/admin/*/api/cron/*/api/webhooks/*/api/ingest/*/api/settings/api-keys
Other /api/* routes not listed under In scope are also outside this document unless and until they are added to the dual-auth migration.
Authentication
- Send
Authorization: Bearer <token>where<token>is the fullsk_…secret shown once at creation. - Expired key:
401with JSON{ "error": "API key expired" }. - Invalid / revoked key:
401with a generic error string (do not rely on wording for automation). - IP whitelist: If the key has a non-empty
ipWhitelist, the request client IP must match an entry or the key is rejected (401, generic message or IP-specific per implementation).
Permission scopes
Keys may carry an optional list of scopes (permissions). An empty permission list on a key means full access (legacy behavior).
When scopes are set, required scopes follow src/lib/api-route-permissions.ts (read vs write by HTTP method and path prefix):
| Prefix | GET / HEAD | Mutating methods |
|---|---|---|
/api/services | services.read | services.write |
/api/zones | dns.read | dns.write |
/api/domains | domains.read | domains.write |
/api/billing (except /api/billing/config) | billing.read | billing.write |
/api/tickets | tickets.read | tickets.write |
/api/teams | teams.read / backups.read when the path includes /backup | teams.write / backups.write when the path includes /backup |
Missing scope: 403 with { "error": "Insufficient API key permissions" }.
Rate limits
API key traffic on the dual-auth routes above is rate-limited per key id (see RATE_LIMITS.dashboardApiKey in src/lib/rate-limit.ts).
Public exception (billing)
GET /api/billing/config(exact path) remains unauthenticated for marketing / calculator use and does not require scopes.- All other
/api/billing/*routes require authentication as in In scope.
Per-endpoint route catalog
Every dual-auth route.ts under src/app/api/{services,teams,zones,domains,billing,tickets} is listed in the per-endpoint route catalog on this page with exact URL, HTTP method, description (from route comments), query parameters (searchParams), request body Zod fields where detected, and an example success response extracted from return NextResponse.json(…) in the handler. The same catalog lives in the panel repo as docs/developer-api-routes.md (regenerate with node scripts/generate-dual-auth-route-docs.mjs).
Phase 2
A machine-readable OpenAPI artifact for the dual-auth subset is planned as a follow-up.