Platform Architecture & Super Admin Role
PLT-001The Platform Admin (also called Super Admin) is the SaaS operator β the entity that runs the multi-tenant Engineering Services platform. While individual engineering businesses (tenants) each operate their own isolated portal environment, the Platform Admin sits above all tenants and has unrestricted visibility and control across every Business record, subscription plan, and audit event in the system.
Each "tenant" represents one engineering business that has subscribed to the platform. Tenants operate through four role-specific portals (Admin, Employee, Client, Contractor). The Platform Admin manages the tenants themselves β provisioning, suspending, upgrading, and monitoring them β but does not participate in their day-to-day operations.
Platform Admin vs Tenant Admin
| Dimension | Platform Admin (Super Admin) | Tenant Admin |
|---|---|---|
| Scope | All tenants, all plans, all audit events | Single tenant's own data only |
user_type | platform_admin | admin |
| Auth guard | rejectUnlessSuperAdmin() | rejectUnlessPortalUserType() |
| Route prefix | /api/platform/* | /api/* scoped to business |
| Business isolation | None β sees all businesses | Strict β business_id bound |
| Tenant CRUD | Yes β creates & manages tenants | No |
| Subscription plans | Full CRUD | Read-only (own plan) |
| Audit log | Platform-level audit events | Business-level activity only |
Route Prefix
All Platform Admin endpoints live under /api/platform/. There is no tenant context in these routes β the Platform Admin sees the system holistically.
GET /api/platform/dashboardβ KPI summaryGET /api/platform/tenantsβ list all tenantsPOST /api/platform/tenantsβ create tenantPUT/PATCH /api/platform/tenants/{id}β update tenantDELETE /api/platform/tenants/{id}β soft-deactivate tenantGET /api/platform/subscription-plansβ list plansPOST /api/platform/subscription-plansβ create planPUT/PATCH /api/platform/subscription-plans/{id}β update planDELETE /api/platform/subscription-plans/{id}β delete/deactivate planGET /api/platform/notificationsβ audit eventsPOST /api/platform/notifications/{id}/mark-readβ mark one readPOST /api/platform/notifications/mark-all-readβ bulk mark readDELETE /api/platform/notifications/{id}β delete audit event
Authentication & Permission Model
PLT-002
Platform Admin authentication uses the same POST /api/login endpoint as other portals but requires
user_type = 'platform_admin'. The rejectUnlessSuperAdmin() middleware guard is
stricter than the standard portal check β it specifically validates that the authenticated
user is a platform admin rather than any portal-type user.
Authentication Flow
POST /api/login with email, password, user_type: "platform_admin"user_type = 'platform_admin'. Returns Bearer token.Authorization: Bearer {token} in every subsequent request to /api/platform/*rejectUnlessSuperAdmin() middleware checks token user type on each request. Returns 403 if check fails β even if token is valid for a different user_type.rejectUnlessSuperAdmin() check with HTTP 403. Platform Admin tokens cannot be used on regular
tenant portal routes and vice versa.
Permission System
Beyond the super admin guard, each capability requires a granular permission assigned to the platform admin user:
| Permission Key | Scope & Allows | Endpoints Protected |
|---|---|---|
tenants.view |
List and view all tenant (Business) records | GET /api/platform/tenants, GET /api/platform/tenants/{id} |
tenants.manage |
Create, update, and soft-deactivate tenants; supersedes view | POST, PATCH, DELETE /api/platform/tenants/* |
subscription_plans.view |
List and view subscription plan records | GET /api/platform/subscription-plans |
subscription_plans.manage |
Full CRUD on subscription plans; supersedes view | POST, PATCH, DELETE /api/platform/subscription-plans/* |
platform_notifications.view |
Read audit event log; filter and search events | GET /api/platform/notifications, GET /api/platform/notifications/{id} |
platform_notifications.manage |
Mark events read (individual or bulk); hard delete events; supersedes view | POST mark-read, POST mark-all-read, DELETE /api/platform/notifications/{id} |
.manage permission implies the corresponding
.view permission. Both guards must pass: (1) rejectUnlessSuperAdmin() then
(2) the specific permission check. Missing either returns 403.
Token Security Notes
- Platform Admin Bearer tokens should be treated as high-privilege credentials β store in secure vault, not env vars or source code.
- Tokens are scoped to a single
user_typeβ cross-type use is rejected server-side. - All platform admin actions are audit-logged via
PlatformAuditLoggerβ token compromise creates a forensic trail. - On token expiry, re-authenticate via
POST /api/loginβ returns a fresh token.
Dashboard KPIs
PLT-003
The Platform Dashboard (GET /api/platform/dashboard) returns a real-time aggregate view of the
entire multi-tenant system. All counts are computed on-demand from the database β there is no caching layer
between the API response and the underlying records.
KPI Fields & Derivations
KPI Field Reference
| Response Field | Type | Source | Notes |
|---|---|---|---|
tenant_count | int | Business.count() | Total tenants β all statuses |
active_tenant_count | int | Business WHERE is_active=true | Tenants currently operating |
suspended_tenant_count | int | tenant_count - active_tenant_count | Arithmetic derivation |
subscription_plan_count | int | SubscriptionPlan WHERE is_active=true | Excludes deactivated plans |
plan_breakdown | array | SubscriptionPlan.withCount(businesses) | Ordered cheapest β most expensive |
recent_tenants | array[5] | Business.with(plan).limit(5) | Latest 5 onboarded, includes plan name |
plan_breakdown Object
Each entry in plan_breakdown represents one subscription plan with its assigned tenant count:
| Field | Type | Description |
|---|---|---|
id | int | Plan ID |
name | string | Plan name (e.g. "Professional") |
slug | string | URL-safe identifier |
monthly_price | numeric | Plan price β sort key |
tenant_count | int | Number of Business records on this plan |
is_active | bool | Only active plans in breakdown |
Example Dashboard Snapshot
PlatformDashboardController::index() calls
rejectUnlessSuperAdmin() but does not require a specific permission beyond being a platform admin.
The dashboard is read-only β it never modifies data.
Tenant Management
PLT-004
Tenant management is the primary responsibility of the Platform Admin. A "tenant" is a
Business model record β each representing one engineering company subscribed to the platform.
Required permission: tenants.manage (plus rejectUnlessSuperAdmin()).
Tenant Fields
| Field | Type | Source | Description |
|---|---|---|---|
id | int | AUTO | Primary key |
business_name | string | REQUIRED | Full business name (e.g. "Al-Noor Engineering") |
owner_name | string | REQUIRED | Primary contact person name |
contact_email | string | REQUIRED | Primary contact email |
contact_phone | string | OPTIONAL | Phone number |
subdomain_slug | string | AUTO | URL-safe slug derived from business_name; unique |
subscription_plan_id | int | REQUIRED | FK β SubscriptionPlan (must be is_active=true) |
subscription_status | enum | AUTO | active | suspended | past_due | cancelled |
is_active | bool | AUTO | true on creation; false = deactivated |
created_at | timestamp | AUTO | Onboarding date |
Create Tenant β Transaction Flow (CRITICAL)
Tenant creation runs inside a database transaction (DB::transaction). All steps succeed or the
entire creation is rolled back. This ensures the Business record and its admin user are always created together.
POST /api/platform/tenants with:business_name REQUIRED
owner_name REQUIRED
contact_email REQUIRED
subscription_plan_id REQUIRED
contact_phone OPTIONAL
create_admin_user BOOL, default: true
subscription_plan_id: plan must exist AND
is_active = true. If plan is inactive or missing β 422 "Invalid subscription plan".
subdomain_slug:base = Str::slug(business_name) (e.g. "Al-Noor Engineering" β al-noor-engineering)If slug already exists: appends
-1, -2, ... until unique.
Business record with
is_active=true, subscription_status='active', and the generated subdomain_slug.
create_admin_user = true: Creates User record with
user_type='admin', business_id set to new Business ID,
username = {subdomainSlug_admin} (dashes stripped + "_admin"),
temp_password = random(12 chars).
PlatformAuditLogger.tenantCreated() β creates PlatformAuditEvent
with event_code='TENANT_CREATED', actor = authenticated platform admin user.
END transaction.
admin_invite object:
{ user_id, username, email, temporary_password }
username and temporary_password to the business owner
(typically via phone or secure message β never via unsecured email).
Subdomain Slug & Admin Username Derivation
Tenant Status Lifecycle
Tenants have two independent status indicators that work together:
| Field | Values | Meaning |
|---|---|---|
is_active | true / false | Binary active/deactivated flag. Changing to false triggers tenantSuspended() audit event. |
subscription_status | active | suspended | past_due | cancelled | Detailed subscription state. Can be patched independently. |
Subscription Status State Machine
is_active transitions from
true β false (or false β true) during an update,
PlatformAuditLogger.tenantSuspended() is called automatically. This is in addition to
the standard tenantUpdated() diff log.
Update Tenant (PATCH semantics)
PATCH /api/platform/tenants/{id} β all fields optional. Only supplied fields are updated.
Changed fields are diffed and logged to the platform audit log.
| Field | Tag | Validation | Audit Notes |
|---|---|---|---|
business_name | OPTIONAL | string | Logged in diff |
owner_name | OPTIONAL | string | Logged in diff |
contact_email | OPTIONAL | email format | Logged in diff |
contact_phone | OPTIONAL | string | Logged in diff |
subscription_plan_id | OPTIONAL | must exist + is_active=true | Plan change logged |
subscription_status | OPTIONAL | in: active, suspended, past_due, cancelled | Status change logged |
is_active | OPTIONAL | boolean | Change fires tenantSuspended() event |
Destroy Tenant β Soft Deactivation
DELETE /api/platform/tenants/{id} performs a
soft deactivation. It sets is_active = false and
subscription_status = 'suspended' then calls
PlatformAuditLogger.tenantSuspended(). The Business record is preserved. Returns
{ id, is_active: false }.
Tenant Listing Filters
| Query Param | Type | Effect |
|---|---|---|
subscription_plan_id | int | Filter tenants by their assigned plan |
is_active | boolean (0/1/true/false) | Filter active or deactivated tenants |
subscription_status | string | Filter by status (active/suspended/past_due/cancelled) |
8-Step Tenant Onboarding Journey
is_active=true that matches the incoming tenant's needs (see PLT-005).POST /api/platform/tenants with business_name, owner_name, contact_email, subscription_plan_id, and create_admin_user: true.subdomain_slug from business_name. Creates Business record (is_active=true, subscription_status='active').user_type='admin', business_id = new tenant ID, username = {slug}_admin, generates secure 12-char temp_password.PlatformAuditLogger.tenantCreated(). API response includes admin_invite: { user_id, username, email, temporary_password }.username and temporary_password to the business owner via phone or secure channel.Subscription Plans
PLT-005
Subscription plans define the feature limits and pricing for each tier of service offered on the platform.
Required permission: subscription_plans.manage. All plan changes are logged via
PlatformAuditLogger.
Plan Field Reference
| Field | Type | Tag | Validation | Notes |
|---|---|---|---|---|
name | string | REQUIRED | max:128 | Human-readable plan name |
slug | string | AUTO | unique across all plans | Auto-generated from name if not provided |
monthly_price | numeric | REQUIRED | min:0 | Can be 0 for free/trial plans |
max_projects | int | REQUIRED | min:-1 | -1 = unlimited |
max_locations | int | REQUIRED | min:-1 | -1 = unlimited |
max_employees | int | REQUIRED | min:-1 | -1 = unlimited |
has_client_portal | bool | OPTIONAL | boolean | Unlocks client portal feature for tenants on this plan |
is_active | bool | OPTIONAL | boolean | Inactive plans cannot be assigned to tenants |
The -1 Unlimited Convention
max_projects, max_locations, and
max_employees, the value -1 signals no limit. The API normalizes this value
server-side β client code should send -1 literally to indicate unlimited. Any non-negative
integer sets a hard cap enforced within the tenant portal.
Plan Slug Auto-Generation
Example Plans
| Plan | Monthly Price | max_projects | max_locations | max_employees | has_client_portal |
|---|---|---|---|---|---|
| Starter | PKR 5,000/mo | 5 | 2 | 10 | No |
| Professional | PKR 15,000/mo | 25 | 10 | 50 | Yes |
| Enterprise | PKR 35,000/mo | -1 (unlimited) | -1 (unlimited) | -1 (unlimited) | Yes |
| Trial | PKR 0/mo | 2 | 1 | 5 | No |
has_client_portal Flag
When has_client_portal = false, tenants on this plan cannot activate or use the client portal
feature β even if they create client users. The tenant admin portal may show the feature as locked or
unavailable. Upgrading a tenant's plan to one with has_client_portal = true immediately
unlocks the feature without data loss.
Plan Lifecycle & Destruction Rules
DELETE /api/platform/subscription-plans/{id} checks
Business WHERE subscription_plan_id = $planβid. If count > 0, the plan is
deactivated (not deleted) and the API returns 422 "Plan has assigned tenants; deactivated
instead of deleted". The response includes the now-deactivated plan. To truly delete a plan,
first reassign all tenants to a different plan.
5-Step Plan Creation Flow
POST /api/platform/subscription-plans with name, monthly_price, max_projects, max_locations, max_employees, has_client_portal.max_* values are integer β₯ -1. Confirms monthly_price β₯ 0.slug from name (unless explicitly provided). Ensures uniqueness with suffix if collision.SubscriptionPlan record with is_active=true. Calls PlatformAuditLogger.planCreated() β PLAN_CREATED audit event.Plan Validation Quick Reference
| Rule | Detail |
|---|---|
| Plan must be active to assign | Tenant creation/update with an inactive plan_id β 422 "Invalid subscription plan" |
| -1 is the only unlimited marker | Any integer β₯ 0 sets a hard cap; -1 = no limit |
| Slug must be globally unique | Manual slug on update that conflicts β 422 "Slug already in use" |
| monthly_price can be 0 | Free/trial plans allowed (min:0, not min:1) |
| name max 128 chars | Longer names will fail validation |
Notifications & Audit Logs
PLT-006
The Platform Audit Log is a table of PlatformAuditEvent records that capture
every significant operation performed through the Platform Admin console. Events are auto-created by
PlatformAuditLogger and are never sent to tenant users β this is the platform operator's
internal forensic trail.
Required guard: rejectUnlessSuperAdmin(). Required permission: platform_notifications.view
(or platform_notifications.manage for write operations).
PlatformAuditEvent records are not
exposed to any tenant portal (Admin, Employee, Client, Contractor). Tenants cannot see platform-level
audit events. This log is exclusively for the SaaS operator's oversight.
Two separate API concerns
| Purpose | Controller | Endpoints | Notes |
|---|---|---|---|
| Bell dropdown | PlatformNotificationController |
GET /notificationsPATCH /notifications/read-all |
Max 20 most recent unread only. No pagination key. |
| Audit log screen | PlatformAuditLogController |
GET /audit-logs + full CRUD (5 endpoints) |
Paginated. Supports all filters. Full history. |
Audit Event Fields
| Field | Type | Description |
|---|---|---|
id | int | Unique event ID β searchable |
title | string | Short human-readable event title β searchable |
body | text | Detailed event description, often includes diff data β searchable |
event_code | string | Machine-readable code (e.g. TENANT_CREATED) β searchable, filterable |
category | string | Grouping category (e.g. "tenant", "plan") β filterable |
severity | string | e.g. "info", "warning", "action_taken" β filterable |
entity_type | string | e.g. "business", "subscription_plan" β filterable |
is_read | bool | Read status β filterable (0/1/true/false) |
actor | User | Eager-loaded user who triggered the event; null for system events |
created_at | timestamp | When the event occurred |
Event Codes
PlatformAuditLogger.tenantCreated() when a new Business is onboarded via POST /api/platform/tenants. Actor = platform admin who created the tenant.PlatformAuditLogger.tenantUpdated() after any field changes on a Business record. Body contains the diff of changed fields.PlatformAuditLogger.tenantSuspended() when is_active changes (in update) OR when DELETE /api/platform/tenants/{id} is called.PlatformAuditLogger.planCreated() when a new SubscriptionPlan is created.PlatformAuditLogger.planUpdated() when plan limits or pricing are changed. Body includes price/limit diff.Audit Log Filters (GET /api/platform/audit-logs)
| Query Param | Type | Effect |
|---|---|---|
category | string | Filter by category (e.g. "tenant", "plan") |
severity | string | Filter by severity (e.g. "info", "warning") |
entity_type | string | Filter by entity type (e.g. "business", "subscription_plan") |
is_read | 0/1/true/false | Filter unread (0) or read (1) events |
search | string | Full-text search across title, body, event_code, category, and id |
per_page | int | Default 15; paginated response |
Audit Log Operations
| Operation | Endpoint | Permission | Notes |
|---|---|---|---|
| Bell dropdown (recent unread) | GET /api/platform/notifications | view | Max 20 unread, newest first, no pagination |
| Mark all read (bell) | PATCH /api/platform/notifications/read-all | manage | Clears bell badge |
| List all events | GET /api/platform/audit-logs | view | Paginated, all filters supported |
| View single event | GET /api/platform/audit-logs/{id} | view | Returns event with actor loaded |
| Mark all read | PATCH /api/platform/audit-logs/read-all | manage | Bulk: all unread β read |
| Mark one read | PATCH /api/platform/audit-logs/{id}/read | manage | Sets is_read = true on one event |
| Delete event | DELETE /api/platform/audit-logs/{id} | manage | Hard delete β permanent, no soft delete |
PlatformAuditEvent is a hard delete β
there is no soft delete or recycle bin. Only delete audit events if you have an external audit trail backup
or compliance policy that permits it. Typically, only mark events as read rather than deleting them.
End-to-End Tenant Onboarding Scenario
PLT-007This 15-step scenario walks through the complete lifecycle of onboarding a new engineering business β "Al-Noor Engineering" β from plan creation to the tenant's first active project, as seen from the Platform Admin perspective.
tenant_count: 10,
active_tenant_count: 10, subscription_plan_count: 2 (Starter + Enterprise only),
recent_tenants shows last 5 onboarded businesses.
POST /api/platform/subscription-plansname: "Professional" Β· monthly_price: 15000 Β·
max_projects: 25 Β· max_locations: 10 Β·
max_employees: 50 Β· has_client_portal: true
slug: "professional".
PlatformAuditLogger.planCreated() fires β
audit event PLAN_CREATED logged with actor = Platform Admin.
Dashboard subscription_plan_count now shows 3.
POST /api/platform/tenantsbusiness_name: "Al-Noor Engineering"owner_name: "Mr. Bilal Ahmed"contact_email: "bilal@alnoor.pk"subscription_plan_id: 3 (Professional)create_admin_user: true
subdomain_slug = "al-noor-engineering"
via Str::slug("Al-Noor Engineering"). Checks no existing Business uses this slug β unique, no suffix needed.
str_replace('-','','al-noor-engineering') + '_admin'
= "alnoorengineering_admin"
"Kx7@mPqZ3nWr" (random 12 chars, mixed case, digits, symbols).
Business (id=11, is_active=true, status='active') β
Creates User (user_type='admin', business_id=11, username='alnoorengineering_admin', hashed temp_password).
PlatformAuditLogger.tenantCreated() fires β TENANT_CREATED event logged.
API returns HTTP 201 with:admin_invite: { user_id: 47, username: "alnoorengineering_admin", email: "bilal@alnoor.pk", temporary_password: "Kx7@mPqZ3nWr" }
alnoorengineering_admin and temporary password Kx7@mPqZ3nWr.
Does NOT send via unsecured email.
has_client_portal: true). Client portal goes live.
tenant_count: 11,
active_tenant_count: 11. Al-Noor Engineering visible in plan_breakdown
under Professional (now 7 tenants on Professional).
GET /api/platform/notifications?search=al-noor. Sees:
TENANT_CREATED (onboarding day) + TENANT_UPDATED (password change event)
+ PLAN_CREATED (Professional plan creation). All events show correct actor and timestamps.
Error Reference
PLT-008
All Platform Admin endpoints follow consistent HTTP error semantics. Errors include a message
field describing the issue. Validation errors (422) include a errors object with per-field
details.
| HTTP Code | Error Message / Condition | Root Cause | Resolution |
|---|---|---|---|
| 401 Unauthorized | Token missing, malformed, or expired | No Authorization header, or Bearer token has expired | Re-authenticate via POST /api/login with platform_admin credentials; use fresh token |
| 403 Forbidden | rejectUnlessSuperAdmin() check failed |
Token belongs to a non-platform_admin user_type (e.g. admin portal token used on platform route) | Authenticate as a user with user_type = 'platform_admin' specifically |
| 403 Forbidden | Missing permission (e.g. tenants.manage) | Platform admin user lacks the required granular permission for the operation | Grant the required permission (tenants.manage, subscription_plans.manage, etc.) to the platform admin user |
| 404 Not Found | Tenant or plan ID not found | The {id} in the route does not correspond to any Business or SubscriptionPlan record |
Verify the ID via the list endpoint; ensure the record was not deleted |
| 422 | "Invalid subscription plan" | subscription_plan_id references a plan that does not exist or has is_active = false |
Use a plan ID where is_active = true. Check plan status via GET /api/platform/subscription-plans |
| 422 | "Plan has assigned tenants; deactivated instead of deleted" | Attempted DELETE /api/platform/subscription-plans/{id} on a plan with 1+ active tenant assignments |
Reassign all tenants from this plan to another plan first, then retry delete. Or accept the deactivation (plan is now is_active=false) |
| 422 | "Slug already in use" | Attempted to set a plan slug to a value already used by another plan |
Choose a different slug or omit it (allow auto-generation with uniqueness suffix) |
| 422 | Validation errors (various) | Required fields missing, invalid types, out-of-range values (e.g. max_projects < -1) | Check errors object in response for per-field details. Ensure all max_* values β₯ -1, monthly_price β₯ 0, name β€ 128 chars |
Common Mistakes
- Using an admin portal token on
/api/platform/*routes β always re-auth asplatform_admin - Assigning a deactivated plan during tenant create/update β check
is_activebefore assignment - Expecting hard delete on tenants β
DELETEonly soft-deactivates; usePATCH is_active:falsefor the same effect - Confusing
subscription_statuswithis_activeβ both exist independently - Expecting DELETE on plans with tenants to work β always deactivates instead, returns 422
Debug Checklist
platform_admin user? Check user_type field of the authenticated user.tenants.manage, subscription_plans.manage, platform_notifications.manage)subscription_plan_id referencing an active plan? (is_active = true)GET /api/platform/tenants?subscription_plan_id={id})GET /api/platform/notifications?is_read=0 shows unread events triggered by the failed operation.Engineering Services API Β· Platform Admin Business Guide Β· β Documentation Hub