# Milestone 6A — SaaS Dashboard Architecture and UX Specification

| Attribute | Value |
|---|---|
| Status | Approved — Architecture Frozen |
| Revision | Revision 3 — Architecture Addendum 6A-1 |
| Milestone type | Architecture and UX specification only |
| Product | MHI Gateway multi-tenant communications platform |
| Date | 2026-07-26 |
| Target stack | Laravel 12, PHP 8.4, MySQL, Redis, queues |
| Review status | Final independent architecture review approved |
| Implementation status | No dashboard implementation is included or authorized by this revision alone |

### Revision history

| Revision | Date | Summary | Implementation authority |
|---|---|---|---|
| Initial | 2026-07-26 | Original dashboard architecture and UX specification | None |
| Revision 1 | 2026-07-26 | Adds first-class Sender IDs, Developer Center, API operational analytics, component health, bounded troubleshooting search, feature-flag architecture, and Integration Launch UX following independent architecture review | None; a separate approved implementation milestone is still required |
| Revision 2 | 2026-07-26 | Resolves Integration Launch completion authority and records remaining implementation ownership decisions for Architecture Freeze preparation | None; pending final independent architecture review |
| Addendum 6A-1 | 2026-07-26 | Freezes Overview dashboard time ranges, UTC boundary semantics, message outcome definitions, delivery-rate calculation, required metrics, and unavailable-source behavior | Authorizes later implementation only through a separately approved milestone |

## 1. Purpose and scope

This document specifies the target architecture and user experience for the MHI Gateway SaaS dashboard. It is the design authority for a later sequence of implementation milestones; it does not authorize implementation by itself.

The dashboard is a browser-based control plane for:

- platform operations staff managing the SaaS service;
- tenant users managing their own organization, applications, credentials, message activity, webhooks, team, and settings;
- tenant users managing Sender ID requests, assignments, and approved inventory without gaining approval authority;
- developers integrating through a versioned Developer Center whose public API contract remains the repository OpenAPI document;
- future finance workflows involving wallets, billing, rates, and subscriptions;
- future communications channels beyond SMS.

This milestone changes no runtime behavior. In particular, it does not add or modify routes, controllers, authentication, middleware, database migrations, models, policies, Livewire components, jobs, queues, or production configuration.

### 1.1 Goals

1. Preserve strict tenant isolation in every dashboard use case.
2. Keep business logic outside HTTP and UI components.
3. Provide an understandable division between platform and tenant workspaces.
4. Make the most common integration and operational tasks discoverable.
5. Define a least-privilege authorization model that can grow without role checks leaking throughout the UI.
6. Reuse existing application and domain authorities rather than creating dashboard-specific business rules.
7. Establish extension seams for billing, wallet, rate limits, subscription plans, and additional channels.
8. Define accessibility, privacy, audit, and responsive behavior before implementation.
9. Keep API usage metrics and component health tied to authoritative, freshness-labeled sources.
10. Preserve one authoritative message lifecycle rather than creating a separate delivery-reports model.

### 1.2 Non-goals

- Selecting or installing a UI component library.
- Choosing Blade, Livewire, Inertia, or a JavaScript framework as an irreversible implementation commitment.
- Redesigning session authentication or the public API authentication scheme.
- Building a customer-facing API from dashboard endpoints.
- Adding message composition, campaigns, templates, scheduling, or provider administration.
- Approving Sender IDs through ordinary tenant roles or allowing dashboard settings to bypass data-plane sender validation and routing.
- Replacing or duplicating the authoritative OpenAPI public API contract.
- Claiming API usage or system health from message-row approximations or missing telemetry.
- Implementing payment collection, wallet top-up, invoicing, pricing, subscriptions, or rate-limit mutation.
- Exposing raw SMPP protocol data, provider credentials, internal database identifiers, message ciphertext, or decrypted message bodies.
- Defining exact commercial prices, quotas, retention periods, trial length, suspension grace periods, or support service levels.

## 2. Repository baseline and compatibility

The specification is grounded in the repository state at the date above.

### 2.1 Existing foundations

| Capability | Existing authority | Dashboard use |
|---|---|---|
| Browser identity | `users`, Laravel `web` guard | Reuse; authentication remains unchanged |
| Platform role | `users.platform_role`, value `platform_admin` | Entry to the platform workspace |
| Tenant identity | `tenants`, public ULID, slug, status | Tenant switcher and tenant lifecycle |
| Tenant membership | `tenant_memberships` with role/status | Workspace access and role assignment |
| Tenant roles | `TenantRole` enum | Initial authorization baseline |
| Permissions | `Permission` enum and `TenantAuthorizationService` | Reuse and extend in later milestones |
| Applications | `applications` | Integration grouping and filters |
| API credentials | `api_credentials` | Safe key inventory and rotation workflow |
| Messages | `messages`, `sms_messages`, attempts, events, receipts | Operational list, summary, and safe detail |
| Audit | append-only `audit_logs` | Tenant and platform security history |
| Webhooks | configuration, events, delivery attempts | Configuration and delivery health |
| Wallet | wallets, reservations, immutable ledger | Future finance read model |
| Tenant scoping | explicit `tenant_id`, composite ownership keys, `forTenant` scope | Mandatory dashboard query invariant |
| Public API tenant context | bearer API credential resolver | Remains separate from browser workspace context |

### 2.2 Gaps relevant to later implementation

- `routes/web.php` contains only the welcome page.
- No dashboard presentation layer exists.
- There is no browser tenant-context resolver or tenant switcher contract.
- Existing permissions cover only a subset of the proposed dashboard.
- Membership invitation and invitation-token persistence do not exist.
- Tenant onboarding progress is not modeled.
- Subscription, plan, price, invoice, payment, and tenant quota persistence do not exist.
- Wallet persistence exists, but no browser finance workflow is defined or exposed.
- Aggregate dashboard projections and time-series reporting tables do not exist.
- Durable Sender ID inventory, scoped approvals, assignments, and review evidence do not exist.
- An API request observability/usage-metering store suitable for tenant dashboard analytics does not exist.
- A component-health aggregation contract and durable health incident store do not exist.
- Feature flag and entitlement persistence beyond current configuration and domain state does not exist.
- Membership status is stored as a string but has no dedicated enum in the current domain.
- `settings_json` exists on tenants and applications but must not become an unbounded substitute for modeled business data.

## 3. Architectural principles

### 3.1 Control plane, not data plane

The dashboard is a control and observation plane. Message delivery, SMPP sessions, public API submission, webhook publication, and wallet accounting continue independently of browser requests. A dashboard outage must not stop the messaging data plane.

### 3.2 Explicit workspace and tenant context

Every browser request operates in one of three contexts:

1. **Identity context:** authenticated user, no selected tenant.
2. **Tenant workspace context:** authenticated user plus one active tenant membership.
3. **Platform workspace context:** authenticated platform administrator, with no implicit tenant.

A platform administrator inspecting a tenant must enter an explicit, audited support context. Platform privilege must never silently turn an unscoped tenant query into an allowed query.

### 3.3 Dependency direction

```mermaid
flowchart TB
    Browser[Browser]
    Presentation[Presentation layer<br/>layouts, pages, forms, components]
    HTTP[HTTP boundary<br/>routes, middleware, requests, controllers]
    App[Application layer<br/>queries, commands, DTOs, services]
    Domain[Domain layer<br/>policies, value objects, state rules]
    Ports[Repository and service contracts]
    Infra[Infrastructure adapters<br/>MySQL, Redis, queues, external billing]
    Data[(Existing and future stores)]

    Browser --> Presentation
    Presentation --> HTTP
    HTTP --> App
    App --> Domain
    App --> Ports
    Infra -. implements .-> Ports
    Infra --> Data

    classDef stable fill:#e8f2ff,stroke:#315d8a,color:#172b3a
    class Domain,Ports stable
```

Rules:

- Pages and UI components render state and collect intent; they do not own business policy.
- Controllers or component action methods are thin adapters.
- Application commands own mutations and transaction boundaries.
- Application queries return purpose-built, immutable read DTOs.
- Domain policies own lifecycle and authorization rules.
- Infrastructure implements persistence, caching, queueing, and third-party integration contracts.
- Eloquent models are never passed directly to browser components as an authorization mechanism.
- Dashboard code may invoke the same application authorities as API code, but may not call API controllers internally.

### 3.4 Command/query separation

Commands represent explicit intent such as `CreateTenant`, `InviteTenantMember`, `CreateApplication`, or `RevokeApiCredential`. Queries represent safe read models such as `GetTenantDashboardSummary`.

This is a pragmatic separation, not a requirement for separate databases or an event-sourcing framework. It prevents large Eloquent graphs and mutation logic from accumulating in UI code.

### 3.5 Tenant isolation is structural

Every tenant-owned query or command input includes an immutable `TenantIdentity`. Repositories require it, SQL includes it, authorization evaluates it, cache keys include it, exports bind it, and audit events record it.

```mermaid
sequenceDiagram
    actor U as Authenticated user
    participant M as Workspace middleware
    participant A as Authorization service
    participant Q as Application query
    participant R as Tenant-scoped repository
    participant DB as MySQL

    U->>M: Request /t/{tenant_public_id}/messages
    M->>A: Resolve membership(user, tenant_public_id)
    A-->>M: Active membership + permissions
    M->>Q: Query(TenantIdentity, UserIdentity, filters)
    Q->>A: Require messages.view
    Q->>R: list(TenantIdentity, filters)
    R->>DB: WHERE tenant_id = ? ...
    DB-->>R: Tenant-owned rows only
    R-->>Q: Safe read DTOs
    Q-->>U: Redacted page model
```

### 3.6 Consistency model

| Information | Required consistency | Strategy |
|---|---|---|
| Tenant status and access | Strong | Authoritative database read; short request-local reuse only |
| Membership and permissions | Strong | Authoritative database read; invalidate any cache on change |
| Credential creation/revocation | Strong | Transactional command; secret shown once |
| Wallet balance and ledger | Strong | Existing accounting authority; never reconstruct in UI |
| Message list/detail | Read-after-write where practical | Existing authoritative tables |
| Dashboard totals and charts | Eventual, visibly timestamped | Read model/cache with freshness marker |
| Platform health indicators | Eventual | Dedicated operational metrics source |
| API usage analytics | Eventual, freshness-labeled | Authoritative observability or usage-metering source; never reconstructed from message rows |
| Sender authorization | Strong in data plane | Approved sender policy and routing authority; dashboard is configuration/observation only |

## 4. Overall system architecture

### 4.1 Logical view

```mermaid
flowchart LR
    subgraph Clients
        TB[Tenant browser]
        PB[Platform operator browser]
        API[API client]
    end

    subgraph WebControlPlane[Dashboard control plane]
        Auth[Existing web authentication]
        Shell[Workspace shell]
        TC[Tenant context]
        PA[Permission authorization]
        DQ[Dashboard queries]
        DC[Dashboard commands]
    end

    subgraph Core[Existing application and domain]
        Tenancy[Tenancy]
        Messaging[Messaging]
        Credentials[Applications and credentials]
        Webhooks[Webhooks]
        Audit[Audit]
        Wallet[Wallet]
    end

    subgraph Async[Asynchronous infrastructure]
        Queue[Queues]
        Projections[Future dashboard projections]
        Export[Future export jobs]
    end

    DB[(MySQL)]
    Redis[(Redis)]
    Provider[Messaging providers]

    TB --> Auth
    PB --> Auth
    Auth --> Shell
    Shell --> TC
    TC --> PA
    PA --> DQ
    PA --> DC
    DQ --> Tenancy & Messaging & Credentials & Webhooks & Audit & Wallet
    DC --> Tenancy & Credentials & Webhooks & Audit & Wallet
    DQ --> Redis
    DC --> Queue
    Queue --> Projections & Export
    Projections --> DB
    Tenancy & Messaging & Credentials & Webhooks & Audit & Wallet --> DB
    API --> Messaging
    Messaging --> Provider
```

### 4.2 Deployment view

The first implementation should remain a modular Laravel monolith:

- same application deployment and database;
- horizontally scalable stateless web nodes;
- shared Redis for cache, session infrastructure where configured, and queues;
- queue workers for expensive reports/exports and later projection refreshes;
- existing independent SMPP runtime and provider connections;
- object storage for future exports, using short-lived authorized downloads.

No microservice split is justified for the dashboard. Module contracts should make a later extraction possible if scale or compliance requires it.

### 4.3 Browser URL model

This document does not create routes. A later routing milestone should adopt stable, context-explicit URL shapes:

```text
/select-workspace
/t/{tenant_public_id}/...
/platform/...
```

Public tenant ULIDs are preferred to slugs for authorization-bearing URLs. Slugs may be display aliases but must not be the sole identity authority. The current tenant must be derived from the path and authorized membership, not from a mutable session value or client-supplied header alone.

## 5. Module decomposition

### 5.1 Shared shell

Responsibilities:

- authenticated layout;
- tenant/platform workspace switcher;
- responsive navigation;
- breadcrumbs and page titles;
- global flash/error presentation;
- permission-aware navigation;
- freshness and degraded-state banners;
- user profile, accessibility preferences, and sign-out access.

The shell may hide unavailable actions for clarity, but server-side authorization remains mandatory.

### 5.2 Identity and workspace selection

Responsibilities:

- list active memberships for the signed-in user;
- select a tenant workspace;
- enter the platform workspace for a platform administrator;
- handle no-membership, inactive-membership, and suspended-tenant states;
- preserve a safe return path after authentication.

It does not authenticate API credentials and does not trust a tenant identifier merely because it appears in the URL.

### 5.3 Tenant administration

Responsibilities:

- view tenant identity and lifecycle state;
- edit approved profile fields;
- manage members and role assignments;
- manage future invitations;
- expose audit history;
- initiate tightly controlled lifecycle requests.

### 5.4 Integration management

Responsibilities:

- list and manage applications;
- issue, inventory, and revoke API credentials;
- display API environment and safe credential metadata;
- configure the tenant delivery webhook;
- show webhook delivery health;
- link to API integration documentation.

API secrets are write-only and shown once. The dashboard never retrieves an existing plaintext secret.

### 5.5 Messaging operations

Responsibilities:

- safe overview metrics;
- message list with bounded filters and cursor pagination;
- message detail using public/sanitized fields;
- lifecycle timeline derived from approved events and statuses;
- application and channel segmentation;
- future asynchronous CSV export.

Message content visibility is a separate permission from message metadata visibility. The initial dashboard should not expose decrypted bodies.

Delivery state remains part of the authoritative message list, message detail, and lifecycle projection. A separate Delivery Reports page or competing delivery-state model is not proposed.

### 5.6 Sender ID management

Responsibilities:

- tenant Sender ID inventory;
- request state: requested, pending review, approved, rejected, suspended, or disabled;
- default Sender ID selection among senders valid for the required scope;
- application-to-Sender-ID assignment;
- provider compatibility and country/route restrictions;
- validity periods where applicable;
- approval evidence and append-only audit history;
- platform review workflows in a later independently approved milestone.

Approval is never performed by ordinary tenant users. Approval is scoped: a Sender ID may be approved for one provider, route, country, or application and unavailable for another. A display badge must not imply provider availability unless an authoritative provider/routing source confirms it. The messaging data plane remains the sender-authorization authority; dashboard configuration cannot bypass public API validation, entitlements, provider capability, or routing policy.

### 5.7 Developer Center

Responsibilities:

- Quick Start: shortest safe path from application and one-time credential to a first request;
- API Reference: human-readable, version-selected views generated from or linked to authoritative OpenAPI;
- OpenAPI / Swagger UI: interactive contract exploration with safe authentication handling;
- Postman Collection: versioned downloadable repository artifact;
- SDK and Code Examples: maintained, versioned examples that use placeholders and environment-aware base URLs;
- Webhook Guide: signature verification, secret rotation overlap, replay tolerance, and safe receiver examples;
- Error Codes: public error vocabulary and troubleshooting actions;
- Changelog: public API changes, compatibility notes, and release dates;
- API Status: safe public/tenant view of API component status from authoritative telemetry;
- Go-Live Checklist: credential storage, sender approval, webhook verification, limits, and operational readiness.

OpenAPI remains authoritative for the public API. The Developer Center renders, links, or packages approved artifacts; it does not duplicate or redefine the contract. Documentation pages may be public when they contain no tenant data, while contextual quick starts, application selections, usage, and go-live state require authentication and tenant permission. Release ownership, supported API versions, generated-artifact checks, and freshness dates must be explicit.

### 5.8 API operational analytics

Responsibilities:

- request volume and requests per minute;
- accepted versus rejected requests;
- HTTP 2xx, 4xx, and 5xx classes;
- 401, 403, 409, 422, and 429 outcomes;
- average and percentile latency;
- peak throughput;
- safe breakdowns by application, API version, and bounded endpoint group.

Metrics come from an authoritative observability or usage-metering source and show freshness. They must not be inferred from message rows because requests, authentication failures, validation failures, replays, and non-message endpoints do not map one-to-one to messages. Labels use bounded route/endpoint groups and must never include recipients, credentials, request bodies, tenant secrets, raw URLs, or high-cardinality identifiers.

### 5.9 Finance

Responsibilities:

- future wallet balance and immutable transaction history;
- future usage charges, invoices, payment methods, top-ups, and statements;
- display all money using currency-aware integer minor units;
- reconcile display data to the existing accounting authority.

Finance is a bounded module. It must not place price calculation inside message screens or UI components.

### 5.10 Plans and limits

Responsibilities:

- future plan catalog;
- tenant subscription and entitlement projection;
- rate-limit and quota display;
- usage versus allowance;
- controlled plan changes.

Entitlements answer “may this tenant use capability X?” Rates answer “how much does usage cost?” Limits answer “how much/how fast?” These are separate concepts and stores.

### 5.11 Platform operations

Responsibilities:

- tenant inventory and lifecycle actions;
- cross-tenant operational summaries;
- tenant support context with reason and audit;
- user and membership support;
- system health summaries;
- independently reported Public API, Database, Redis, Queues, SMPP Runtime, Messaging Providers, Webhook Delivery, and scheduled/projection-worker health;
- future plan, pricing, wallet adjustment, and limit administration.

Platform queries are dedicated cross-tenant use cases, never calls to tenant repositories with a missing tenant condition.

### 5.12 Reporting and export

Responsibilities:

- bounded aggregate queries;
- eventual time-series projections;
- asynchronous large exports;
- export status and expiration;
- permission checks both when generating and downloading.

Exports are immutable snapshots labeled with tenant, requester, filters, generation time, and expiry.

### 5.13 Notifications

Responsibilities:

- future in-dashboard notices;
- onboarding prompts;
- credential expiry warnings;
- webhook failure warnings;
- wallet/plan/limit warnings.

Notification delivery preferences must remain separate from operational events and audit evidence.

## 6. Navigation hierarchy

### 6.1 Tenant workspace

```text
Tenant workspace
|-- Overview
|-- Messaging
|   |-- Messages
|   |-- Sender IDs
|   |-- Analytics                         [future]
|   `-- Exports                           [future]
|-- Integrations
|   |-- Applications
|   |   `-- Application detail
|   |       `-- API credentials
|   `-- Delivery webhook
|-- Developer Center
|   |-- Quick Start
|   |-- API Reference
|   |-- OpenAPI / Swagger UI
|   |-- Postman Collection
|   |-- SDK and Code Examples
|   |-- Webhook Guide
|   |-- Error Codes
|   |-- Changelog
|   |-- API Status
|   `-- Go-Live Checklist
|-- Finance                               [future, entitlement-controlled]
|   |-- Wallet
|   |-- Transactions
|   |-- Usage and charges
|   `-- Invoices
|-- Plan and usage                        [future]
|   |-- Subscription
|   |-- Limits
|   `-- Usage
`-- Settings
    |-- Organization
    |-- Team
    |-- Audit log
    `-- Security
```

Primary navigation should contain no more than seven top-level items. Future entries stay absent until implemented and entitled; disabled “coming soon” navigation should not clutter normal operation.

### 6.2 Platform workspace

```text
Platform workspace
|-- Overview
|-- Tenants
|   `-- Tenant detail
|       |-- Summary
|       |-- Memberships
|       |-- Applications
|       |-- Sender IDs
|       |-- Messaging health
|       |-- Finance                       [future]
|       |-- Subscription and limits       [future]
|       `-- Audit
|-- Users
|-- Operations
|   |-- Component health
|   |   |-- Public API
|   |   |-- Database
|   |   |-- Redis
|   |   |-- Queues
|   |   |-- SMPP Runtime
|   |   |-- Messaging Providers
|   |   |-- Webhook Delivery
|   |   `-- Scheduled / projection workers
|   |-- API analytics
|   `-- Troubleshooting search
|-- Sender ID review                      [future]
|-- Commercial                            [future]
|   |-- Plans
|   |-- Pricing
|   |-- Subscriptions
|   `-- Wallet operations
`-- Platform audit
```

### 6.3 Navigation behavior

- Desktop: persistent left sidebar, top utility bar, and breadcrumb.
- Tablet: collapsible sidebar with preserved labels.
- Mobile: drawer navigation; page actions move below the title or into an accessible menu.
- Current workspace and tenant name are always visible.
- Platform support context uses a persistent warning-colored banner naming the tenant and recording that the user is acting with platform privilege.
- Navigation is permission-filtered, but direct URL access is independently authorized.
- A tenant switch clears tenant-specific filters, cached page state, and sensitive modal state.

## 7. Roles and permissions

### 7.1 Role model

The repository currently defines:

- platform role: `platform_admin`;
- tenant roles: `tenant_admin`, `messaging`, `finance`, `support`, `auditor`, and `read_only`.

Roles are named bundles of permissions, not authorization checks. Application services authorize permission values. Future custom roles can therefore be added without rewriting each screen.

### 7.2 Proposed permission catalog

Existing permissions retain their meaning. New values below are proposals for later implementation.

| Domain | Permissions |
|---|---|
| Tenant | `tenant.view`, `tenant.update`, `tenant.lifecycle.request` |
| Memberships | `memberships.view`, `memberships.manage`, `invitations.manage` |
| Applications | `applications.view`, `applications.manage` |
| Credentials | `credentials.view`, `credentials.create`, `credentials.revoke` |
| Messages | `messages.view`, `messages.content.view`, `messages.export` |
| Sender IDs | `sender_ids.view`, `sender_ids.request`, `sender_ids.assign`, `sender_ids.default.manage`, `sender_ids.review`, `sender_ids.suspend` |
| Webhooks | `webhooks.view`, `webhooks.manage`, `webhooks.deliveries.view` |
| Developer Center | `developer_center.view`, `developer_artifacts.download`, `api_usage.view` |
| Finance | `wallet.view`, `wallet.transactions.view`, `billing.view`, `invoices.view`, `payments.manage` |
| Plans and limits | `subscription.view`, `subscription.manage`, `limits.view` |
| Audit | `audit.view`, `audit.export` |
| Platform | `platform.dashboard.view`, `platform.tenants.view`, `platform.tenants.manage`, `platform.support_context.enter`, `platform.operations.view`, `platform.commercial.manage`, `platform.audit.view` |

`sender_ids.review` and `sender_ids.suspend` are platform-only review authorities and are never included in ordinary tenant roles. `messages.content.view`, `payments.manage`, platform tenant lifecycle actions, wallet adjustments, credential creation/revocation, role changes, and Sender ID approval changes are sensitive permissions and should require elevated controls.

### 7.3 Tenant role matrix

Legend: **M** manage, **V** view, **—** unavailable. This is the target default mapping and requires approval before implementation.

| Capability | Tenant admin | Messaging | Finance | Support | Auditor | Read only |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| Organization profile | M | V | V | V | V | V |
| Team and roles | M | — | — | V | V | — |
| Applications | M | M | V | V | V | V |
| API credentials | M | M | — | V metadata | V metadata | — |
| Messages and status | V | V | V summary | V | V | V |
| Sender ID inventory | M request/assign | M request/assign | V | V | V | V |
| Sender ID approval/review | — | — | — | — | — | — |
| Message content | Optional grant | Optional grant | — | — | — | — |
| Delivery webhook | M | M | — | V | V | V |
| Developer Center | V | V | V docs | V | V | V |
| API usage analytics | V | V | V summary | V | V | V |
| Wallet and transactions | V | V balance | V | V balance | V | V balance |
| Billing and invoices | V | — | V | — | V | — |
| Subscription and limits | M | V | V | V | V | V |
| Audit log | V | — | V finance | V support | V | — |
| Data export | V | V messages | V finance | — | V approved | — |

Notes:

- The existing authorization service currently grants `ViewCredentials` to messaging, finance, and support roles. A later authorization milestone must reconcile that current behavior with the more restrictive target matrix; 6A does not change it.
- “V metadata” means name, prefix/hint, state, expiry, and last-used time only. No secret is viewable.
- “Optional grant” should be off by default and may require a future custom-role or explicit grant model.
- Tenant “manage” access to Sender IDs means request, choose a valid default, and assign within authorized scope. It never means approve, widen provider/country/route scope, or override data-plane policy.

### 7.4 Platform permissions

The existing `platform_admin` remains the only platform role initially. Internally, platform features should still authorize fine-grained platform permissions so a later `platform_support`, `platform_finance`, `platform_operations`, or `platform_auditor` role can be introduced safely.

### 7.5 Authorization invariants

1. Authentication never implies tenant authorization.
2. Active user membership, active membership status, and requested tenant must all match.
3. Suspended tenants may receive limited read-only access if policy approves it; API and mutation behavior follow the authoritative tenant status policy.
4. Closed tenants expose no normal workspace and have a separately approved retention/export process.
5. Platform support context requires an explicit tenant, reason, time, and audit record.
6. A disabled control is not an authorization control.
7. List counts, search suggestions, errors, and timing must not reveal cross-tenant existence.
8. Mutations re-authorize at execution time; permission checked when a form was opened may have changed.
9. Background jobs serialize tenant identity and re-establish authorization-relevant scope; they do not rely on request globals.

## 8. Tenant lifecycle

### 8.1 Existing state model

The repository defines `trial`, `active`, `suspended`, and `closed`. Trial and active currently permit public API access; suspended and closed do not.

```mermaid
stateDiagram-v2
    [*] --> Trial: tenant created
    Trial --> Active: activation criteria met
    Trial --> Suspended: risk / trial policy
    Active --> Suspended: policy or operator action
    Suspended --> Active: issue resolved
    Trial --> Closed: cancellation
    Active --> Closed: approved closure
    Suspended --> Closed: approved closure
    Closed --> [*]
```

### 8.2 State semantics

| State | Workspace | Public API | Mutations | Intended UX |
|---|---|---|---|---|
| Trial | Full onboarding workspace | Permitted by current domain policy | Allowed within trial entitlements | Trial badge, setup checklist, limits |
| Active | Full workspace | Permitted | Allowed by role/entitlements | Normal operating state |
| Suspended | Restricted support/read view | Denied by current domain policy | Blocked except approved remediation | Persistent reason-neutral suspension banner and support path |
| Closed | No normal workspace | Denied | Blocked | Closure confirmation and approved data-access path only |

The dashboard must not display internal fraud, compliance, or security notes to tenant users. User-facing reason categories are separately curated.

### 8.3 Transition controls

- Lifecycle transitions occur in application services, not direct model updates.
- Every transition validates current state, desired state, actor permission, reason category, and optional evidence.
- Sensitive transitions require recent authentication confirmation once an implementation milestone is approved.
- Suspension and closure invalidate active tenant sessions or force authorization re-evaluation.
- API credentials need not be physically revoked on suspension; access fails through tenant status. Closure policy may revoke credentials in a separately designed, idempotent workflow.
- Transitions append audit evidence.
- Closure is not deletion. Data retention and legal deletion are separate workflows.
- Reopening a closed tenant is prohibited unless a future state model and compliance policy explicitly allow it.

### 8.4 Lifecycle ownership

| Transition | Tenant admin | Platform admin | Automated policy |
|---|:---:|:---:|:---:|
| Trial → Active | Request/complete criteria | Approve or automatic | Possible |
| Trial/Active → Suspended | No | Yes | Possible with auditable rule |
| Suspended → Active | Request | Yes | Possible after condition clears |
| Trial/Active/Suspended → Closed | Request | Approve | No implicit closure |

## 9. Tenant onboarding workflow

### 9.1 Principles

- Onboarding is resumable and idempotent.
- The first useful API request is the success outcome.
- Required steps are distinguished from recommended steps.
- A credential secret is displayed exactly once and is never logged or recoverable.
- Trial and commercial acceptance are separate from technical setup.
- Onboarding status must be derived from authoritative facts where possible, not duplicated checkboxes.

### 9.2 Workflow

```mermaid
flowchart TD
    Start[User signs in] --> Choice{Eligible workspace?}
    Choice -->|Existing tenant| Select[Select tenant]
    Choice -->|No tenant and creation allowed| Create[Create organization]
    Choice -->|Invitation| Accept[Accept invitation]
    Create --> Membership[Create tenant admin membership]
    Accept --> MembershipReady[Activate membership]
    Membership --> Profile[Confirm organization profile]
    MembershipReady --> Profile
    Select --> Checklist[Open onboarding checklist]
    Profile --> App[Create first application]
    App --> Key[Issue API credential]
    Key --> Launch[Open Integration Launch page]
    Launch --> Secret[Show credential secret once + integration resources]
    Secret --> Request[Make first API request]
    Request --> Verify{Accepted by authoritative API evidence?}
    Verify -->|No / not yet| Incomplete[Technical onboarding remains incomplete]
    Verify -->|Yes| Complete[Technical onboarding complete]
    Complete --> Webhook[Configure delivery webhook - recommended]
    Webhook --> Team[Invite team - recommended]
    Team --> Commercial[Review plan/limits - future]
    Commercial --> Done[Operational]
```

### 9.3 Step contract

| Step | Required | Completion authority | Failure/recovery |
|---|---:|---|---|
| Organization created | Yes for self-service | Tenant row + admin membership transaction | Retry with idempotency; no orphan tenant |
| Profile confirmed | Yes | Approved tenant profile fields | Preserve entered non-sensitive values |
| First application | Yes | Active application exists | Name/code conflict shown safely |
| First credential | Yes | Active credential exists | New secret can be issued; never reveal old secret |
| Integration Launch page | Required workflow handoff; not a completion criterion | No completion authority; the page presents the original one-time secret response and approved integration resources | Revisit without the old secret; revoke and issue a new credential if necessary |
| First API request | Yes | Request is submitted through the authoritative public API | Keep the launch guidance available and show safe troubleshooting |
| Verify first successful request | Yes; sole technical completion criterion | Authoritative evidence that the public API accepted the first request | Pending or unavailable evidence must not mark onboarding complete |
| Technical onboarding complete | Derived outcome | Successful first-request verification only | Recommended setup remains visible but does not reverse technical completion |
| Delivery webhook | Recommended | Valid configuration exists | Permit skip; test behavior requires separate design |
| Team invitation | Recommended | Future invitation/membership authority | Permit skip; expired invitation can be reissued |
| Plan selection | Future/policy | Subscription authority | Trial remains explicit |

### 9.4 Invitation workflow

Future invitation lifecycle:

```mermaid
stateDiagram-v2
    [*] --> Pending
    Pending --> Accepted
    Pending --> Expired
    Pending --> Revoked
    Expired --> Pending: reissue as new invitation
    Accepted --> [*]
    Revoked --> [*]
```

Invitation tokens must be random, single-use, hashed at rest, short-lived under an approved policy, and bound to tenant, email, role, and inviter. Existing users authenticate before acceptance; new users follow the unchanged authentication registration policy when available.

### 9.5 Empty and interrupted states

- No memberships: explain that the user needs an invitation or tenant-creation permission.
- Inactive membership: show a neutral access-unavailable message without tenant data.
- Onboarding interrupted: return to the first incomplete required step.
- Credential secret modal closed early: secret is not recoverable; offer revocation and new issuance.
- Application created but credential absent: direct the user to create the credential before Integration Launch.
- Integration Launch revisited after credential reveal: show credential metadata and all documentation links, but never the previous secret. Offer revocation and issuance of a new credential.
- Integration Launch opened but no request submitted: retain safe examples and mark technical onboarding “Waiting for first API request.”
- First request rejected: retain the launch resources, show sanitized public API troubleshooting, and keep technical onboarding incomplete.
- First-request verification unavailable: label verification “Unavailable,” retain the safe request example, and do not mark technical onboarding complete.
- A dashboard test-message action, if later approved, must invoke the same public API authority or a separately approved application command with identical validation, sender authorization, tenancy, limits, billing, and routing. It must never write directly to message or SMPP stores.

### 9.6 Integration Launch contract

Integration Launch is the required handoff page between credential creation and the first API request. It is not a completion authority. The page consolidates:

- organization and first application confirmation;
- newly issued one-time credential secret during the original creation response only;
- environment-aware API base URL;
- OpenAPI/API Reference, Postman collection, SDK/code examples, webhook signing guide, and error-code links;
- a generated public API example containing placeholders rather than embedded persisted secrets;
- first-request submission guidance and verification status from authoritative API evidence;
- next steps covering Sender ID readiness, webhook setup, limits, credential storage, monitoring, and go-live.

The sequence is credential created → Integration Launch page → first API request → verify accepted request → technical onboarding complete. Only authoritative evidence that the public API accepted the first request may produce technical completion. Page access, credential creation, viewing/copying examples, or an attempted/rejected request cannot do so.

The secret must not enter URLs, logs, analytics, browser storage, screenshots generated by the product, error reporting, or later responses. Closing the page makes it unrecoverable. Webhook-secret behavior remains governed by the existing write-only replacement and overlap contract.

## 10. Dashboard information architecture

### 10.1 Page anatomy

Every page uses this hierarchy:

1. Skip link and landmark structure.
2. Workspace/tenant identity.
3. Breadcrumb.
4. Page title, concise purpose, status, and primary action.
5. Alerts requiring action.
6. Summary or task content.
7. Supporting detail.
8. Freshness timestamp where data is aggregated.

### 10.2 Tenant overview

The overview answers:

- Is my integration operational?
- Are messages succeeding?
- Does anything require action?
- What is my current usage/financial position?
- What should I do next?

Recommended content order:

1. Critical banners: suspension, credential expiry, webhook failures, low balance (future).
2. Onboarding checklist until authoritative evidence confirms the first API request was accepted and technical onboarding is complete.
3. Key metrics: submitted where authoritative, accepted, delivered, failed, pending, and delivery rate for the selected approved time range.
4. Message trend with accessible tabular alternative.
5. Integration health: applications, active credentials, credential last use, webhook state.
6. Sender ID readiness: approved/default/expiring or restricted scope, based only on authoritative approval data.
7. API operations: request volume, error classes, throttling, latency, and freshness from authoritative telemetry.
8. Recent message activity.
9. Wallet/plan summary when enabled.

Metric definitions are centralized by Architecture Addendum 6A-1 below.

#### 10.2.1 Architecture Addendum 6A-1 — Overview dashboard metrics contract

This contract applies only to the tenant Overview dashboard. It does not define a general reporting system.

##### Supported time ranges

The only supported Overview ranges are:

| Range | Duration | Default |
|---|---:|:---:|
| Last 24 Hours | 24 hours | Yes |
| Last 7 Days | 7 days | No |
| Last 30 Days | 30 days | No |

Custom ranges are not supported on Overview. A future reporting module may introduce them independently.

Every calculation uses one UTC `to` instant and derives `from` by subtracting the selected duration. Intervals are half-open: `[from, to)`. A timestamp exactly equal to `from` is included; one exactly equal to `to` is excluded. This prevents double-counting across adjacent intervals.

For example, a one-day interval includes timestamps satisfying `2026-07-01T00:00:00Z <= timestamp < 2026-07-02T00:00:00Z`.

##### Message outcome definitions

| Metric | Overview definition |
|---|---|
| Submitted | Display only when an authoritative submitted-message source exists |
| Accepted | The SMS Gateway successfully accepted the message for processing; this is gateway acceptance, not carrier acceptance |
| Pending | Accepted by the gateway but not yet at a terminal outcome |
| Delivered | A terminal carrier delivery receipt indicates successful delivery |
| Failed | Every terminal outcome other than Delivered, including Rejected, Expired, Undeliverable, Cancelled, and Failed |
| Unknown | Excluded from Overview metrics; reserved for future diagnostic or reporting modules |

Future terminal outcomes belong to Failed unless a later architecture revision explicitly defines otherwise.

##### Delivery rate

Delivery Rate is:

```text
Delivered
------------------
Delivered + Failed
```

Pending and Unknown are excluded from both numerator and denominator. If `Delivered + Failed` is zero, the rate has no denominator and must display an honest empty/not-applicable state rather than a fabricated percentage.

##### Required Overview presentation

Top summary:

- Submitted, when authoritative data exists;
- Accepted;
- Pending;
- Delivered;
- Failed.

Operational metrics:

- Delivery Rate;
- API Requests;
- Active Applications;
- Sender IDs.

Additional widgets:

- Delivery Trend;
- Recent Activity.

Every value must come from an authoritative source. A metric without such a source displays `Unavailable` or `Not Yet Configured`, whichever accurately describes the condition. Placeholder production values are prohibited, and missing data must never be represented as zero.

### 10.3 Platform overview

The platform overview answers:

- Are core service paths healthy?
- Which tenants require intervention?
- Is throughput or failure behavior abnormal?
- Are queues, providers, and webhooks degraded?
- What is API volume, latency, rejection, authentication, conflict, validation, and throttling behavior?
- Are lifecycle or commercial actions pending?

Cross-tenant values must be aggregates or dedicated platform DTOs. Tenant drill-down requires platform authorization and support-context audit where tenant data is exposed.

### 10.4 API operational metrics

The tenant and platform analytics models may report:

| Metric | Definition requirement |
|---|---|
| Request volume / requests per minute | Count gateway HTTP requests from authoritative telemetry within an explicit interval |
| Accepted versus rejected | Use an approved API outcome classifier; do not infer from message creation |
| HTTP 2xx / 4xx / 5xx | Response class counts, with safe endpoint grouping |
| 401 / 403 | Authentication and authorization failures; tenant attribution only when trustworthy and safe |
| 409 | Approved idempotency or state conflicts |
| 422 | Validation failures without submitted values |
| 429 | Throttled requests and policy-safe retry context |
| Average / percentile latency | At least average and approved percentiles, with sampling/source declared |
| Peak throughput | Highest bounded requests-per-time-bucket value |
| Usage breakdown | Application, API version, or allowlisted endpoint group where attribution is authoritative |

Every view shows source freshness and interval. Data unavailable is “Unavailable,” not zero. Metric dimensions must be bounded route names or endpoint groups—never recipients, credential values/prefixes, request bodies, raw URLs, tenant secrets, arbitrary correlation IDs, or public message IDs.

### 10.5 Platform component health

Health is independently reported; no overall “Healthy” claim is allowed when required component telemetry is missing.

| Component | Measurement | Authoritative source | Visibility / alert owner |
|---|---|---|---|
| Public API | availability, error rate, latency, traffic | API telemetry/health probes | Tenant-safe summary; platform drill-down / API operations |
| Database | connectivity, query health, replication/capacity signals where supported | database monitoring, not an application write probe | Platform only / database operations |
| Redis | connectivity, latency, memory/eviction signals where supported | Redis monitoring | Platform only / infrastructure |
| Queues | backlog age/depth, failure rate, worker heartbeat | queue metrics and failed-job authority | Platform only / application operations |
| SMPP Runtime | lease/runtime heartbeat, bind state, safe submission outcome rate | runtime state/metrics authority | Tenant sees messaging availability only; platform drill-down / messaging operations |
| Messaging Providers | provider-scoped availability, latency, rejection/failure trends | provider adapter/runtime metrics | Tenant-safe availability where appropriate; platform / provider operations |
| Webhook Delivery | attempt outcome/rate and backlog for future retries | webhook event/attempt metrics | Tenant sees own webhook health; platform aggregate / application operations |
| Scheduled/projection workers | heartbeat, lag, last success, backlog | scheduler/worker metrics | Platform; tenant sees projection freshness only / application operations |

Each component reports:

- **Healthy:** telemetry is fresh and all approved service-level thresholds pass.
- **Degraded:** telemetry is fresh and one or more approved thresholds fail while some service remains.
- **Unavailable:** authoritative evidence confirms the component cannot provide its service.
- **Unknown:** telemetry is absent, stale, contradictory, or insufficient.

Every status includes an `observed_at`/freshness timestamp. Drill-down exposes only allowlisted diagnostics and runbook links. It never exposes credentials, raw SMPP PDUs, stack traces, secret hosts, internal connection strings, or sensitive infrastructure topology.

### 10.6 Search, filters, and pagination

- Message list uses bounded, allowlisted filters aligned with existing safe API fields.
- Default time windows protect database performance.
- Cursor pagination is preferred for mutable, high-volume message data.
- Tenant/application lists may use standard pagination.
- Filter state is encoded in the URL for shareable, reversible navigation.
- Secrets, full recipients, bodies, ciphertext, hashes, or internal IDs are never searchable.
- Search suggestions must be tenant scoped.
- Export uses the same validated filter DTO as the on-screen query.

Audit and troubleshooting search is federated through bounded, purpose-specific queries rather than one unrestricted raw-table search:

| Evidence family | Allowlisted keys |
|---|---|
| Audit evidence | correlation ID, actor, action, outcome, application public ID where recorded, date range; tenant public ID in platform context |
| Message lifecycle evidence | public message ID, correlation ID, application public ID, date range |
| Webhook delivery evidence | webhook event public ID, public message ID, outcome, date range |
| Provider operational evidence | safe provider code/category, bounded time range, sanitized outcome; platform only |

These evidence families remain distinct and link through safe public identities/correlation lineage only where authoritative. Queries accept public IDs, enforce tenant context, use bounded time ranges and stable pagination, apply permission-specific redaction, and return neutral not-found results. Export retains the same family, scope, filters, redaction, requester, and download re-authorization; it never becomes a cross-table data dump.

### 10.7 Status language

Use plain, consistent labels:

- Tenant: Trial, Active, Suspended, Closed.
- Application: Active, Disabled.
- Credential: Active, Expiring, Expired, Revoked.
- Public message: Queued, Submitted, Delivered, Failed, Expired, Rejected, Unknown, as defined by the public status authority.
- Webhook attempt: In progress, Delivered, Failed.
- Sender ID: Requested, Pending review, Approved, Rejected, Suspended, Disabled; scope/availability shown separately.
- Component health: Healthy, Degraded, Unavailable, Unknown.

Color is supplementary. Every status has text and, where useful, an icon.

### 10.8 Data presentation rules

- Dates display in the user's selected timezone, with UTC available and included in exports.
- Currency always includes ISO 4217 code; formatting converts integer minor units only at presentation.
- Recipient values are masked by default.
- Message bodies are excluded from the initial dashboard.
- Credential displays use name, key prefix/hint, created, expires, last used, and state.
- Public ULIDs are copyable where useful; internal numeric IDs are not displayed.
- Empty states explain why data is absent and provide a permitted next action.
- Partial/degraded data is labeled; it must not silently appear as zero.

### 10.9 Responsive and accessible UX

Target WCAG 2.2 AA.

- All functionality works with keyboard alone.
- Focus order follows visual order; focus is restored after dialogs.
- Dialogs have programmatic names, trapped focus, Escape behavior where safe, and explicit destructive confirmation.
- Charts have text summaries and tables.
- Minimum interactive target size follows accessibility guidance.
- Validation errors are summarized and associated with inputs.
- Live status messages use appropriate ARIA live regions without excessive announcements.
- Reduced-motion preferences are respected.
- Tables collapse to labeled cards or horizontal scroll without hiding essential columns.
- Destructive actions never depend on swipe or icon-only controls.

## 10A. Feature flags and entitlements architecture

These mechanisms are intentionally distinct:

| Mechanism | Purpose | Typical scope | Authority |
|---|---|---|---|
| Deployment feature flag | Protect incomplete or environment-dependent code deployment | Environment/build | Engineering release configuration |
| Release rollout flag | Gradually expose an implemented capability | Cohort/tenant/percentage for a bounded release | Release owner |
| Tenant entitlement | State that a tenant may use a product capability | Tenant/application | Entitlement policy |
| Subscription-plan capability | Default entitlement supplied by a versioned plan | Plan version | Subscription/plan authority |
| Operational kill switch | Stop a risky/degraded operation safely | Global/provider/channel/tenant where designed | Named operations/security owner |
| User preference | Change presentation without granting capability | User | User preference authority |

Principles:

1. Flags are not authorization and never replace permission checks.
2. Flags and entitlements never replace structural tenant isolation.
3. UI visibility and server-side capability enforcement consume the same resolved state/version; hidden UI alone is insufficient.
4. Kill switches fail safely, have explicit fallback behavior, and must not silently re-enable because a flag service/cache is unavailable.
5. Sensitive flag, entitlement, and kill-switch changes are audited with actor, reason, previous/new value, scope, and expiry.
6. Temporary rollout flags have an owner, creation date, expected removal date, and cleanup task. Permanent business policy must move to typed entitlements or domain rules.
7. Cache keys include the tenant, relevant permission state, and flag/entitlement version where cached output can differ.
8. Subscription capabilities provide defaults; explicit safety restrictions and kill switches may still deny operation.
9. User preferences cannot enable an unauthorized, unentitled, or disabled capability.

Future storage options:

- deployment flags in validated configuration for environment-wide boot/deploy behavior;
- release-rollout records in a typed flag store with scope, variants, owner, reason, start/expiry, and version;
- tenant entitlements and plan capabilities in the versioned subscription models proposed later;
- operational kill switches in a strongly controlled, highly available policy store with safe local defaults;
- user preferences in an explicitly modeled user-owned store.

A later implementation must test flag-service failure, stale caches, rollout inclusion/exclusion, tenant isolation, permission/entitlement disagreement, kill-switch behavior, audit evidence, and cleanup of expired flags.

## 11. Screen-by-screen wireframes

Wireframes specify information and interaction priority, not visual styling.

### 11.1 Workspace selection

```text
┌──────────────────────────────────────────────────────────────────┐
│ MHI Gateway                                      User menu       │
├──────────────────────────────────────────────────────────────────┤
│ Select a workspace                                               │
│ Choose the organization you want to manage.                      │
│                                                                  │
│ Search organizations [____________________]                      │
│                                                                  │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Acme Health                         ACTIVE    Tenant admin   │ │
│ │ Last opened 25 Jul 2026                         [Open →]     │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Community Clinic                    TRIAL     Messaging      │ │
│ │ Setup: 3 of 5 required steps                     [Open →]    │ │
│ └──────────────────────────────────────────────────────────────┘ │
│                                                                  │
│ Platform administration                           [Open →]       │
└──────────────────────────────────────────────────────────────────┘
```

States: loading, no memberships, inactive membership only, one membership (may redirect if policy allows), platform access, and access error.

### 11.2 Tenant overview

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Acme Health ▼ │ Overview                     Last updated 10:42  │
│───────────────│ [!] Webhook delivery failed. Review integration │
│ Overview      │                                                  │
│ Messaging     │  Submitted  Accepted   Pending   Delivered Failed │
│ Integrations  │  [authoritative values or explicit unavailable]  │
│ Finance       │  Last 24 Hours (default) · [7 Days] · [30 Days]  │
│ Plan & usage  │                                                  │
│ Settings      │  Delivery Rate · API Requests · Active Apps      │
│               │  Sender IDs                                      │
│               │  Delivery Trend                                  │
│               │  ┌────────────────────────────────────────────┐  │
│               │  │ chart + “View as table”                   │  │
│               │  └────────────────────────────────────────────┘  │
│               │                                                  │
│               │  Integration health     Recent Activity          │
│               │  [source-backed states and values only]          │
└───────────────┴──────────────────────────────────────────────────┘
```

Primary action depends on state: continue setup, create application, or view messages. Metrics never display if the user lacks `messages.view`.

### 11.3 Onboarding checklist

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Get your integration ready          60% complete │
│               │ ━━━━━━━━━━━━━━━━━━━──────────────                 │
│               │                                                  │
│               │ [✓] Confirm organization details                │
│               │ [✓] Create an application                       │
│               │ [→] Create an API credential        [Continue]  │
│               │ [ ] Open Integration Launch                      │
│               │ [ ] Verify first accepted API request            │
│               │ [ ] Configure delivery webhook      Recommended │
│               │ [ ] Invite teammates                Recommended │
│               │                                                  │
│               │ Need help? API quick start · Contact support     │
└───────────────┴──────────────────────────────────────────────────┘
```

The checklist is derived from authoritative facts. A dismiss action may hide recommended items but must not falsify completion.

### 11.3A Integration Launch

```text
INTEGRATION LAUNCH                                      READY TO TEST

[x] Organization: Acme Health
[x] Application: Clinic system
[x] API credential created

Save this credential now; it appears only once.
[mhi_live_....REDACTED-ONCE-SHOWN....] [Copy]
[ ] I stored it securely

Base URL [https://api.example/api/v1] [Copy]
[API Reference] [Postman] [SDK examples]
[Webhook signing guide] [Error codes]

First request
[Safe cURL example containing a SECRET placeholder]
Technical onboarding: Waiting for first accepted API request...

Next: Sender ID | Webhook | Limits | Go-live checklist
```

Integration Launch is an integration handoff, not proof of completion. After the original response ends, this page shows only credential metadata and offers revoke/reissue; it cannot recover the secret. Technical onboarding becomes complete only after authoritative evidence confirms that the public API accepted the first request. Rejection, missing evidence, or unavailable verification leaves it incomplete. A future test-message action follows the public API or an equivalently authorized application command.

### 11.4 Message list

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Messages                                         │
│               │ Inspect tenant-scoped message delivery.          │
│               │                                                  │
│               │ Date [Last 24h▼] App [All▼] Status [All▼]       │
│               │ Channel [SMS▼] Correlation [____________] [Apply]│
│               │                                                  │
│               │ ID         Recipient   App       Status   Created │
│               │ 01K...9F   +255•••42   Clinic    Delivered 10:41 │
│               │ 01K...8A   +255•••19   Alerts    Failed    10:39 │
│               │ 01K...72   +255•••03   Clinic    Submitted 10:38 │
│               │                                                  │
│               │ [← Newer]                      [Older →]          │
│               │ Showing authoritative data · Updated 10:42       │
└───────────────┴──────────────────────────────────────────────────┘
```

Row activation opens detail. Filters are allowlisted, URL-backed, and tenant scoped. Bulk row actions are not included initially.

### 11.5 Message detail

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ ← Messages                                       │
│               │ Message 01K...9F                    DELIVERED     │
│               │ [Copy public ID]                                 │
│               │                                                  │
│               │ Summary                                          │
│               │ Recipient      +255••••••42                      │
│               │ Application    Clinic integration                │
│               │ Channel / type SMS / Transactional               │
│               │ Correlation    appointment-1042                  │
│               │ Accepted       26 Jul 2026, 10:40:01 EAT         │
│               │ Delivered      26 Jul 2026, 10:40:08 EAT         │
│               │                                                  │
│               │ Lifecycle                                        │
│               │ ● Queued ── ● Submitted ── ● Delivered           │
│               │                                                  │
│               │ Content is not available in this dashboard.      │
└───────────────┴──────────────────────────────────────────────────┘
```

The lifecycle is a safe projection, not raw provider evidence. Unknown/contradictory evidence is represented conservatively.

### 11.5A Sender IDs

```text
SENDER IDS                                               [Request ID]

Sender       Status          Scope                  Default
MHIHEALTH    Approved        TZ / YAS               Yes
CLINIC       Pending review  TZ / all applications  -
OLDNAME      Suspended       TZ / YAS               -

Approval and availability are scope-specific.
[Manage application assignments]
```

Detail shows request history, validity, provider/country/route/application scope, assignment, and safe approval evidence. Only platform review users see approve/reject/suspend actions. “Approved” does not claim current provider availability; sender enforcement remains in the messaging data plane.

### 11.6 Applications list

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Applications                  [New application]  │
│               │                                                  │
│               │ Name              Code          Keys   Status     │
│               │ Clinic system     clinic-prod   2      Active     │
│               │ Appointment app   reminders     1      Active     │
│               │ Legacy test       legacy-test   0      Disabled   │
│               │                                                  │
│               │ Select an application to manage credentials.     │
└───────────────┴──────────────────────────────────────────────────┘
```

Users without manage permission see no creation action. Disabled applications remain visible to authorized users.

### 11.7 Application detail and credentials

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Clinic system                        ACTIVE       │
│               │ Code: clinic-prod       [Edit] [Disable]         │
│               │                                                  │
│               │ API credentials                  [Create key]    │
│               │ Name       Prefix       Last used   Expires State│
│               │ Production mhi_live_ab… 5 min ago   Never   Active│
│               │ Staging    mhi_live_cd… 4 days ago  30 Aug Active│
│               │                                      [⋯ Revoke] │
│               │                                                  │
│               │ Integration                                     │
│               │ API base URL [Copy] · Quick start · API docs     │
└───────────────┴──────────────────────────────────────────────────┘
```

Credential values are never listed. Revocation uses confirmation, recent-auth policy, re-authorization, and audit.

### 11.8 Credential one-time reveal

```text
┌──────────────────────────────────────────────────────────────────┐
│ API credential created                                      [×] │
│                                                                  │
│ Copy this value now. It will not be shown again.                 │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ mhi_live_••••••••.•••••••••••••••••••••••••••••••••••••• │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ [Copy credential]                                                │
│                                                                  │
│ [ ] I stored the credential securely.                            │
│                                              [Done]              │
└──────────────────────────────────────────────────────────────────┘
```

The real value is shown, but must be excluded from telemetry, page history, server logs, DOM snapshots, error reporting, and later responses. Closing without copying does not make it recoverable.

### 11.9 Delivery webhook

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Delivery webhook                                 │
│               │ Status: ENABLED                                  │
│               │ URL [https://example.org/hooks/mhi___________]   │
│               │ Secret [Replace secret]                          │
│               │ [Save configuration]                             │
│               │                                                  │
│               │ Recent delivery                                  │
│               │ Outcome     HTTP    Attempted            Event    │
│               │ Failed      —       26 Jul 10:39        01K...   │
│               │ Delivered   204     26 Jul 10:10        01K...   │
│               │                                                  │
│               │ Signature guide · Receiver example               │
└───────────────┴──────────────────────────────────────────────────┘
```

GET never reveals current or previous secrets. Secret replacement follows the existing overlap contract. A future test-send action needs a separate safe contract and is not assumed here.

### 11.9A Developer Center

```text
DEVELOPER CENTER                                   API v1 [change]
Base URL: Production [https://.../api/v1] [Copy]
Docs release: 2026-07-26 | Contract: current

[Quick Start]        [API Reference]
[OpenAPI / Swagger]  [Postman Collection download]
[SDK & Examples]     [Webhook Guide]
[Error Codes]        [Changelog]
[API Status]         [Go-Live Checklist]

Context: Clinic system | credential placeholder only
```

Public documentation omits tenant/application context and credentials. Authenticated views may select an authorized application and environment, but generated examples use placeholders and never embed stored secrets. Missing or stale generated artifacts show an unavailable/stale notice and link to the authoritative OpenAPI source rather than serving an unverified download.

### 11.9B API usage analytics

```text
API ANALYTICS                                  Updated 10:42:15
Range [24 hours] Application [All] Version [v1]

Requests 84,210 | RPM avg 58 | Peak 142
2xx 96.2% | 4xx 3.7% | 5xx 0.1% | p95 184 ms

Outcomes: 401 | 403 | 409 | 422 | 429
[Bounded trend chart] [View accessible table]

Endpoint group       Requests   Errors   p95
Message submission   70,120     2.8%     190 ms
Message retrieval    13,800     0.4%      92 ms
```

When telemetry is missing or stale, cards show “Unavailable” with freshness context. Counts are not rebuilt from message rows.

### 11.10 Team management

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Team                              [Invite member]│
│               │                                                  │
│               │ Name          Email            Role       Status │
│               │ A. Admin      a@acme.test      Admin      Active │
│               │ M. Operator   m@acme.test      Messaging  Active │
│               │ F. Reviewer   f@acme.test      Finance    Invited│
│               │                                                  │
│               │ Roles determine access. [View role permissions]  │
└───────────────┴──────────────────────────────────────────────────┘
```

Prevent removal or demotion of the last active tenant administrator. Role changes display their effective permissions before confirmation.

### 11.11 Audit log

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Audit log                                        │
│               │ Date [7 days▼] Actor [All▼] Action [All▼] [Apply]│
│               │                                                  │
│               │ Time       Actor      Action             Outcome │
│               │ 10:31:04   A. Admin   credential.created Success │
│               │ 09:16:42   M.Operator webhook.updated    Success │
│               │                                                  │
│               │ Select a row for safe metadata and request ID.   │
└───────────────┴──────────────────────────────────────────────────┘
```

Metadata is schema-allowlisted by event type. Secrets, content, password data, and credentials never appear.

Audit filters include bounded date range, correlation ID, actor, action, outcome, application public ID, and—when recorded—public message or webhook event ID. Platform context may also use tenant public ID. Audit results remain audit evidence; message lifecycle, webhook delivery, and provider operational evidence open in separate authorized views with their own filters and redaction.

```text
AUDIT / TROUBLESHOOTING FILTER BAR
Date [bounded range]  Actor [All]  Action [All]  Outcome [All]
Correlation ID [........]  Application public ID [........]
Evidence link: Message public ID [........] or Webhook event ID [........]
Platform only: Tenant public ID [........]                  [Apply]
```

### 11.12 Organization settings

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Organization                                     │
│               │ Name [Acme Health_____________________________]  │
│               │ Slug [acme-health]      Public ID [01K... Copy]  │
│               │ Timezone [Africa/Dar_es_Salaam▼]                 │
│               │ [Save changes]                                   │
│               │                                                  │
│               │ Danger zone                                      │
│               │ Request account closure             [Request]    │
└───────────────┴──────────────────────────────────────────────────┘
```

Only explicitly modeled settings belong here. Sensitive lifecycle actions are separated visually and procedurally.

### 11.13 Wallet and finance overview — future

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Wallet                                           │
│               │ Available        Reserved         Currency       │
│               │ TZS 1,250,000    TZS 84,200       TZS            │
│               │ [Top up - future policy]                         │
│               │                                                  │
│               │ Transactions                                     │
│               │ Time       Type       Amount       Reference      │
│               │ 10:40      Capture    -TZS 32      Message 01K…  │
│               │ 10:20      Credit     +TZS 50,000  Funding 01K…  │
│               │                                                  │
│               │ Balances are ledger-backed · Updated 10:42       │
└───────────────┴──────────────────────────────────────────────────┘
```

All values originate as integer minor units. The UI cannot directly edit balances.

### 11.14 Plan and usage — future

```text
┌───────────────┬──────────────────────────────────────────────────┐
│ Tenant nav    │ Plan and usage                                   │
│               │ Current plan: Growth                 ACTIVE      │
│               │ Renewal: 01 Aug 2026            [Change plan]    │
│               │                                                  │
│               │ API requests       62,500 / 100,000   62%        │
│               │ SMS submissions    48,120 / 75,000    64%        │
│               │ Throughput         20 messages/second             │
│               │                                                  │
│               │ Usage period: 01–31 Jul 2026                     │
└───────────────┴──────────────────────────────────────────────────┘
```

Usage, throttle rate, concurrency, and financial charge are labeled separately.

### 11.15 Platform overview

```text
┌────────────────┬─────────────────────────────────────────────────┐
│ PLATFORM       │ Platform overview                Updated 10:42  │
│ Overview       │ [!] 3 operational alerts                       │
│ Tenants        │                                                 │
│ Users          │ Active tenants  Trial  Suspended  Message rate  │
│ Operations     │ 184             22     3          420/min       │
│ Commercial     │                                                 │
│ Audit          │ Service health                                  │
│                │ API Healthy · SMPP Degraded · Webhooks Warning  │
│                │                                                 │
│                │ Tenants requiring attention                     │
│                │ Tenant         Issue                   Since    │
│                │ Acme Health    Webhook failures        10:31    │
└────────────────┴─────────────────────────────────────────────────┘
```

Health claims require real observability sources; absent telemetry displays “Unavailable,” never “Healthy.”

### 11.15A Platform component health

```text
COMPONENT HEALTH                                  Updated 10:42:15

Component                 State        Observed       Alert owner
Public API                Healthy      10:42:12       API Operations
Database                  Healthy      10:42:10       Infrastructure
Redis                     Unknown      10:31:00 stale Infrastructure
Queues                    Degraded     10:42:08       App Operations
SMPP Runtime              Healthy      10:42:11       Messaging Ops
Messaging Providers       Degraded     10:42:05       Provider Ops
Webhook Delivery          Healthy      10:41:58       App Operations
Projection workers        Unavailable  10:42:02       App Operations

[Select a component for safe trends, incidents, and runbook]
```

Each row is independent. A component detail shows threshold evidence, freshness, safe trends, incident/alert ownership, and runbooks. It omits credentials, raw PDUs, stack traces, secret hostnames, connection strings, and sensitive topology.

### 11.16 Platform tenant list

```text
┌────────────────┬─────────────────────────────────────────────────┐
│ Platform nav   │ Tenants                         [Create tenant]  │
│                │ Search [________] Status [All▼] Plan [All▼]     │
│                │                                                 │
│                │ Tenant       Status  Apps  24h msgs  Created    │
│                │ Acme Health  Active  2     12,480    02 Jun     │
│                │ Clinic B     Trial   1     40        24 Jul     │
│                │ Tenant C     Susp.   3     0         11 Jan     │
│                │                                                 │
│                │ [Previous]  Page 1 of …  [Next]                 │
└────────────────┴─────────────────────────────────────────────────┘
```

Cross-tenant filters are allowlisted and authorized. Sensitive tenant fields are not included in result rows.

### 11.17 Platform tenant detail and support context

```text
┌──────────────────────────────────────────────────────────────────┐
│ PLATFORM SUPPORT CONTEXT · Acme Health · Reason: ticket MHI-1042 │
├────────────────┬─────────────────────────────────────────────────┤
│ Platform nav   │ Acme Health                          ACTIVE      │
│                │ [Suspend] [Enter tenant workspace]               │
│                │                                                 │
│                │ Summary · Memberships · Apps · Health · Audit   │
│                │                                                 │
│                │ Public ID      01K...                           │
│                │ Applications   2                                │
│                │ Messages 24h   12,480                           │
│                │ Webhook        Failing                          │
│                │                                                 │
│                │ Every support-context access is audited.        │
└────────────────┴─────────────────────────────────────────────────┘
```

Entering tenant workspace does not impersonate a tenant user. It retains platform actor identity, shows a persistent banner, applies explicit support-context permissions, and records access.

### 11.18 Error, forbidden, and degraded screens

| Condition | UX |
|---|---|
| Not authenticated | Existing authentication entry with safe return path |
| No tenant membership | Workspace-empty guidance |
| Forbidden | Neutral permission message and link back; no resource existence detail |
| Tenant suspended | Restricted page with support guidance |
| Tenant closed | Closure page without tenant operational data |
| Not found/cross-tenant | Same neutral not-found response |
| Stale aggregate | Data shown with warning and last successful refresh |
| Aggregate unavailable | “Unavailable,” not zero; core navigation remains usable |
| Component telemetry stale/missing | Component is Unknown with last observation; no combined Healthy claim |
| Developer artifact missing/stale | Disable unverified download, show release/freshness, link to authoritative source |
| Sender scope unavailable | Show approval and live availability separately; block unsupported assignment/action |
| Mutation conflict | Explain that state changed, reload authoritative values |
| Rate limited | Retry guidance from authoritative policy, no automatic mutation retry |
| Unexpected error | Correlation/request ID and sanitized message |

## 12. Database impact assessment

No database change is made in this milestone.

### 12.1 Existing tables reusable without schema change

| Table group | Intended read/use | Constraints |
|---|---|---|
| `users` | Browser identity, platform role | Authentication unchanged |
| `tenants` | Workspace identity/status/profile | Avoid unbounded `settings_json` |
| `tenant_memberships` | Access and role | Needs later membership status domain authority |
| `applications` | App inventory | Tenant-scoped |
| `api_credentials` | Safe metadata, issue/revoke via existing concepts | Never expose `secret_hash`; secret is one-time only |
| `messages`, `sms_messages` | Safe list/detail | No ciphertext/body exposure in initial dashboard |
| `message_attempts`, `message_events`, `message_delivery_receipts` | Lifecycle projection | Do not expose provider internals |
| `audit_logs` | Audit list/detail | Append-only; metadata allowlist |
| webhook tables | Configuration and delivery health | Secret ciphertext excluded |
| wallet tables | Future balance and ledger views | Ledger/accounting service remains authority |

### 12.2 Likely future schema changes

Each item requires its own approved migration milestone.

| Need | Proposed durable model | Why existing storage is insufficient |
|---|---|---|
| Membership invitations | `tenant_invitations` | Tokens, expiry, inviter, role, and lifecycle do not exist |
| Onboarding state exceptions | `tenant_onboarding_state` only if facts cannot derive progress | Avoid placing workflow history in `settings_json` |
| User preferences | `user_preferences` or explicitly modeled columns | Timezone/accessibility/display preferences are user-owned |
| Support context | `platform_support_sessions` | Reason, target tenant, expiry, and actor require audit-grade evidence |
| Plans | `subscription_plans`, `plan_versions`, `plan_entitlements` | Commercial catalog and immutable versioning do not exist |
| Subscriptions | `tenant_subscriptions`, `subscription_events` | Tenant-to-plan lifecycle and history do not exist |
| Rate policies | `rate_limit_policies`, `tenant_rate_limit_assignments` | Current rate limiting is config-based, not plan-aware persistence |
| Usage metering | `usage_meters`, `usage_aggregates` | Message rows are not a complete generic entitlement meter |
| Pricing | versioned price books/rates | Must preserve historical charge inputs |
| Invoices/payments | invoice, line, payment, payment-attempt tables | Wallet ledger is not invoicing |
| Dashboard aggregates | daily/hourly tenant metric projections | High-volume charts should not repeatedly scan transactional data |
| Exports | `data_exports` | Async status, object key, filters, expiry, and requester |
| Notifications | notification preference and delivery records | Operational audit is not user notification state |
| Sender IDs | `sender_ids`, scoped `sender_id_approvals`, and `application_sender_ids` assignments | Inventory, lifecycle, default, scope, validity, evidence, and application assignment do not exist |
| API operational metrics | external metrics backend or bounded `api_usage_aggregates` projections | Requests and failures cannot be reconstructed correctly from message rows |
| Component health | preferably observability backend; optional `component_health_incidents`/snapshots where durable history is required | Transactional tables are not health telemetry |
| Feature rollouts | typed rollout store with scope, version, owner, reason, start/expiry | Ad hoc configuration cannot safely model tenant cohorts and cleanup |
| Developer artifacts | release manifest/index if repository artifacts alone cannot provide freshness/version metadata | Downloads need version, checksum, compatibility, and release ownership |

### 12.3 Data modeling rules

1. Every business table has explicit `tenant_id`, except genuinely platform-global catalog tables.
2. Tenant-owned child tables use composite ownership constraints where practical.
3. Public identities use ULIDs; browser URLs do not expose internal numeric IDs.
4. Money uses signed or unsigned integers in minor units as appropriate, plus ISO currency.
5. Commercial plan and price changes are versioned; historical charges point to immutable versions.
6. Audit/event/ledger evidence is append-only.
7. JSON is limited to non-authoritative, schema-controlled metadata; core state, permissions, money, limits, and lifecycle fields are columns/tables.
8. Invitation and reset-like tokens are hashed at rest.
9. Secrets use encryption or hashing appropriate to whether plaintext recovery is required; dashboard retrieval is never the recovery path.
10. Retention, deletion, and anonymization are explicit lifecycle jobs, not cascading accidents.
11. Sender approval scope is normalized enough to express provider, country, route, application, and validity without treating one global status as universal authorization.
12. Metrics aggregate only bounded dimensions; high-cardinality request/message/correlation identifiers remain troubleshooting evidence, not metric dimensions.
13. Feature rollout state, entitlements, subscription capabilities, kill switches, and user preferences are not stored in one ambiguous flags table.

### 12.4 Index and query impact

Before implementation, representative production-volume query plans must be reviewed for:

- tenant message list by `(tenant_id, application_id, status, created_at, id)`;
- tenant aggregate counts by time/status;
- credential expiry and last-used inventory;
- webhook attempts by tenant and attempted time;
- audit history by tenant and time;
- platform tenant search and status;
- wallet ledger history by tenant/wallet/time.
- Sender ID inventory and effective scoped approval/assignment;
- API usage aggregates by tenant/application/version/endpoint group/time bucket;
- audit evidence by correlation ID, actor/action/outcome/application/date;
- message and webhook evidence by public identity within bounded time ranges;
- platform troubleshooting by tenant public ID and evidence family;
- component health history by component and observation time.

Existing indexes support several transactional lookups but may not support every dashboard aggregate. Indexes should follow measured queries, not speculative UI requirements. Large cross-tenant analytics should use projections rather than weakening tenant boundaries or adding unbounded scans.

### 12.5 Implementation sequencing and future migration boundaries

Recommended independent milestones:

1. Browser workspace context and authorization expansion, using current schema.
2. Read-only tenant shell and overview with transactional-data queries.
3. Developer Center shell, versioned artifact manifest, and public/authenticated documentation boundaries.
4. Applications/credentials/webhook administration and Integration Launch.
5. Message operations and bounded audit/troubleshooting views.
6. Sender ID inventory/request schema and tenant assignment UX; platform approval is a separate milestone.
7. Authoritative API usage metering/observability contract and analytics projections.
8. Independent platform component-health adapters and operations screens.
9. Membership invitations and team management schema.
10. Aggregate projections and exports.
11. Feature rollout resolver/store, only when a concrete rollout requires it.
12. Wallet read experience.
13. Plans, subscriptions, entitlements, limits, and usage metering.
14. Billing, invoices, and payments.
15. Platform commercial and remaining operational tooling.

Each is separately reviewed, implemented, tested, and stopped for approval.

## 13. Security review

### 13.1 Trust boundaries

```mermaid
flowchart LR
    Internet((Internet))
    Browser[Untrusted browser]
    Web[Dashboard web boundary]
    App[Application/domain]
    Cache[(Redis)]
    DB[(MySQL)]
    Queue[Queue workers]
    External[External billing / email<br/>future]

    Internet --> Browser
    Browser -->|session + CSRF-protected request| Web
    Web -->|validated DTO| App
    App -->|tenant-scoped query/command| DB
    App -->|tenant-keyed cache| Cache
    App -->|tenant-bearing job| Queue
    Queue --> DB
    Queue --> External
```

The browser, route identifiers, query strings, form state, uploaded/exported files, and external callbacks are untrusted.

### 13.2 Threats and controls

| Threat | Required control |
|---|---|
| Cross-tenant IDOR | Public IDs plus membership authorization plus tenant-scoped repository predicates; neutral 404 |
| Missing tenant condition | Tenant identity required by repository method signatures; query tests and database composite ownership |
| Platform privilege abuse | Fine-grained permission, explicit support context, reason, expiry, immutable audit, persistent banner |
| CSRF | Framework CSRF protection on all session-authenticated mutations |
| XSS | Escaped templates, restrictive content security policy, no raw metadata rendering |
| Credential disclosure | One-time reveal, hashed stored secret, no logging/telemetry, no retrieval endpoint, safe clipboard UX |
| Password/session theft | Existing auth controls; secure/HttpOnly/SameSite cookies, TLS, session rotation; MFA is a future auth milestone |
| Stale authorization | Re-authorize mutations; invalidate permission cache; active membership/status checked per request |
| Mass assignment / over-posting | Validated command DTOs and allowlisted fields |
| SQL injection | Parameterized repository queries and allowlisted sort/filter fields |
| Formula injection in export | Escape spreadsheet formula prefixes and treat values as text |
| CSV data leakage | Async export scoped to tenant/requester; short-lived download; audit and expiry |
| SSRF through webhook URL | Retain/strengthen URL validation and outbound network controls; never fetch URL from browser preview |
| Sensitive audit/log data | Event-specific safe metadata schema; sanitize exceptions; never log keys, passwords, SMPP credentials, bodies |
| Cache tenant collision | Namespace every tenant entry by immutable tenant ID and permission-relevant dimensions |
| Background job context loss | Include immutable tenant ID and actor/request lineage; re-load scoped records |
| Clickjacking | `frame-ancestors` CSP or equivalent deny policy |
| Brute force / abuse | Existing authentication protections plus action-specific throttles for invitation/key/export actions |
| Unsafe destructive action | Clear object name, consequence, confirmation, recent auth for high-risk operations, idempotent command |
| Enumeration | Consistent errors for absent/cross-tenant resources and invitation handling |
| Open redirect | Allowlisted relative return paths only |
| Supply-chain risk | Lockfiles, package review, dependency scanning; no arbitrary dashboard plugins |
| Sender approval escalation | Platform-only review permission; scoped approval records; tenant users cannot widen provider/country/route/application validity |
| Sender configuration bypass | Data plane independently enforces sender authorization, public API validation, entitlements, and routing |
| Misleading sender availability | Present approval scope and live provider availability separately with freshness/source |
| Metric data leakage/cardinality attack | Bounded label allowlist; never label by recipient, credential, body, secret, raw URL, correlation ID, or public message ID |
| False health status | Independent component authorities, freshness, Unknown state, and no aggregate Healthy claim with missing evidence |
| Troubleshooting search as data exfiltration | Separate evidence-family queries, bounded ranges, public IDs, tenant scope, permission checks, stable pagination, redaction |
| Interactive API docs leak credentials | Placeholder-only generated examples; no persisted secret injection; no secret in URL/storage/telemetry |
| Feature flag authorization bypass | Permission, tenant isolation, entitlement, and server enforcement remain independent; audit flag changes |
| Unsafe kill-switch failure | Approved fail-safe default, local enforcement behavior, owner, alert, expiry/recovery procedure |

### 13.3 Sensitive-data classification

| Class | Examples | Dashboard treatment |
|---|---|---|
| Secret | passwords, API key secret, webhook secret, SMPP credentials | Never list or log; one-time API key reveal only |
| Message content | body, decrypted recipient | Excluded initially; future explicit permission and audit |
| Personal data | user name/email, recipient fragments | Minimum display, masked where possible, tenant scoped |
| Financial | balance, ledger, invoice/payment details | Finance permission; integer values; audited mutations |
| Internal operational | provider reference, SMPP status/raw PDU, stack trace | Not exposed to tenants |
| Public-safe identifier | tenant/application/message public ULID | Display/copy as needed |
| Observability | aggregate request counts, latency, component state | Bounded tenant-safe dimensions; infrastructure drill-down platform-only |
| Sender approval evidence | reviewer, scope, validity, reason categories | Tenant sees safe approval outcome/scope; internal review notes platform-only |

### 13.4 Session and browser controls

Without changing authentication in this milestone, a later implementation must verify:

- TLS-only production access;
- secure, HttpOnly, and appropriate SameSite cookies;
- session ID rotation after login and privilege change;
- idle/absolute timeout policy;
- password confirmation or recent-auth challenge for high-risk actions;
- CSP, HSTS, referrer policy, content-type options, and frame protections;
- no secrets in URLs, browser storage, analytics payloads, or HTML caches;
- `Cache-Control: no-store` for secret reveal and highly sensitive finance pages;
- tenant switch removes tenant-specific client state.

### 13.5 Audit requirements

At minimum, audit:

- tenant lifecycle request and transition;
- support-context entry/exit;
- membership invitation, acceptance, revocation, role/status change;
- application creation/update/disable;
- credential creation and revocation, but never the secret;
- webhook configuration and secret replacement, but never URL query secrets or secret values;
- export request/download/expiry;
- plan/subscription/limit change;
- Sender ID request, approval/rejection/suspension, default, scoped assignment, and validity change;
- rollout flag, entitlement, or operational kill-switch change;
- wallet adjustment, payment, or refund;
- denied high-risk actions where useful and safe.

Audit includes tenant, actor, action, target public identity, outcome, request/correlation ID, source IP under policy, and schema-allowlisted metadata.

### 13.6 Security test strategy for later milestones

- tenant A cannot read or mutate tenant B resources through path, form, filter, export, cache, or queued job;
- inactive memberships lose access immediately;
- each role has positive and negative permission coverage;
- platform context is explicit and audited;
- secret values never appear in logs, serialized models, exceptions, HTML after reveal, exports, or audit metadata;
- CSRF, XSS encoding, open redirect, filter allowlist, and mass-assignment tests;
- concurrency tests for credential issuance/revocation, invitations, last-admin protection, and lifecycle transitions;
- browser accessibility and security-header tests;
- query budget tests to prevent unbounded dashboard scans.
- Sender ID tests proving tenant users cannot approve/widen scope and data-plane rejection still wins over dashboard configuration;
- Developer Center tests proving artifacts match released versions/OpenAPI and examples never embed secrets;
- metrics tests for source attribution, bounded labels, tenant isolation, freshness, unavailable-not-zero, and percentile definitions;
- health tests for independent component states, stale-to-Unknown transitions, safe drill-down, and missing-telemetry behavior;
- audit/troubleshooting tests for evidence-family separation, public-ID-only lookup, bounded dates, neutral not-found, pagination, redaction, and export scope;
- feature-flag tests for flag failure, stale cache/versioning, permission and entitlement disagreement, safe kill switches, audit, expiry, and cleanup.

## 14. Future extensibility

### 14.1 Billing

Billing should be introduced as a distinct domain:

```mermaid
flowchart LR
    Usage[Rated usage evidence]
    Price[Versioned price book]
    Charge[Immutable charge]
    Invoice[Invoice and lines]
    Payment[Payment attempts]
    Wallet[Wallet ledger]

    Usage --> Charge
    Price --> Charge
    Charge --> Invoice
    Invoice --> Payment
    Payment -. optional funding .-> Wallet
```

Rules:

- Pricing inputs and calculated charges are immutable/versioned.
- Money is integer minor units with currency.
- Invoice state is not inferred from wallet balance.
- Provider cost, customer price, tax, discount, credit, and payment are separate values.
- Reversals use compensating entries, not destructive edits.
- UI calls billing application services; it performs no price arithmetic.

### 14.2 Wallet

The existing wallet ledger remains authoritative.

- Dashboard reads a purpose-built wallet summary DTO.
- Balance changes occur only through approved accounting operations.
- No editable balance form exists.
- Platform adjustments require reason, idempotency key, maker/checker policy when approved, and audit.
- Ledger entries are append-only; pagination is stable.
- Currency conversion is out of scope until explicitly designed.

### 14.3 Rate limits

Rate limits have at least four dimensions:

- request rate;
- message submission throughput;
- concurrency;
- periodic quota/usage allowance.

A future policy resolver combines platform safety ceilings, plan entitlements, tenant overrides, application overrides, and temporary operational restrictions. The most restrictive applicable safety bound wins unless policy explicitly defines otherwise.

```mermaid
flowchart TD
    Safety[Platform safety ceiling]
    Plan[Plan entitlement]
    Tenant[Tenant override]
    App[Application override]
    Temp[Temporary restriction]
    Resolver[Effective limit resolver]
    Enforcement[API/runtime enforcement]
    Display[Dashboard effective-limit DTO]

    Safety --> Resolver
    Plan --> Resolver
    Tenant --> Resolver
    App --> Resolver
    Temp --> Resolver
    Resolver --> Enforcement
    Resolver --> Display
```

Dashboard display and data-plane enforcement must consume the same effective policy authority.

### 14.4 Subscription plans

Plans are versioned catalogs of entitlements, not role bundles.

- A plan version is immutable once assigned.
- A tenant subscription references a plan version and effective interval.
- Entitlements use typed keys and typed values, not arbitrary UI JSON.
- Plan change preview shows effective date, limits, billing consequence, and lost capabilities.
- Scheduled downgrade and proration require a billing-specific design.
- Subscription status must not be overloaded into `tenants.status`.
- Tenant suspension for security and subscription delinquency are distinct causes even if both restrict service.

### 14.5 Multi-channel expansion

Dashboard information architecture is channel-neutral:

- overview metrics segment by channel;
- message list uses the existing channel field;
- integration settings are capabilities attached to applications/tenants;
- channel-specific configuration appears as submodules;
- shared identity, permissions, audit, billing, wallet, limits, reports, and notification patterns remain reusable.

WhatsApp, email, push, USSD, and voice must add typed domain modules rather than channel flags scattered across SMS screens.

### 14.6 Localization and regionalization

Future-ready seams:

- user display timezone;
- locale-aware dates and numbers;
- ISO currency;
- translatable interface copy;
- region-specific data retention and residency policy;
- phone number masking that remains useful across numbering plans.

Core identifiers, stored timestamps, event times, and exports retain UTC authority.

### 14.7 Observability

Dashboard observability should cover:

- page/query latency by safe route name;
- cache/projection age;
- error rate with request ID;
- failed authorization counts without sensitive identifiers;
- export queue latency;
- dashboard command outcomes;
- tenant isolation test/alert evidence.
- request volume, requests/minute, accepted/rejected outcomes, HTTP class and selected status-code counts;
- average/percentile latency and peak throughput;
- application, API version, and bounded endpoint-group usage;
- independent component state and freshness.

Metrics labels must not include API keys, emails, recipients, message IDs at high cardinality, bodies, webhook URLs, or tenant secrets.

Message records are not an API observability substitute: authentication failures, validation failures, throttles, conflicts, reads, and non-message endpoints may produce no message. Metric and usage sources must define attribution, aggregation, sampling, retention, late arrival, percentile calculation, and freshness.

### 14.8 Sender ID extensibility

Sender ID policy should resolve an effective authorization from:

- tenant ownership/request;
- approval state and evidence;
- provider compatibility;
- country and route;
- application assignment and default;
- validity interval;
- tenant/application/channel entitlement;
- current operational/provider availability.

The resolver is shared with or authoritative for the messaging data plane. Dashboard read DTOs explain the safe result but never create an alternate authorization path. Future channels may introduce analogous originator identities behind channel-specific typed policies rather than overloading SMS Sender IDs.

### 14.9 Developer Center release lifecycle

Developer artifacts are versioned release products:

1. OpenAPI is reviewed and released as the public API contract.
2. Reference pages, Swagger rendering, Postman collection, and generated examples are validated against that version.
3. SDK/examples declare compatible API and artifact versions.
4. A release manifest records version, checksum/link, publication time, owner, and support state.
5. Changelog and go-live guidance publish with the same release.
6. Automated freshness checks detect drift; stale artifacts are labeled or withheld.

Environment-aware base URLs come from approved configuration and are displayed separately from user credentials. Public documentation contains no tenant context. Authenticated enhancements require tenant access and relevant permission.

### 14.10 Feature flags and entitlements

The resolver described in section 10A may later combine deployment availability, rollout cohort, tenant entitlement, plan capability, operational restriction, user preference, and authorization. It returns a typed explanation/version suitable for consistent UI and server enforcement. It does not turn a flag vendor or configuration file into a business-policy authority.

## 15. Architectural decisions

The following decisions are proposed for approval.

### ADR-DASH-001 — Modular monolith first

**Decision:** Implement the dashboard inside the Laravel application using explicit modules and application boundaries.

**Rationale:** It reuses existing domain authorities and transactions, reduces operational complexity, and preserves extraction seams.

**Rejected:** A separate dashboard microservice, because it would duplicate tenancy, authorization, and business rules prematurely.

### ADR-DASH-002 — Separate platform and tenant workspaces

**Decision:** Use distinct navigation and URL namespaces.

**Rationale:** Platform cross-tenant authority is materially different from tenant membership authority. Visible separation reduces accidental privilege use.

### ADR-DASH-003 — Tenant identity in the path

**Decision:** Bind tenant workspace requests to a public tenant identity in the path and authorize it on every request.

**Rationale:** Links are understandable, multi-tab use is safe, and tenant context is not hidden in mutable session state.

### ADR-DASH-004 — Permission-based application authorization

**Decision:** UI and application services authorize permissions; roles map to permissions centrally.

**Rationale:** Supports least privilege, future custom roles, and reliable tests.

### ADR-DASH-005 — Purpose-built read DTOs

**Decision:** Dashboard queries return immutable, safe DTOs rather than Eloquent models.

**Rationale:** Prevents lazy-loading surprises, accidental sensitive serialization, unbounded graphs, and presentation-owned policy.

### ADR-DASH-006 — Transactional source first, projections when measured

**Decision:** Initial bounded reads may use existing stores; expensive historical analytics use dedicated asynchronous projections after query evidence justifies them.

**Rationale:** Avoids both premature reporting infrastructure and damaging transactional scans.

### ADR-DASH-007 — No message body in initial dashboard

**Decision:** Show safe metadata and masked recipients only.

**Rationale:** Minimizes privacy exposure and avoids inventing content-access/retention policy.

### ADR-DASH-008 — One-time credential reveal

**Decision:** Plaintext API credentials are returned once on creation and never recoverable.

**Rationale:** Matches hashed secret storage and reduces disclosure risk.

### ADR-DASH-009 — Derived onboarding progress

**Decision:** Derive completion from authoritative resources wherever possible.

**Rationale:** Avoids drift between checklist flags and real integration readiness.

### ADR-DASH-010 — Commercial domains remain separate

**Decision:** Wallet, pricing, billing, subscriptions, entitlements, and rate limits have distinct authorities.

**Rationale:** These concepts have different invariants and must not be collapsed into tenant settings or message status.

### ADR-DASH-011 — Technology choice deferred within a fixed boundary

**Decision:** Do not select Livewire, Blade-only, Inertia, or a SPA framework in 6A. Any choice must preserve the presentation/application/domain boundaries and accessibility requirements in this document.

**Rationale:** UX and architecture should not depend on a UI transport mechanism. The current repository contains no dashboard framework dependency.

### ADR-DASH-012 — No tenant impersonation

**Decision:** Platform staff use an explicit support context while retaining their own actor identity.

**Rationale:** Preserves accountability and avoids ambiguous audit trails.

### ADR-DASH-013 — Sender IDs are scoped policy, not display configuration

**Decision:** Model Sender IDs as first-class inventory, scoped approval, and assignment concepts; keep final enforcement in the messaging data plane.

**Rationale:** One sender can be valid for one provider, route, country, validity interval, or application and invalid for another.

### ADR-DASH-014 — OpenAPI remains the public API authority

**Decision:** Developer Center artifacts are derived from or validated against versioned OpenAPI and never redefine it.

**Rationale:** Prevents contract drift across reference pages, interactive docs, Postman, examples, and SDK guidance.

### ADR-DASH-015 — API metrics are not message projections

**Decision:** API analytics consume authoritative observability or usage metering.

**Rationale:** Message rows omit rejected, unauthenticated, invalid, throttled, read, and other requests.

### ADR-DASH-016 — Component health is independent

**Decision:** Each platform component owns a freshness-labeled Healthy, Degraded, Unavailable, or Unknown result.

**Rationale:** A combined health claim hides missing telemetry and unrelated failure modes.

### ADR-DASH-017 — Troubleshooting evidence remains separated

**Decision:** Audit, message lifecycle, webhook delivery, and provider operational evidence use separate bounded query contracts.

**Rationale:** One unrestricted cross-table search would weaken authorization, redaction, query budgets, and evidence semantics.

### ADR-DASH-018 — Flags and entitlements are distinct

**Decision:** Deployment flags, release rollouts, tenant entitlements, plan capabilities, kill switches, and user preferences have different owners and authorities.

**Rationale:** Prevents temporary delivery mechanisms from becoming hidden permanent business logic or authorization.

## 16. Risks and mitigations

| Risk | Likelihood | Impact | Mitigation |
|---|---:|---:|---|
| Cross-tenant data leakage | Medium | Critical | Structural scoping, permission checks, composite ownership, adversarial tests |
| Platform workspace becomes an unrestricted backdoor | Medium | Critical | Fine permissions, explicit support context, reason/expiry/audit, no impersonation |
| Dashboard scans degrade message ingestion | Medium | High | Bounded windows, cursor pagination, query budgets, projections |
| Role model is too coarse | High | Medium | Permission authority and future custom roles; sensitive optional grants |
| Existing permission mapping conflicts with target UX | High | Medium | Dedicated authorization milestone and compatibility tests |
| `settings_json` accumulates core business state | Medium | High | Modeled tables/columns and schema review |
| Secret leaks through telemetry/browser tooling | Medium | Critical | One-time page controls, telemetry exclusion, tests, no-store |
| Aggregate values disagree with operational state | Medium | High | Defined metrics, freshness marker, source ownership, reconciliation |
| Wallet UI bypasses accounting invariants | Low | Critical | Read DTOs and existing accounting commands only |
| Subscription status is confused with tenant status | Medium | High | Separate lifecycle models and effective-access policy |
| Platform health shows false confidence | Medium | High | “Unavailable” state and authoritative telemetry contracts |
| Framework choice drives business logic into components | Medium | High | Enforce dependency rules and thin adapters |
| Invitation abuse or enumeration | Medium | High | Hashed single-use tokens, expiry, throttle, neutral responses |
| Export leaks sensitive or stale permissions | Medium | High | Snapshot scope, download re-authorization, expiry, audit, redaction |
| Mobile tables become unusable | Medium | Medium | Responsive card/table pattern and accessibility QA |
| Requirements expand into multiple implementation milestones | High | Medium | Milestone boundaries in section 12.5 and review gate |
| Sender shows Approved but is unusable on the selected route/provider | Medium | High | Scope-specific status, authoritative availability, data-plane enforcement |
| Developer artifacts drift from OpenAPI | Medium | High | Release manifest, generated validation, ownership, freshness and version labels |
| Message rows undercount API errors/traffic | High | High | Dedicated observability/usage authority |
| Missing telemetry produces false Healthy/zero claims | Medium | High | Unknown/Unavailable semantics, freshness, independent components |
| Broad troubleshooting search leaks or overloads data | Medium | Critical | Evidence-family separation, public IDs, bounded ranges, permissions, indexes |
| Temporary flags become permanent hidden policy | High | Medium | Named owner/expiry/cleanup and typed entitlement migration |

## 17. Assumptions and open questions

### 17.1 Assumptions

1. Existing Laravel session authentication remains the browser identity authority.
2. A user may belong to multiple tenants.
3. `platform_admin` users are trusted employees or operators, but still require auditable least privilege.
4. Trial and active tenants continue to permit API access unless the domain policy changes in a separate milestone.
5. The first implementation targets SMS but preserves channel-neutral navigation.
6. Dashboard message content is not required for the first usable release.
7. Existing public message status projection remains authoritative for tenant-facing status.
8. The existing wallet accounting design remains authoritative.
9. English is the initial interface language; localization readiness is required.
10. Desktop is common for administration, but all core tasks must work on mobile.
11. OpenAPI remains the authoritative public API contract.
12. Sender approval and live provider availability can differ and require separate authorities.
13. Authoritative API request telemetry will be selected before analytics implementation.
14. Component health data may come from multiple monitoring systems and is normalized only through safe read DTOs.

### 17.2 Decisions required before implementation

| Question | Why it matters | Owner |
|---|---|---|
| Can users self-create tenants, or only accept invitations? | Changes onboarding and abuse controls | Product/security |
| Is email verification required before tenant access? | Authentication policy; explicitly outside 6A | Security/product |
| Which tenant profile fields are legally/business required? | Schema and onboarding | Product/compliance |
| What membership statuses are valid? | Domain enum, authorization, invitations | Architecture/product |
| Can suspended tenants view historical data? | Lifecycle authorization and support UX | Security/compliance |
| Who may activate, suspend, reactivate, and close tenants? | Lifecycle command policy | Operations/compliance |
| Is message content ever visible, and under what retention/audit policy? | High-risk privacy scope | Security/compliance |
| Which role matrix differences from current behavior are approved? | Backward compatibility | Product/security |
| What non-technical criteria, if any, are required for tenant activation after authoritative technical onboarding completion? | Keeps technical completion distinct from commercial or operational activation | Unresolved |
| What is the credential expiry/rotation policy? | Security warnings and workflow | Security |
| Which UI rendering approach will be used? | Implementation dependencies and test strategy | Engineering |
| What data source defines provider/queue health? | Platform overview truthfulness | Unresolved |
| Who owns Sender ID approval? | Approval accountability and separation from ordinary tenant roles | Unresolved |
| What is the Sender ID approval scope model across provider, route, country, application, and validity period? | Effective sender authorization and durable modeling | Unresolved |
| What is the authoritative observability source for API analytics, including retention, buckets, percentiles, and attribution? | Analytics correctness and freshness | Unresolved |
| What precisely defines Healthy, Degraded, Unavailable, and Unknown, including thresholds and staleness windows for each component? | Consistent health semantics and alerting | Unresolved |
| Which Developer Center pages are public? | Documentation exposure and discoverability | Unresolved |
| Which Developer Center pages require authenticated tenant context? | Tenant data, application context, and permission boundaries | Unresolved |
| Who owns the OpenAPI, Swagger UI, Postman, SDK, and code-example release lifecycle? | Contract/artifact freshness and release accountability | Unresolved |
| Who owns deployment and release Feature Flags? | Release safety, expiry, and cleanup accountability | Unresolved |
| Who owns operational Kill Switches? | Fail-safe activation, recovery, and audit accountability | Unresolved |
| Which flag/entitlement store and fail-safe defaults are approved for the first concrete rollout? | Release and operational safety | Unresolved |
| Will Integration Launch support an in-dashboard test message, or remain documentation-only? | Determines whether a later approved messaging command/UI boundary is needed | Unresolved |
| Are wallet top-ups manual, payment-provider based, or both? | Finance architecture | Finance/product |
| What are plan, quota, pricing, tax, invoice, and proration rules? | Commercial schema | Finance/product |
| What retention applies to audit, exports, messages, and user data? | Storage and compliance | Compliance |

## 18. Implementation constraints for subsequent milestones

Any later dashboard implementation must:

- keep controllers and UI components thin;
- add no business logic to Blade templates;
- use explicit command/query DTOs;
- keep tenant filtering in all tenant-owned persistence operations;
- use existing domain/projector/accounting authorities;
- never edit vendor files;
- never log passwords, API keys, webhook secrets, SMPP credentials, or message bodies;
- store money as integer minor units;
- add focused tenant isolation, role, negative authorization, concurrency, and accessibility tests;
- verify database query shape at representative volume;
- introduce one reviewed capability milestone at a time.
- preserve OpenAPI as the Developer Center’s public API authority and validate downloadable artifacts against releases;
- keep Sender ID approval platform-only, scope-specific, audited, and enforced again by the messaging data plane;
- source API analytics and component health from named authoritative telemetry with visible freshness and unavailable/unknown semantics;
- keep audit, message, webhook, and provider evidence searches separate, bounded, public-ID-based, tenant-scoped, redacted, and paginated;
- treat flags as deployment/rollout mechanisms, entitlements as business capability, permissions as authorization, and tenant filtering as structural isolation.

## 19. Suggested acceptance criteria for the first implementation milestone

These criteria are guidance, not implementation in 6A:

1. An authenticated user can see only active tenant memberships.
2. A tenant path is resolved to an immutable tenant identity and authorized on every request.
3. Tenant A cannot infer, list, view, mutate, export, or cache tenant B data.
4. A platform administrator enters a distinct workspace and cannot accidentally execute an unscoped tenant query.
5. The shared shell is keyboard accessible and responsive.
6. Navigation is permission-aware while server authorization remains authoritative.
7. Suspended and closed tenant behavior follows an approved policy.
8. No application route, controller, public API contract, authentication behavior, or data-plane runtime is regressed.
9. All tests pass, including new adversarial tenant isolation coverage.
10. Implementation stops at the single approved follow-up milestone for review.
11. Developer artifacts cannot drift from the selected authoritative OpenAPI release.
12. Sender ID dashboard state cannot bypass sender validation or routing.
13. Missing metrics/health telemetry produces Unavailable or Unknown, never zero or Healthy.
14. Feature visibility agrees with server-side flag/entitlement resolution and independent authorization.

## 20. Milestone 6A scope confirmation

### Documentation created

| File | Responsibility |
|---|---|
| `docs/architecture/dashboard/saas-dashboard-architecture-and-ux.md` | Revision 3 dashboard architecture and UX specification incorporating Architecture Addendum 6A-1 and final independent architecture review |

### Production files changed

None.

### Database changes

None. Section 12 is an impact assessment only.

### Explicitly unchanged

- routes;
- controllers;
- authentication and authorization implementation;
- middleware;
- Livewire or other UI components;
- migrations and database schema;
- models;
- services, repositories, events, and jobs;
- public API and OpenAPI contract;
- SMPP runtime and provider integrations;
- billing, wallet, rate-limit, and subscription behavior.

### Review gate

This document must be reviewed by product, security, operations, and engineering. Open questions in section 17 must be resolved only as needed for a narrowly scoped follow-up milestone. Approval of this specification does not approve all future modules for implementation at once.

Revision 3 incorporates Architecture Addendum 6A-1 and the approved independent architecture review refinements but does not authorize implementation beyond separately approved milestones.
