V8 Central — Product & Technical Spec
  1. Docs
  2. Technical
  3. API architecture

#API architecture

Phase 8 latest (2026-10-14): Step 0 pushed (2899bd1); 8.5 Connect an existing OSS panel + server list/view built and pushed (Pair 2 API62–64, up to 5be6dec; response field system_info (was facts); tested live on an OSS test server; API address (https://api.…) + token only — panel web address refused with oss_not_a_panel). Next: 8.4 → 8.1 → 8.3. Connect a panel.

#Shared frontend API client (lib/api/)

ConcernRule
Base URLhttps://sa-central-api-2.23-172-120-38.nip.io + /api/v1 (from env, never another pair's API). Known since 2026-10-08, when Phase 1 was built: routes live in routes/v1/<domain>.php under /api/v1 V8 requirement
AuthenticationAuthorization: Bearer <access token> added server-side (BFF route handlers / server components) from the httpOnly cookie. The browser never sees the token (D-1)
Cookies / sessionhttpOnly, secure, sameSite=Lax. Refresh token in its own httpOnly cookie
Organization contextOrganization id in the path of organization-scoped calls (/organizations/{org}/…, V7 style) Assumption. Never a global header the server trusts blindly
LocaleAccept-Language from the chosen locale (cookie)
ErrorsNormalised to {status, code, message, fields, reference}. 401 → refresh once → login. 403 → permission state. 404 → not-found. 422 → form. 429 → back off with Retry-After. The real shapes are now published (2026-10-09) — see the table below
RetryGET only: 1 retry on network error/502/503/504 with backoff. Never auto-retry POST/PUT/PATCH/DELETE
CancellationAbortController per request. Cancel on unmount, org switch, and a newer search
Request statesidle → loading → success / error, exposed by a small hook or TanStack Query Open question (TanStack Query isn't installed; the project rules mention "TanStack queries")
Timeouts15 s default. Proxied server calls up to 60 s; long jobs use 202 + polling
VersioningThe API is versioned — /api/v1 V8 requirement. So keep the version in one place (the env base URL or a single constant), never repeated per call, and treat a future /api/v2 as a config change rather than a refactor. Worth knowing now: nothing in the doc says how long v1 is supported, so do not build anything that depends on the version string being permanent

#The backend's own API reference — read it before building a screen V8 requirement

Since 2026-10-09 the backend publishes a live API reference, separate from the project doc:

Pagehttps://sa-central-api-2.23-172-120-38.nip.io/v8-central-docs-ea60c2f1c31c3c9d/api-reference.html (re-renders itself every 30 s)
CoversOnly the endpoints Pair 2 has actually built, and it grows as they build. Today: the profile half (3.1–3.7), the whole security half (3.8–3.13, 3.16) and in-app notifications (3.14)
Does not coverAuthentication — that is Pair 1's code, and this reference says so. So it is not one place for the whole API yet
Remaining Account workB3.15 channels now built, reference §§33–38; B3.17 server alerts wait for Servers

This changes how a screen should be planned. Until now every field list on this site came from the project doc's one-line descriptions, so our rules were careful guesses. The reference gives exact field names, validation limits, response shapes, messages and status codes — so before building any screen, read the matching section there and use its names verbatim. It moves while the backend builds, so read it again rather than trusting a copy, including the copies on this site.

#Published response shapes V8 requirement

CaseBody
Success with data{ "data": { … } } — and { "data": …, "message": "…" } after a change, with the message already written and translated
Success without data{ "message": "…" }
Failed sign-in state{ "message": "…", "code": "unauthenticated" } (401), account_banned / email_not_verified (403)
Validation{ "message": "…", "errors": { "field": ["…"] } } (422) — field names match the request
Refused delete{ "message": "…", "code": "delete_protection_enabled" } (423)
Blocked for the personal API token{ "message": "…", "code": "api_token_not_allowed" } (403) — the token may only view the profile
Needs a verified email{ "message": "…", "code": "email_not_verified" } (403) — only on the four guarded actions (invitations, ownership transfer, creating a server, promo credits), not on ordinary calls
Too many attempts{ "message": "Too many attempts. Please try again in N seconds." } + Retry-After (429)
Lists{ "data": [ … ], "links": {first,last,prev,next}, "meta": {current_page,from,last_page,per_page,to,total} } — Laravel's standard paginator

Two rules follow from this, and they save work:

  • Show the backend's message instead of writing our own. The messages are already final and already translated through Accept-Language, so a second English string in our dictionary would only drift. Our own copy is for things the API cannot know — labels, empty states, confirmations.
  • Sensitive actions are rate-limited much harder than the rest — changing a password or deleting the account is 5 a minute, requesting an email change is 3 per 10 minutes, ordinary reads are 60 a minute. The limits themselves are the backend's business, but the 429 message carries the number of seconds, so those forms need a real waiting state (disabled button + the seconds), not a generic "something went wrong".

#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 generic data wrapper.
  • 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.
  • Auth error codes are published and stable V8 requirement — Phase 2 was built on 2026-10-09 with email_not_verified, account_banned, ip_not_approved, two_factor_expired, session_expired, unauthenticated. Map these to frontend keys by code, never by message text. (This site had guessed unverified and banned; both were wrong.)
  • Never retry or parallelise a token refresh V8 requirement — the built refresh token is rotated on every use, and reusing a spent one revokes the whole login chain (2.9). So a retried refresh, or two requests refreshing at once, will log the user out of everything. The BFF must serialise refresh through a single in-flight promise and never auto-retry it; a failed refresh means go to login, not try again.
  • Treat every id as a string V8 requirement — Phase 1 step 1.1 (2026-10-08) stores UUIDs as char(36), so an id can be a 36-character string rather than a number. Never parseInt an id, never sort by it numerically, and keep route params as strings. This costs nothing where ids are integers and saves a class of bug where they are not.
  • Text comparison on the backend is case-insensitive — 1.1 sets the collation to utf8mb4_unicode_ci. So a uniqueness check treats MyApp and myapp as the same name Assumption. Duplicate-name errors should say the name is taken, not hint that different capitalisation would work. And because the charset is utf8mb4, names and labels can legitimately contain emoji and other 4-byte characters — so truncate by character, never by byte.
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

Built V8 rows use published contracts under /api/v1; other rows remain V7 references, not V8 contracts. Pair 1's API reference now covers Organization, Members, Roles, permissions/menu and activity (§38). 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.

ModuleCapabilityV7 reference (method path)AuthOrg-scopedV8 status
AuthRegisterUnder /api/v1/auth/* — the exact path is not published (the API reference covers Pair 2's endpoints only, and auth is Pair 1's). What is published: it returns tokens, the user and the organizations, exactly like login (2026-10-09)publicnoV8 requirement 2.1 built
AuthVerify email / resendGET /verify/{token}, POST /resend/verification-linkpublicnoMissing
AuthLoginPOST /loginpublicnoMissing V8 requirement 2.3
Auth2FA verify / resendPOST /two-factor-authentication/verify, /resendpublic (pending login)noMissing
AuthSocial loginPOST /users/{provider}/url, GET /users/{provider}/callbackpublicnoMissing
AuthForgot / resetPOST /forgot-password, POST /reset-passwordpublicnoMissing
AuthIP approvalGET /user/whitelist-ip/{key}/authorizepublic (signed)noMissing
AuthRefresh tokenCentral's own (Sanctum has none)refresh tokennoMissing V8 requirement 2.9
AuthLogoutGET /user/logoutusernoMissing
AuthCurrent userGET /meusernoMissing
AccountView / update profileGET /account/profile, PATCH /account/profileusernoConfirmed V8 requirement 3.1, 3.2 built
AccountChange passwordPUT /account/passwordusernoConfirmed V8 requirement 3.4 built
AccountChange emailPOST /account/email, then POST /account/email/confirm (no token — public)user / publicnoConfirmed V8 requirement 3.3 built
AccountEmail preferencesPATCH /account/email-preferencesusernoConfirmed V8 requirement 3.5 built
AccountDelete protectionPUT /account/delete-protectionusernoConfirmed V8 requirement 3.6 built
AccountDelete accountDELETE /account (JSON body: password + optional reason)usernoConfirmed V8 requirement 3.7 built
AccountLogin historyGET /account/login-history?page=&per_page=usernoConfirmed V8 requirement 3.11 built
AccountActive sessionsGET /account/sessionsusernoConfirmed V8 requirement 3.16 built
AccountLog out one deviceDELETE /account/sessions/{id} — allowed for the current deviceusernoConfirmed V8 requirement 3.16 built
AccountLog out all other devicesPOST /account/sessions/logout-others — returns how manyusernoConfirmed V8 requirement 3.16 built
Account2FA statusGET /account/two-factor — enabled, method (null / email / authenticator), backup-code countsusernoConfirmed V8 requirement 3.8 built
AccountTurn email 2FA on / offPOST /account/two-factor/email (no password) · DELETE /account/two-factor (password)usernoConfirmed V8 requirement 3.8 built
AccountNew backup codesPOST /account/two-factor/backup-codes (password) — six codes, shown onceusernoConfirmed V8 requirement 3.8 built
AccountAuthenticator setup / confirm / offPOST /account/two-factor/authenticator → …/authenticator/confirm (6-digit code) · DELETE …/authenticator (password)usernoConfirmed V8 requirement 3.9 built
AccountIP whitelist listGET /account/ip-whitelist?page=&per_page= — rows plus enabled and current_ip; each row approved or pendingusernoConfirmed V8 requirement 3.10 built
AccountIP whitelist on / offPUT /account/ip-whitelist (no password) — off removes every IPusernoConfirmed V8 requirement 3.10 built
AccountAdd / remove an IPPOST /account/ip-whitelist/ips (max 100, only while on) · DELETE /account/ip-whitelist/ips/{id} — tells you if it was the current IPusernoConfirmed V8 requirement 3.10 built
AccountAPI access (excluded from the frontend)GET /account/api-access · POST /account/api-access (on) · POST …/regenerate · DELETE /account/api-access (off)usernoConfirmed V8 requirement 3.13 built
AccountActivity logGET /account/activities?page=&per_page=&type=&action= — rows carry a ready translated sentence and the filter options come back with the response; login token only (the API token gets 403)usernoConfirmed V8 requirement 3.12 built
NotificationsIn-app list**`GET /account/notifications?filter=unreadall&page=&per_page=** — each row brings its own **title, message, icon, link and actor**; unread_count` comes with ituserno
NotificationsUnread countGET /account/notifications/unread-count — cheap, and the backend says to poll it for the bellusernoConfirmed V8 requirement 3.14 built
NotificationsMark one / all readPOST /account/notifications/{id}/read · POST /account/notifications/read-all — both return the new unread countusernoConfirmed V8 requirement 3.14 built
OrganizationsList/create/show/update/delete/defaultGET/POST /organizations, GET/PATCH/DELETE /organizations/{organization}, POST …/defaultlogin; manage for update, owner for deleteyesV8 requirement B4.1/B4.2 built; list search(max100)/type all,mine,invited; membership_status joined/pending, no pagination; pending no access/token; profile/login joined-only; server guard later
OrganizationsOwnership transferGET/POST/DELETE /organizations/{organization}/ownership-transfer, POST …/confirmations, POST …/acceptowner starts/cancels; selected recipient accepts; writes verifiedyesV8 requirement B4.7 built including deletion safety; preview email delivery unverified; unpaid-charge/recipient-plan guards not enforced yet
OrganizationsVisible menuGET /organizations/{organization}/menu — sorted visible items; substitute URL placeholders from contextany memberyesV8 requirement B4.11 built; separate deny-reason map not promised
MembersList/update/remove/leaveGET /organizations/{organization}/members?search=&page=&per_page=, PATCH/DELETE …/members/{member}, POST …/leavemember-view/manage; any member may leave, not owneryesV8 requirement B4.4 built; 25/page, max 100; owner first then name
MembersAccept invitationPOST /user/password-set/{token} or register with invitation_tokenpublic tokennoMissing
RolesCRUDGET/POST /organizations/{organization}/roles, GET/PATCH/DELETE …/roles/{role}role-view/manageyesV8 requirement B4.5 built; fixed roles immutable; in-use deletion refused
RolesPermission catalogGET /organizations/{organization}/permissions — parent, requires, owner-only, orderrole-viewyesV8 requirement B4.9–B4.11 built
PlansCatalognone in V7 (hard-coded)user?Missing
SubscriptionShow + usageGET /organizations/{org}/new-subscription-planowneryesMissing
SubscriptionCreate / change / renew / cancel / resume / quotePOST /create-subscription, PATCH /change-subscription, /renew-subscription, /cancel-subscription, /resume-subscription, GET /remaining-creditownerV7: userMissing
BillingEnabled gatewaysGET /account/payment-gateways, no input; provider/name/mode onlylogin; unverified allowed; API tokens403noV8 requirement B6.1 Pair2 built/pushed; no keys or checkout; empty valid;60/min
BillingWallet balanceGET /account/wallet, no inputown active login; unverified allowednoV8 requirement B6.3 built/pushed;USD exact decimal strings20 places+as_of;no-store; referenced500≠zero; no public mutations
BillingAdd-credit quote/startPOST /account/payment-quotes, POST /account/paymentsown active login; unverified allowednoV8 requirement B6.4 local/not activated; exact quote/operation key,503 reference
BillingTransactions, receiptGET /payment, GET /payment-receipt/{key}/receiptuserV7: userMissing
BillingPayment status/confirmGET /account/payments/{UUID}, POST …/confirm empty body+operation keyown login, other owner404noV8 requirement B6.4 local/not activated; no browser-success inference
BillingCards, auto-recharge/payment-detail/cards*, /stripe-auto-chargeusernoMissing
BillingInvoicesGET /organizations/{org}/invoices, POST …/{number}/{action}owneryesMissing
BillingBilling detailsGET/PUT /account/billing-details, full form, first save200active own-account login; unverified allowed; no org-role gatenoV8 requirement B6.2 Pair2 built/pushed; null before first save; referenced500 must not become empty/overwrite;60/min
ServersList / connect / show / disconnect(V7 servers were agent-based, not reusable)member + permyesMissing
ServersProxy to OSS—member + permyesMissing (D-10)
DashboardAggregates—memberyesMissing
ProvidersList accountsGET /organizations/{organization}/cloud-provider-accounts?page=&per_page=&search=&provider=&status=organization.integrations.cloud-providers (was organization.integrations.cloud-providers, renamed 2026-10-17) viewyesV8 requirement B5.1 built Pair 1, 10/page max 50; credentials/usage counts absent. Connect/Edit built Pair1; Disconnect endpoint partial, usage guard/warning pending
ProvidersConnect accountPOST /organizations/{organization}/cloud-provider-accounts: name/provider/nested credentialsintegration-manageyesV8 requirement B5.2 built Pair1; 422 rejected vs503 temporary failure; no persistence on failure; real-provider success unverified
ProvidersEdit accountPATCH /organizations/{organization}/cloud-provider-accounts/{cloudProviderAccount}: name and/or complete credentialsintegration-manage; scoped IDsyesV8 requirement B5.3 built Pair1; rename no status reset;422/503 no changes;409 reload/review; never auto-retry
ProvidersCheck saved credentialsPOST /organizations/{organization}/cloud-provider-accounts/{cloudProviderAccount}/check, no inputintegration-manage; scoped IDsyesV8 requirement B5.4 built Pair1, on-demand only;200 Connected/Needs new token;503 unchanged/unreadable→Edit;409 reload;shared10/min
ProvidersDisconnect — partialDELETE /organizations/{organization}/cloud-provider-accounts/{cloudProviderAccount}, no inputintegration-manage; scopedyesV8 requirement B5.5 endpoint built Pair1;200 deleted metadata snapshot/repeat404; no upstream revoke/server delete;usage guard/warning NOT built
ProvidersRegions / sizes…/{id}/regions, …/{id}/sizesmember + permyesMissing
ProvidersCreate VPSPOST /organizations/{org}/servers (V7 store)member + permyesMissing
BlueprintsCRUD, upload, WP.org search/wordpress-blueprints*userV7: userMissing
AuditOrganization listGET /organizations/{organization}/activity?page=&per_page=&type=&action= — translated descriptions, actor snapshots, filters, paginatororganization.activity viewyesV8 requirement B4.8 built, Pair 1; 10/page, max 50
SearchGlobal(V7 route disabled)memberyesMissing
NotificationsChannels + send testGET/POST /account/notification-channels, GET/PATCH/DELETE …/{id}, POST …/{id}/testlogin only; unverified allowednoV8 requirement B3.15 built; test202 queued, poll detail; recorded≠delivered; no saved-count cap
NotificationsFuture routing/attachmentsNo published B3.15 attachment/event-group APIuser/server permission laterserver laterMissing Phase11/Servers, not channel CRUD

#OSS endpoints Central calls (exist today) OSS API

PurposeMethod + pathAuth
Health / versionGET /api/healthnone
Validate key / identityGET /api/auth/meBearer central key
Facts / metrics / processesGET /api/server/facts, /server/metrics/live, /server/metrics/history, /server/processesBearer
CapabilitiesGET /api/server/capabilitiesBearer
Applications, databases, system users, services, firewall, cron, backups, PHP, Node, Fail2ban, settings, logs, disk cleaner, syncsee Server detailsBearer
Paid add-onsGET /api/central/addons, GET /api/central/addons/runs/{run}, /api/central/addons/applications/{id}/wordpress/*, /log-monitoring/*Bearer (Central only)
Install with keyCENTRAL_TOKEN=… bash install.sh --stack=…installer env
Not available to CentralPOST/DELETE /central (enable/revoke) needs an OSS admin session; user/role mutations → 403—

Provider code availability (2026-10-10): Pair2 reports B5.1–B5.5 pulled through cf36220;1211 tests/7473 assertions, Pint/PHPStan and optimization passed after pull (not rerun by this sync). List/Connect/Edit/Check and partial Disconnect are present in its copy. No genuine provider success is established. Disconnect server-usage blocker/server-name warning remain unbuilt until Phase8; Git/storage still wait Servers.

| Billing | Free-credit redemption | POST /account/promo-redemptions code+operation key | own login, verified required | no | V8 requirement B6.4 local; no tax/gateway/+3 |

Provider webhooks API48 are setup-only, not frontend calls; acknowledgement is intake, not paid credit. Return route /verify-transaction/{payment UUID} and Billing destination must be coordinated before activation.

#Built administrator entry (Pair1 B17.1, API49–50)

CapabilityEndpointAccessReadiness
Own admin discovery (removed 2026-10-16)GET /api/v1/account/admin-accessactive login, unverified allowed; no personal API token; own role/flag onlyV8 requirement Pair1 built, flag for navigation not authorization
Protected admin identity (removed 2026-10-16)GET /api/v1/admin/meactive verified exact administrator login; current state checkedV8 requirement own identity only; permissions/menu/business APIs still missing

No-store responses.401 unauthenticated;403 admin_access_denied/email_not_verified/api_token_not_allowed/account_banned as appropriate;429 shared60/min/account. Normal login/profile format unchanged. No HTTP promotion endpoint or global organization bypass. Pair2 code availability unconfirmed. Entry screen.

2026-10-12 latest readiness: Pair1 B17.2/17.2a adminroles/catalog/assignment APIs51–58 BUILT, menu/sidebar17.3 not built. Pair2 B6.5 savedcards/default/remove,autorecharge/consent/same-payment recovery APIs49–62 IN PROGRESS—notcomplete/committed/pushed/activated; safety fixes and final verification ongoing. Admin contracts/screens; Billing drafts/states. Do not confuse Pair1API49–58(admin) with Pair2API49–62(cards).

Latest B6.5 status (2026-10-12 10:35): 6.4 + 6.5 pushed to GitHub; later local follow-ups: card APIs answer directly (0ff3da9) and auto-recharge is not available for India (billing country or India-issued card, as V7) — the India mandate API (old 62) was removed (e65e8c3). Real payments still not activated; no real keys/charges or host scheduler change. Frontend/consent/provider proof still required.

Removed 2026-10-16 (Pair 1): GET /account/admin-access and GET /admin/me no longer exist. Admin entry = role / is_administrator from the login/profile response + the filtered sidebar GET /admin/menu (API59). The admin's role and permissions now come with /admin/menu (admin_role, permissions — added 2026-10-16 05:22).

Latest B17.3 (Pair1): GET /api/v1/admin/menu built, active verified administrator; role-filtered translated group/title/Lucide/localURL/order/parent, no role→empty. 59 permissions, 34 sidebar screens at that time — now 53 permissions / 33 sidebar screens after B17.11 (2026-10-16); tabs/actions no URLs. Queue-size page/permission removed, Horizon instead. B17.4+ business/viewer unbuilt; menu≠working destination, Pair2 availability unconfirmed. Menu states.

B17.4 latest: Pair1 admin Activity log API60 GET /api/v1/admin/activity BUILT; active verified administrator+admin-activity view, private/no-store. Table/filters/pagination/snapshot targets read-only, separate admin feed; account/org feed unaffected. B17.5+ still unbuilt,Pair2unconfirmed. Activity screen.

B17.6 latest (2026-10-12): Pair1 admin Users API61–68 BUILT — list/counts/view/create/ban/unban/verify/delete, deleted users, referred users; statuses active/pending/banned (no locked); admins can't ban/delete themselves or other administrators. Users are now soft deleted (also self-delete B3.7) — the email can still sign up again, nothing changes on screen. B17.5 and B17.7+ still unbuilt, Pair2 unconfirmed. Users screens.

B17.7 latest (2026-10-12): Pair1 admin User details API69–76 BUILT — organizations, login history, account activity, 2FA status/turn off (backup codes counted only), IP whitelist view/turn off/remove IP; new users.activity view permission (60 admin permissions); servers tab waits for Phase 8. Admin changes show in the user's activity as by a ServerAvatar admin. B17.5 and B17.8+ still unbuilt. User details.

B17.10 latest (2026-10-14): Pair1 Login as user API77 BUILT — users.login-as manage only; normal user login without password/2FA/IP whitelist; refused for own account/administrators/banned users; recorded only in admin activity. Login as user.

B17.15 latest (2026-10-14): Pair1 admin Promo codes API78–81 BUILT — list/create/on-off/delete (soft, name stays taken, history kept); no edit after creation; cohort renamed customer_type (all/new/existing). Promo codes.

B17.17 latest (2026-10-16): Pair1 admin Site settings API93–97 BUILT — basic details (branding, uploads, custom header/footer, links) + company billing details (tax % = checkout tax); public GET /site-settings before login. D-64 open (custom code). Site settings.

B17.12 Part 1 (2026-10-17): Pair1 admin Servers API114–119 BUILT — list/view/edit (name, hostname, IP) + user/organization Servers and Deleted servers tabs; live parts later. Servers are now soft deleted (hidden from users). Deleted list = D-63 conflict. Admin servers.

B17.19 (2026-10-17): Pair1 admin General settings API103–113 BUILT — display message (banner for every user via API97 display_message), trial mode on/off (not used — V8 has no trial, D-56), referral program settings, Slack webhook for admin alerts. User settings / temporary domain / support email removed (57 permissions, 31 screens). General settings.

B17.23 (2026-10-16): Pair1 admin Error monitor API99–101 BUILT — list/view/resolve unhandled request/job/command errors (secrets removed, kept 2 days, reopen on repeat). Error monitor.

B17.6 edit (2026-10-16): Pair1 admin Edit a user API98 BUILT — profile, email (no confirmation), password, role, admin role, status for any account incl. administrators; lockout guard. Edit a user.

B17.20 Part 1 latest (2026-10-14): Pair1 admin Integration keys API82–88 BUILT — social login (GitHub/Google), Turnstile, payment gateways (Stripe/PayPal/Instamojo, test/live keys, webhook URLs); secrets write-only. SMTP + test mail (Part 2) next. Integration keys.

B17.20 Part 2 (2026-10-15): Pair1 admin SMTP + test email API89–92 BUILT (saved vs server-default settings, write-only password, test email with short failure reason, reset to defaults). Integration keys complete except cloud platforms (to confirm). SMTP.

ServerAvatar V8 Central · prepared by central-app-2 (Pair 2 frontend) for Bhavik Jethwa · the frontend in this spec is not built yet; the backend started on 2026-10-08 · Built 2026-10-29 03:20 UTC