#API architecture
Frontend
Next.js pages / server components / route handlers
Shared API client
lib/api (axios instance + interceptors) — one place for auth, org, locale, errors, retry
Central API
sa-central-api-2 (Laravel, Passport) — auth, org membership, permissions, plan, audit
Downstream
Central DB (MariaDB)OSS panels (per-server key, shared OSS client)Payment gatewaysCloud providersMail
#Shared frontend API client (lib/api/)
| Concern | Rule |
|---|---|
| Base URL | https://sa-central-api-2.167-233-229-184.nip.io (from env, never another pair's API) |
| Authentication | Authorization: Bearer <access token> added server-side (BFF route handlers / server components) from the httpOnly cookie. The browser never sees the token (D-1) |
| Cookies / session | httpOnly, secure, sameSite=Lax. Refresh token in its own httpOnly cookie |
| Organization context | Organization id in the path of organization-scoped calls (/organizations/{org}/…, V7 style) Assumption. Never a global header the server trusts blindly |
| Locale | Accept-Language from the chosen locale (cookie) |
| Errors | Normalised to {status, code, message, fields, reference}. 401 → refresh once → login. 403 → permission state. 404 → not-found. 422 → form. 429 → back off with Retry-After |
| Retry | GET only: 1 retry on network error/502/503/504 with backoff. Never auto-retry POST/PUT/PATCH/DELETE |
| Cancellation | AbortController per request. Cancel on unmount, org switch, and a newer search |
| Request states | idle → loading → success / error, exposed by a small hook or TanStack Query Open question (TanStack Query isn't installed; the project rules mention "TanStack queries") |
| Timeouts | 15 s default. Proxied server calls up to 60 s; long jobs use 202 + polling |
#Response conventions (proposed for sa-central-api-2)
Follow the OSS conventions so both APIs feel the same Assumption:
- Named top-level keys (
{"servers": [...]}), no genericdatawrapper. - Errors:
{"message": "...", "code": "...", "errors": {...}, "reference": "..."}. - Pagination
meta: {current_page, per_page, total, last_page},?page=&per_page=. - Timestamps: ISO-8601 UTC (frontend formats). OSS uses
DD-MM-YYYY HH:mm:ss, which the proxy passes through or converts Open question.
Proposed error shape (same as OSS)
// 422
{"message": "The given data was invalid.", "code": "validation_failed",
"errors": {"email": ["The email has already been taken."]}}
// 403 (plan)
{"message": "Your plan allows 5 servers.", "code": "plan_limit_reached",
"limit": 5, "used": 5}
// 504 (proxied server)
{"message": "This server is not responding.", "code": "server_unreachable",
"server_id": 42, "reference": "3f9a…"}#API dependency table
Every Central endpoint is missing today (sa-central-api-2 has none). The "V7 reference" column shows the existing V7 equivalent. It's not a V8 contract, and V8 paths are decided per phase. OSS rows exist now.
| Module | Capability | V7 reference (method path) | Auth | Org-scoped | V8 status |
|---|---|---|---|---|---|
| Auth | Register | POST /users | public | no | Missing V8 requirement 2.1 |
| Auth | Verify email / resend | GET /verify/{token}, POST /resend/verification-link | public | no | Missing |
| Auth | Login | POST /login | public | no | Missing V8 requirement 2.3 |
| Auth | 2FA verify / resend | POST /two-factor-authentication/verify, /resend | public (pending login) | no | Missing |
| Auth | Social login | POST /users/{provider}/url, GET /users/{provider}/callback | public | no | Missing |
| Auth | Forgot / reset | POST /forgot-password, POST /reset-password | public | no | Missing |
| Auth | IP approval | GET /user/whitelist-ip/{key}/authorize | public (signed) | no | Missing |
| Auth | Refresh token | Passport | refresh token | no | Missing V8 requirement 2.9 |
| Auth | Logout | GET /user/logout | user | no | Missing |
| Auth | Current user | GET /me | user | no | Missing |
| Account | Profile, email, password, 2FA, Google 2FA, IP whitelist, login history, API access, sessions | /user/*, /user/2fa/*, /user/google-2fa/*, /user/whitelist-ip, /login-history | user | no | Missing V8 requirement Phase 3 |
| Organizations | List / create / show / update / delete | GET/POST /organizations, GET/PATCH/DELETE /organizations/{id}, GET /auth/user/organizations | user | — | Missing |
| Organizations | My permissions | GET /organizations/{org}/my-permissions/{level} | member | yes | Missing |
| Members | List / invite / role / remove | GET/POST /organizations/{org}/members, PATCH …/{member}/assign-role, DELETE …/{member} | member + perm | yes | Missing |
| Members | Accept invitation | POST /user/password-set/{token} or register with invitation_token | public token | no | Missing |
| Roles | CRUD | Route::resource /organizations/{org}/roles | member + perm | yes | Missing |
| Roles | Permission catalog | GET /organizations/{org}/permissions | member | yes | Missing |
| Plans | Catalog | none in V7 (hard-coded) | user | ? | Missing |
| Subscription | Show + usage | GET /organizations/{org}/new-subscription-plan | owner | yes | Missing |
| Subscription | Create / change / renew / cancel / resume / quote | POST /create-subscription, PATCH /change-subscription, /renew-subscription, /cancel-subscription, /resume-subscription, GET /remaining-credit | owner | V7: user | Missing |
| Billing | Wallet top-up | POST /user/wallet | user | V7: user | Missing |
| Billing | Transactions, receipt | GET /payment, GET /payment-receipt/{key}/receipt | user | V7: user | Missing |
| Billing | Payment execute / verify | GET /payment/{key}/execute, GET /payment/{key}/verify | public key | no | Missing |
| Billing | Cards, auto-recharge | /payment-detail/cards*, /stripe-auto-charge | user | no | Missing |
| Billing | Invoices | GET /organizations/{org}/invoices, POST …/{number}/{action} | owner | yes | Missing |
| Billing | Billing details | GET/POST /organizations/{org}/billing-details, /billing-detail | owner | yes | Missing |
| Servers | List / connect / show / disconnect | (V7 servers were agent-based, not reusable) | member + perm | yes | Missing |
| Servers | Proxy to OSS | — | member + perm | yes | Missing (D-10) |
| Dashboard | Aggregates | — | member | yes | Missing |
| Providers | List / connect / OAuth callback / delete | /organizations/{org}/cloud-server-providers*, GET /integrations/cloud-service-providers | member + perm | yes | Missing |
| Providers | Regions / sizes | …/{id}/regions, …/{id}/sizes | member + perm | yes | Missing |
| Providers | Create VPS | POST /organizations/{org}/servers (V7 store) | member + perm | yes | Missing |
| Blueprints | CRUD, upload, WP.org search | /wordpress-blueprints* | user | V7: user | Missing |
| Audit | List | GET /organizations/{org}/activities, GET /activities | member + perm | yes | Missing |
| Search | Global | (V7 route disabled) | member | yes | Missing |
| Notifications | List / unread / mark read | GET /notifications/unread, /mark-as-read | user | no | Missing |
| Notifications | Channels | /user/notification-channels* | user | no | Missing |
#OSS endpoints Central calls (exist today) OSS API
| Purpose | Method + path | Auth |
|---|---|---|
| Health / version | GET /api/health | none |
| Validate key / identity | GET /api/auth/me | Bearer central key |
| Facts / metrics / processes | GET /api/server/facts, /server/metrics/live, /server/metrics/history, /server/processes | Bearer |
| Capabilities | GET /api/server/capabilities | Bearer |
| Applications, databases, system users, services, firewall, cron, backups, PHP, Node, Fail2ban, settings, logs, disk cleaner, sync | see Server details | Bearer |
| Paid add-ons | GET /api/central/addons, GET /api/central/addons/runs/{run}, /api/central/addons/applications/{id}/wordpress/*, /log-monitoring/* | Bearer (Central only) |
| Install with key | CENTRAL_TOKEN=… bash install.sh --stack=… | installer env |
| Not available to Central | POST/DELETE /central (enable/revoke) needs an OSS admin session; user/role mutations → 403 | — |