Skip to content

Central API v1

Base path: /api/central/v1

Auth: Sanctum bearer token, guard central-api.

Authorization: Spatie permissions + policies. The superadmin role bypasses all gates.

Response envelope on every endpoint:

json
{ "status": "success" | "error", "message": "...", "data": { } | [ ] | null, "meta": { } | null }

Paginated list endpoints populate meta (current_page, last_page, per_page, total, next_page_url, prev_page_url); single-resource endpoints set meta: null.

Auth & profile

MethodPathNotes
POST/auth/loginThrottled (throttle:auth-login, 5/min by email or IP). When TOTP is confirmed for the account, returns { two_factor_required: true, challenge_token } instead of a bearer token.
POST/auth/two-factor/challengeComplete login after password step. Body: challenge_token, code, optional remember. Throttled (auth-login).
GET/auth/passkeys/login/optionsWebAuthn discoverable credential options.
POST/auth/passkeys/loginPasskey login. Body: options_token, credential, optional remember.
POST/auth/forgot-password
POST/auth/reset-passwordBody: email, token, password, password_confirmation
GET/meCurrent user + roles/permissions (+ avatar_url)
POST/meUpdate profile (name, email)
POST/me/avatarUpload profile picture (multipart/form-data field file; jpg/png/webp, max 2 MB)
DELETE/me/avatarRemove profile picture
POST/me/change-passwordBody: old_password, password, password_confirmation, optional revoke_other_sessions (default true). Throttled (auth-sensitive).
GET/me/sessionsList active bearer sessions for the current user.
DELETE/me/sessions/{token}Revoke one session by token id.
POST/me/sessions/revoke-othersRevoke all sessions except the current token.
GET/me/two-factor{ enabled, recovery_codes_remaining }
POST/me/two-factorBegin TOTP setup (password required). Returns { svg, secret }.
POST/me/two-factor/confirmConfirm TOTP with code. Returns { recovery_codes }.
DELETE/me/two-factorDisable TOTP. Body: password, code (authentication or recovery code).
POST/me/two-factor/recovery-codesRegenerate recovery codes (password required).
GET/me/passkeysList registered passkeys.
GET/me/passkeys/optionsWebAuthn registration options (options_token + options).
POST/me/passkeysRegister passkey. Body: password, name, options_token, credential.
DELETE/me/passkeys/{passkey}Remove passkey. Body: password.
POST/me/logoutRevokes tokens
GET/dashboardPlatform stats — permission dashboard.view; see Dashboard payload
GET/pulse/enterRole-only: superadmin, developer, or tester. Returns { url } — a signed web URL that opens Laravel Pulse (/pulse) on the central domain. Not a Spatie permission. Requires verified email (verified middleware).
GET/horizon/enterRole-only: superadmin, developer, or tester. Returns { url } — a signed web URL that opens Laravel Horizon (/horizon) on the central domain. Not a Spatie permission. Requires verified email (verified middleware).

Platform

Tenants

MethodPathPermissionNotes
GET/tenantstenants.list
POST/tenantstenants.createProvisions default modules — see tenant provisioning
GET/tenants/{tenant}tenants.readIncludes installed_modules when loaded
PUT/PATCH/tenants/{tenant}tenants.update
DELETE/tenants/{tenant}tenants.deleteSoft delete
POST/tenants/{tenant}/restoretenants.restoreAlso restores the tenant's soft-deleted users and domains
DELETE/tenants/{tenant}/forcetenants.force.deleteForce-deletes users, domains, module subscriptions, then the tenant
POST/tenants/{tenant}/archivetenants.archiveSets archived_at; independent of soft delete
POST/tenants/{tenant}/unarchivetenants.archiveClears archived_at
POST/tenants/{tenant}/verify-ownertenants.verifyMarks workspace owner email verified
POST/tenants/{tenant}/branded-domain/mark-ssl-provisionedtenants.updateAfter Forge / proxy TLS is live for the workspace custom domain
GET/tenants/{tenant}/entitlementstenants.read{ core, modules } — licensing only
POST/tenants/{tenant}/modulesmodule-subscriptions.createInstall module — body: module_id, optional billing_cycle
GET/tenants/{tenant}/invoicesinvoices.listPaginated workspace invoices
GET/tenants/{tenant}/paymentspayments.listPaginated workspace payments
GET/tenants/{tenant}/impersonation-sessionsimpersonation.listPaginated sessions for this workspace (reason, admin, start/end; no token)
GET/tenants/{tenant}/audit-logstenants.readPaginated platform activity_log rows for this workspace
POST/tenants/{tenant}/impersonateimpersonation.startBody: reason (required, 5–1000 chars)

Tenant create/update body: company_name, workspace_name?, slug?, email, phone?, logo? (image upload), notes?, status?, timezone?, currency?, country?, locale?. Multipart form-data is supported for logo uploads. Response includes logo_path and logo_url. Platform domain is auto-generated from the slug + PLATFORM_DOMAIN_SUFFIXES (client domain values are ignored). Custom domains are tenant self-service via the Branded module.

Users

MethodPathPermissionNotes
POST/users/inviteusers.inviteCreates the user, assigns roles, emails an invite, sets invite_token/invite_sent_at
GET/usersusers.list
POST/usersusers.create
GET/users/{user}users.read (or self)
PUT/PATCH/users/{user}users.update
DELETE/users/{user}users.delete
POST/users/{user}/restoreusers.restore
DELETE/users/{user}/forceusers.force.delete
POST/users/{user}/suspendusers.suspend
POST/users/{user}/unsuspendusers.unsuspend
POST/users/{user}/change-passwordusers.reset-password
GET/users/{user}/activityusers.read (or self)Up to 50 recent spatie/laravel-activitylog entries for the user

Invite body: name, email, phone?, role[] (role names, at least one).

Create/update body adds: phone?, avatar_path?, password (create), role[].

Roles

MethodPathPermissionNotes
GET/roles/permissions-matrixroles.listAll permissions grouped by prefix, with the list of role names holding each one
GET/rolesroles.list
POST/rolesroles.create
GET/roles/{role}roles.readReturns the role plus every permission with an is_assigned flag
PUT/PATCH/roles/{role}roles.updateBody: name, permissions[] (permission IDs)
DELETE/roles/{role}roles.deleteBlocked for protected roles (config('central-protected-roles'))
POST/roles/{role}/cloneroles.cloneCopies all permissions to a new role; optional name in body, otherwise auto-generated

permissions-matrix response shape: [{ "id", "name", "group", "roles": ["admin", "manager", ...] }], where group is the permission name's prefix before the first . (e.g. tenants for tenants.archive).

Catalog (admin)

ResourcePathsNotes
ModulesCRUD + restore/forceFull catalog admin. Fields include uuid, pricing (monthly_price, yearly_price, currency), status, is_default_included, is_billableno payment-provider IDs

Default-included modules (Leads, Tasks, ToDos) cannot be deleted while marked is_default_included. Modules with workspace subscriptions cannot be deleted until those subscriptions are removed.

Provider price mappings are managed under Payment Gateways (GET/PUT /payment-gateways/{id}/module-prices), not on Modules. Features catalog has been removed — modules are licensing products; Spatie permissions handle authorization.

Marketplace

Published modules only. Permission: modules.list / modules.read.

MethodPathNotes
GET/marketplace/modulesPaginated; filters: search, category_id
GET/marketplace/modules/{module}Detail + already_installed, can_cancel, blocking_dependents, dependency hints; optional ?tenant_id=

Install for a workspace: POST /tenants/{tenant}/modules. Tenants self-serve via Tenant Marketplace (purchase / cancel).

Module subscriptions

MethodPathPermissionNotes
GET/module-subscriptionsmodule-subscriptions.listFilters: tenant_id, status, source
GET/module-subscriptions/{module_subscription}module-subscriptions.readIncludes module, tenant, history
POST/module-subscriptions/{module_subscription}/cancelmodule-subscriptions.updatePurchased modules only; included modules rejected
POST/module-subscriptions/{module_subscription}/deactivatemodule-subscriptions.deactivatePlatform-admin suspend (works on included modules)

Financial ledger (read-only)

MethodPathPermissionNotes
GET/invoicesinvoices.listPlatform-wide paginated list
GET/invoices/{invoice}invoices.readIncludes tenant, items, payments
GET/paymentspayments.listPlatform-wide paginated list
GET/payments/{payment}payments.readIncludes tenant, invoice, transactions

Tenant-scoped lists: GET /tenants/{tenant}/invoices, GET /tenants/{tenant}/payments.

Invoices are created by the Billing Engine (consolidated run or purchase settlement) — no public write endpoints.

Payment gateways

MethodPathPermissionNotes
GET/payment-gatewayspayment-gateways.listList providers
GET/payment-gateways/{id}payment-gateways.readRedacted config (secrets never returned)
POST/payment-gateways/{id}/enablepayment-gateways.update
POST/payment-gateways/{id}/disablepayment-gateways.updateRejects if default
POST/payment-gateways/{id}/defaultpayment-gateways.updateSyncs default_payment_gateway setting
PUT/payment-gateways/{id}/configpayment-gateways.updateMerge encrypted credentials
PUT/payment-gateways/{id}/modepayment-gateways.updatesandbox | live
POST/payment-gateways/{id}/test-connectionpayment-gateways.updateDriver probe
GET/payment-gateways/{id}/webhook-statuspayment-gateways.read
GET/payment-gateways/{id}/logspayment-gateways.readOperational logs
GET/payment-gateways/{id}/webhook-logspayment-gateways.read
GET/payment-gateways/{id}/capabilitiespayment-gateways.readCapabilities + currencies + requires_product_mapping
GET/payment-gateways/{id}/module-pricespayment-gateways.readGateway ↔ module product/price mappings
PUT/payment-gateways/{id}/module-pricespayment-gateways.updateReplace mappings ({ mappings: [...] }); 422 if gateway does not require mapping

Also accepts billing.manage as an alternate permission.

Impersonation

MethodPathPermissionNotes
GET/tenants/{tenant}/impersonation-sessionsimpersonation.listPaginated history for the workspace; optional search on reason / admin
POST/tenants/{tenant}/impersonateimpersonation.startCreates session; audits reason, IP, user-agent
POST/impersonation/{impersonation}/endimpersonation.end (or session owner)Sets ended_at, duration_seconds

Start returns session metadata plus a short-lived tenant_token. List responses never include tokens.

Tenant Audit Logs (GET /tenants/{tenant}/audit-logs, tenants.read) include impersonation_started / impersonation_ended (and other platform events for that workspace). List responses allowlist properties to a safe subset — e.g. reason, impersonation session ids, duration_seconds, tenant_id, actor/ip metadata — and omit nested before / after blobs and other unreviewed keys. Full audit rows remain in the database; only the list resource redacts.

Stripe / gateway / email webhooks

MethodPathNotes
POST/stripe/webhookCashier-compatible path (config('cashier.path') + /webhook)
POST/webhooks/gateways/{code}Gateway-agnostic ingress for all drivers
POST/webhooks/email/{provider}Email delivery webhooks (Central)
POST/webhooks/email/{provider}/{tenant}Email delivery webhooks (Tenant custom mail)

Not under /api/central/v1. Payment paths normalize via PaymentGatewayInterface::parseWebhook() into BillingEngine. Email paths use SupportsWebhooks drivers. CSRF-exempt; rate-limited. Stripe Cashier route additionally syncs Cashier mirror tables.

System

MethodPathNotes
GET/public/settingsUnauthenticated bootstrap (branding, formats, registration/Founding Beta/maintenance flags). No secrets.
GET/public/statsUnauthenticated marketing Trust metrics: workspaces (active non-archived), module_installations (active/trial subscriptions), published_modules, uptime_percent, currency (platform default).
GET/public/modulesUnauthenticated marketing catalog: { currency, modules[] } with availability (available / in-progress / planned), pricing (included / free / paid when available), and prices in catalog modules.currency (not workspace default system_settings.currency). Excludes deprecated.
POST/public/beta-applicationsPublic beta intake, throttled 10/min. Returns the application UUID.
GET/public/beta-invites/{token}Public invite preview, throttled 60/min. Returns valid, expired, activated, email, name, company, and message.
POST/public/beta-invites/resendBody: email; throttled 5/min. Rotates and emails only if Central already issued an invite (invite_sent_at / token present), status accepted, and not activated. Always returns the same non-enumerating success message.
POST/public/register-workspaceSelf-service workspace create when registration_enabled; otherwise 403 unless invite_token identifies an accepted, active, unactivated Founding Beta invite and email matches the application. Body: company_name, owner_name, email, password (+ confirmation), optional invite_token; platform domain auto-generated from slug (client domain ignored).
GET/system-settingsAll admin settings (secrets masked). Response meta.mail_webhook includes webhook URL + event catalog when the active provider supports webhooks.
PUT/system-settings{ "settings": { "key": value } } — per-key validation; may include mail_webhook_events / mail_webhook_secret
POST/system-settings/test-mail{ "email": "…" } — sends test mail using runtime SMTP config
POST/system-settings/branding/{asset}Multipart file upload. Assets: logo, favicon, auth-image-login, auth-image-register, auth-image-forgot-password, auth-image-reset-password, auth-image-email-verify
GET/email-logsPaginated delivery logs (has_body; bodies omitted)
GET/email-logs/{uuid}Log detail including body_html / body_text
POST/email-logs/{uuid}/resendResend from stored body (email-logs.resend, throttle 6/min)

Settings groups: general, localization, mail, branding, security, maintenance, billing.

Consumed keys

GroupKeysRuntime use
generalapp_name, company_name, timezone, locale, currency, registration_enabled, founding_beta_enabled, founding_beta_apply_url, founding_beta_invite_ttl_daysApp title/config, tenant defaults, self-service registration, registration-closed beta CTA, invite lifetime
localizationdate_format, time_formatCentral SPA formatters
mailmail_provider, SMTP / Postmark / Mailgun credentials, mail_webhook_secret, mail_webhook_events, From identityLaravel mail + delivery webhooks
brandingbutton_color, support_email, logo_path, favicon_path, auth_image_*_pathSPA CSS/document.title/sidebar/auth panels; support footer on tenant-facing emails
securitysession_lifetime_minutes, password_min_length, password_require_specialSession lifetime; centralized PasswordRule / Password::defaults()
maintenancemaintenance_mode, maintenance_message, maintenance_etaTenant Application only (tenant.available middleware). Central stays up.
billinginvoice_prefix, proration_mode, default_payment_gateway, trial_enabled, stripe_enabled, stripe_webhook_configuredBilling engine / invoices

Removed: primary_color, feature_registration, feature_invites, queue_connection_display, filesystem_disk.

Founding Beta application administration

All administration endpoints require Central authentication. Listing/reading uses the matching beta-applications.list / beta-applications.read permission; update and invite actions use beta-applications.update.

MethodPathNotes
GET/beta-applicationsPaginated list; filters status, search; sort by created_at, updated_at, name, or status.
GET/beta-applications/{beta_application}Full application including invite timestamps and has_active_invite, invite_expired, activated.
PATCH/beta-applications/{beta_application}Update status and internal notes.
POST/beta-applications/{beta_application}/inviteAccepts the application (unless already activated — then 422), rotates the hashed token and expiry, queues the encrypted invite email, and returns the application plus one-time plaintext invite_url.

Dashboard payload

GET /dashboard returns workspace stats, module subscription status counts, revenue (MRR from billable active subscriptions), growth series, recent tenants, recent module subscriptions, recent activities.

Series shapes (frontend contract):

  • growth[]: { month: "YYYY-MM", count: number }
  • revenue_series[]: { month: "YYYY-MM", amount: number } (zeros until paid modules exist)

Permissions

Seeded by Database\Seeders\Central\PermissionsSeeder, guard central-api.

GroupPermissions
userslist, create, read, update, delete, restore, force.delete, suspend, unsuspend, invite, reset-password
tenantslist, create, read, update, delete, restore, force.delete, archive
roleslist, create, read, update, delete, clone
dashboardview
billingmanage
moduleslist, create, read, update, delete, restore, force.delete
module-subscriptionslist, create, read, update, delete, deactivate
invoiceslist, read, update
paymentslist, read, update
impersonationstart, end, list
system-settingslist, update
beta-applicationslist, read, update
feedbacklist, read, update, comment, stats

Removed

  • /features (+ restore/force)
  • /plans, /plans/{plan}/modules|features|limits
  • /limit-definitions
  • /tenant-subscriptions (+ cancel/resume/suspend)
  • /subscriptions, /setting-definitions

Artisan

CommandNotes
billing:run-consolidatedDaily scheduled; invoices all due workspaces

Postman: EloSync-Backend/.docs/postman/Central.postman_collection.json (refresh after API changes).

Official documentation for the EloSync SaaS Platform.