Skip to content

Frontend Implementation Specification

Related documentation: Kisum System · Backend implementation · Backend Auth · Backend Core · Backend Admin · Frontend Applications · Frontend Admin Specification

  • Implemented now: Kisum frontend is not one single application. The local workspace currently contains multiple frontend applications with different stacks and maturity levels.
  • Implemented now: Frontend-Kisum is the main product app and is a large Next.js App Router codebase with React 19.
  • Implemented now: Frontend-Kisum-Admin is the platform-admin app and uses React Router 7 with React 19.
  • Implemented now: Frontend-Kisum-Finance is a separate finance-focused Next.js application with internal finance routes and a live vendor portal.
  • Implemented now: Frontend-Kisum-Venues is a real Next.js 16 + React 19 App Router application with live venue CRUD/operations/reporting surfaces.
  • Implemented now: Frontend-Kisum-Artists is no longer a chassis-only repo; the FE-Phase 0–7 app surface is shipped.
  • Implemented now: Frontend-Kisum-Promoters is a real dedicated promoter persona app with /booking/*, venue marketplace, billing, and legacy promoter CRM surfaces.
  • Implemented now: Frontend-Kisum-Website is the public marketing SPA and hands checkout/payment to System-Kisum-Checkout.
  • Implemented now: Frontend-Nextkt is a frontend-only Next.js ticket sales prototype with landing and event-detail purchase UI. It does not define backend ownership or checkout contracts yet.
  • Target architecture: frontend should converge on the shared Auth/Core/Base/module contract described in this docs site, even though the current applications are still partly fragmented by domain and history.

This document is FINAL and ENFORCEABLE for frontend implementation.

It defines how the frontend must integrate with:

  • Auth Backend
  • Platform Core (indirectly only, never direct for access)
  • Base Backend
  • Module Backends

This document complements:


This document explains how the frontend should work in the Kisum architecture, while also reflecting the current multi-app implementation in the local workspace.

It covers:

  • login flow
  • token usage
  • access bootstrap
  • company switching
  • module visibility
  • route guarding
  • permission-aware UI
  • request headers
  • cache and refresh behavior
  • what frontend is allowed to decide
  • what frontend must never decide
  • how the current frontend applications are split today

Frontend is the UX layer.

Frontend is responsible for:

  • login screens
  • app shell
  • company selector
  • module navigation
  • page rendering
  • route visibility
  • button visibility
  • form visibility
  • user interaction

Frontend is not responsible for:

  • validating JWTs as security authority
  • computing effective access
  • deciding entitlements
  • deciding company membership
  • deciding permissions as source of truth
  • replacing backend authorization

2.1 Current frontend applications in the workspace

Section titled “2.1 Current frontend applications in the workspace”

The current workspace includes at least these active frontend codebases:

  • Frontend-Kisum
    • main user-facing product app
    • Next.js App Router
    • large shared/domain UI surface
  • Frontend-Kisum-Admin
    • platform staff control-plane UI
    • React Router 7
    • talks primarily to Backend-Kisum-Admin
  • Frontend-Kisum-Finance
    • finance-focused app
    • separate Next.js app with its own company/auth/session handling
  • Frontend-Kisum-Artists
    • standalone artist / agency app
    • live operational workflow surface backed by Backend-Kisum-Artists
  • Frontend-Kisum-Venues
    • standalone venue-management app
    • Next.js App Router
    • operations-first shell for venue CRM, bookings, contracts, deposits, operations, and reports
  • Frontend-Kisum-Promoters
    • standalone promoter persona app
    • consumes Backend-Kisum-Promoters plus Artists/Venues network BFF routes
  • Frontend-Kisum-Website
    • public Vite marketing site
    • pricing, module marketing, geo lookup, and handoff to Checkout
  • Frontend-Nextkt
    • frontend-only ticket sales prototype
    • public event discovery and event detail ticket selection UI
    • static event data until a real ticketing backend/source-of-truth is defined

This means the frontend documentation must cover both:

  • the target shared platform rules
  • the current reality that multiple frontends still exist and need alignment

Frontend-Kisum-Venues is the dedicated management surface for the venue module.

Current product contract:

  • one company manages one venue in v1
  • one venue contains many spaces
  • spaces act like lightweight BU scopes
  • multi-space bookings are supported
  • admin/superadmin finalize confirmation-sensitive and contract-sensitive actions
  • Finance write workflows remain outside the Venue frontend

This app should not be documented as a generic venue-detail reader. It is a venue-operations CRM.


Frontend may use access data for UX,
but backend remains the final authority.

This means:

  • frontend may hide a module
  • frontend may show a module
  • frontend may disable a button
  • frontend may guard a route

But if frontend accidentally shows something, backend must still enforce the real rule.


Frontend must understand that access is resolved in 3 layers:

Owned by Platform Core, resolved through Auth.

Question:

  • did the company buy this module?

Owned by Auth.

Question:

  • does this membership have this module?

Owned by Auth.

Question:

  • what actions inside the module are allowed?

Frontend must never try to compute these layers itself.

Frontend must consume the final result from Auth.


4.1 Company creation and package purchase model

Section titled “4.1 Company creation and package purchase model”

Frontend must treat company creation and package purchase as backend-owned workflows.

Frontend may collect input and show progress, but it must not invent company ids or decide subscription truth locally.

Runtime pattern:

Platform Admin UI -> Platform Admin Backend -> Core.companies

Frontend responsibilities:

  • collect company form data
  • submit to Platform Admin backend
  • show lifecycle/status returned by backend
  • after creation, optionally navigate to membership assignment UX

Self-serve company creation during checkout

Section titled “Self-serve company creation during checkout”

Runtime pattern:

Checkout UI -> Checkout/Admin Backend -> Core.companies -> subscription flow

Frontend responsibilities:

  • collect company and package selection input
  • submit to backend
  • show pending-payment state if payment is not yet finalized
  • wait for backend-confirmed status changes before unlocking tenant UX

The frontend must assume the authoritative company row exists in Core first. Memberships and access only become usable after backend flows create or activate them in Auth.


GET /auth/me
GET /auth/me/access

These are the main access/bootstrap endpoints.

Not every existing frontend app is fully standardized on this contract yet.

  • the target contract is still /auth/me plus /auth/me/access
  • admin-only UIs may also call Backend-Kisum-Admin
  • older domain apps may still carry local session/company-context logic that should eventually converge on the shared Auth contract

Used for:

  • identity
  • basic profile
  • memberships list
  • session info

Used for:

  • active company access
  • effective modules
  • permissions
  • delegation info
  • rendering decisions

Frontend calls:

POST /auth/login

Request body example:

{
"email": "user@example.com",
"password": "password123",
"accountType": "internal"
}

On successful login, frontend receives:

  • accessToken
  • refreshToken
  • expiresIn
  • tokenType

Frontend must then:

  1. store tokens securely
  2. load /auth/me
  3. determine active company
  4. call /auth/me/access
  5. bootstrap UI from returned access context

Frontend must handle:

  • 401 unauthorized
  • 403 pending_approval
  • 403 registration_rejected
  • 403 account_inactive
  • 429 rate_limited

Frontend should map these into clear user messages.

All frontend apps should converge on the same identity authority:

  • Auth is the only login authority
  • frontend apps must not invent their own user truth
  • module-specific or admin-specific frontends may vary in UI, but not in identity ownership

Use for:

  • authenticated API requests

Header:

Authorization: Bearer <accessToken>

Access token is short-lived.


Use only for:

  • POST /auth/refresh
  • POST /auth/logout

Frontend must store refresh token securely.


Frontend must not:

  • put permissions in local hardcoded state as final truth
  • assume JWT contains modules or permissions
  • infer access from JWT payload alone
  • use token contents as replacement for /auth/me/access

After login, frontend should do this sequence:

Call:

GET /auth/me
Authorization: Bearer <accessToken>

Read memberships / available companies

Select active company

Call:

GET /auth/me/access
Authorization: Bearer <accessToken>
x-org: <companyId>

Store returned access context in frontend state

Render app shell and module visibility


Frontend must always have the concept of an active company.

This is the company currently selected by the user.

That company must be sent as:

x-org: <companyId>

for all tenant-scoped requests.


When user changes company:

  1. update active company state
  2. call /auth/me/access again with new x-org
  3. replace access context in memory
  4. rerender visible modules and allowed routes
  5. update outgoing request header source

Frontend must never rely on an old company context after switch.

Access must be reloaded.


Frontend must keep these concepts separate in product and UX flows:

This belongs to Core.

Examples:

  • company buys Basic
  • company buys Finance add-on
  • company is pending_payment
  • company subscription becomes active

The frontend should read these outcomes through backend responses and later through /auth/me/access.

This belongs to Auth.

Examples:

  • user becomes member of company
  • user receives tenant role
  • user receives module grants
  • user receives permissions

Do not treat package purchase as equal to user access.

The frontend must wait for Auth-resolved access because:

effective_access = company_entitlements ∩ membership_grants

Frontend should keep these as separate state domains:

  • accessToken
  • refreshToken
  • expiresAt
  • session info
  • user id
  • name
  • email
  • globalRole
  • authType
  • memberships
  • activeCompanyId
  • company list
  • tenantRole
  • companyEnabledModules
  • membershipGrantedModules (optional if returned)
  • effectiveModules
  • permissions
  • delegation
  • tokenVersion
  • entitlementVersion
  • generatedAt

Frontend should be prepared for an access context similar to:

{
"companyId": "uuid",
"tenantRole": "ADMIN",
"companyEnabledModules": ["basic", "finance", "market"],
"membershipGrantedModules": ["basic", "finance"],
"effectiveModules": ["basic", "finance"],
"permissions": [
"basic.dashboard.view",
"basic.event.view",
"finance.expense.view",
"finance.expense.edit"
],
"delegation": {
"canBuyAddons": false,
"canManageUsers": true,
"grantableModules": ["basic"],
"grantablePermissions": [
"basic.dashboard.view",
"basic.event.view"
]
},
"meta": {
"tokenVersion": 2,
"entitlementVersion": 8,
"generatedAt": "2026-04-16T05:00:00Z"
}
}

Frontend must treat this as rendering context, not security authority.


Frontend should show module tiles / routes only if module exists in:

effectiveModules

Examples:

  • show Basic app only if basic exists
  • show Finance module only if finance exists
  • show Artist only if artist exists
  • show Venue only if venue exists
  • show AI only if ai exists
  • show Touring only if touring exists

Do not show module only because role sounds related.

Example:

  • FINANCE role does not automatically mean Finance module
  • ADMIN does not automatically mean all modules
  • USER may still have Finance if explicitly granted

Module visibility must come from effective access, not role name.


Frontend may guard routes for UX purposes.

Example:

  • /finance/* requires finance
  • /artist/* requires artist
  • /venue/* requires venue

If missing:

  • redirect
  • hide route
  • show “access denied” UX

But backend remains the final authority.


Frontend may use permissions to control UI details.

Examples:

  • show “Create Expense” button only if finance.expense.create
  • show “Edit Event” button only if basic.event.edit
  • show “Approve Contract” only if market.contract.approve

This is valid and recommended.


Permission-based UI visibility is convenience only.

Backend must still enforce.


Frontend should use delegation info to determine whether to show:

  • user management screens
  • module grant controls
  • permission grant controls
  • add-on purchase controls

Examples:

  • if canManageUsers = false, hide user-management actions
  • if canBuyAddons = false, hide upgrade/purchase actions
  • if grantableModules empty, hide module assignment UI

For tenant-scoped backend requests, frontend must send:

Authorization: Bearer <accessToken>
x-org: <companyId>

Do not send auth unless needed.


If vendor flow exists, use vendor auth model only on vendor routes.

Do not mix tenant x-org assumptions into vendor flows unless explicitly designed.


Frontend must refresh access in these cases:

  • after login
  • after token refresh
  • after company switch
  • after page reload / app restore
  • after module grant change
  • after permission grant/revoke
  • after add-on/subscription change
  • after receiving backend response that indicates access changed
  • after invitation acceptance flow if it changes membership

Frontend may cache:

  • last /auth/me
  • last /auth/me/access
  • active company id
  • visible modules
  • permission context

Possible locations:

  • in-memory state
  • app store
  • short-lived session storage (if product chooses)

Frontend must not:

  • trust cached access forever
  • assume cache beats backend
  • continue showing old company access after company switch
  • assume stale cached permissions are correct

When in doubt, refetch.


Frontend must distinguish:

Authentication problem

  • invalid token
  • expired token
  • refresh needed
  • session revoked

Typical frontend behavior:

  • refresh token if possible
  • otherwise redirect to login

Authorization problem

  • user authenticated
  • request forbidden
  • missing module / permission / scope

Typical frontend behavior:

  • show access denied
  • refresh access context if this may be stale
  • rerender UI

Client/request issue

  • missing or invalid x-org
  • malformed request

Typical frontend behavior:

  • fix request context
  • recover active company state

Upstream/system issue

  • Auth unavailable
  • Core unavailable through Auth
  • access resolution unavailable

Typical frontend behavior:

  • show temporary error state
  • allow retry
  • do not guess access

Frontend should classify routes as:

  • require JWT
  • require active company
  • require /auth/me/access
  • no auth required
  • vendor auth only
  • must be explicitly documented

Frontend should implement a central access provider/store.

Responsibilities:

  • keep current access context
  • expose helpers like hasModule() and hasPermission()
  • expose active company
  • refresh access on demand
  • clear access on logout

Frontend should centralize helpers similar to:

hasModule("finance")
hasPermission("finance.expense.view")
canManageUsers()
canBuyAddons()
isTenantRole("ADMIN")

These helpers must read from current access context, not hardcoded role assumptions.


Example for Finance page:

  1. ensure session valid
  2. ensure active company selected
  3. ensure access context loaded
  4. check hasModule("finance")
  5. if no → deny route
  6. if yes → render page
  7. inside page, show/hide actions by permission

Frontend should have explicit UI states for:

  • loading session
  • loading memberships
  • loading access
  • company selection required
  • access denied
  • temporary service unavailable
  • session expired

This avoids confusing transitions.


On logout:

  1. call POST /auth/logout
  2. clear accessToken
  3. clear refreshToken
  4. clear identity state
  5. clear access state
  6. clear active company
  7. redirect to login

If “logout all” is used:

  • same frontend clearing behavior

Frontend must never:

  • store internal API keys
  • call Platform Core directly for access decisions
  • call internal machine routes
  • trust JWT payload as full access truth
  • bypass backend checks
  • assume UI visibility is security

Frontend should not call Platform Core directly for access.

Correct pattern:

Frontend → Auth → Core

not:

Frontend → Core

Frontend may only interact with Core indirectly through Auth or admin backend flows.


27. Relationship with Base / Module Backends

Section titled “27. Relationship with Base / Module Backends”

Correct pattern:

Frontend → Base/Module Backend
Backend → Auth for enforcement

Frontend must not assume that because /auth/me/access says “yes”, backend will never reject.
Backend is still the final enforcement point.


28. Company and company data migration note

Section titled “28. Company and company data migration note”

If some legacy systems still store companies separately, frontend should still treat:

x-org = canonical company id

Do not preserve legacy mixed company-id behavior in frontend forever.

Target state:

  • one canonical company id across app

29. QA / test scenarios frontend must support

Section titled “29. QA / test scenarios frontend must support”
  • login success
  • login pending approval
  • login rejected
  • login inactive
  • token refresh flow
  • logout flow
  • company switch reloads access
  • access denied hides module
  • backend 403 updates UI correctly
  • backend 503 shows retry state
  • stale cached access gets replaced after refresh
  • invited user accepts invitation and sees new company after reload

Frontend consumes access
Frontend does not compute access
Frontend does not enforce security
Frontend does not replace backend authorization

Frontend uses Auth access context to render the app correctly,
but every real security decision is still enforced by backend services.