Global Access Users Matrix
Purpose / How to read
Section titled “Purpose / How to read”This document defines system-wide access control across all modules.
Access is determined in layers, not only roles.
Access Resolution Order
Section titled “Access Resolution Order”A request is allowed ONLY if all are true:
- Company owns the module (Platform Core)
- Membership has the module (Auth)
- Membership has permission (Auth)
- Endpoint rule allows it (Backend)
Matrix A — Module Access Preconditions (CORRECTED)
Section titled “Matrix A — Module Access Preconditions (CORRECTED)”| Package / Add-on | Module | Company Must Own | Membership Must Be Granted | Grantable By |
|---|---|---|---|---|
| Basic | basic | Yes | Yes | SA / AD (delegation rules apply) |
| Finance | finance | Yes | Yes | SA / AD (delegation rules apply) |
| Market | market | Yes | Yes | SA / AD (delegation rules apply) |
| Venue | venue | Yes | Yes | SA / AD (delegation rules apply) |
| AI | ai | Yes | Yes | SA / AD (delegation rules apply) |
| Touring | touring | Yes | Yes | SA / AD (delegation rules apply) |
Critical Rule
Section titled “Critical Rule”ANY role can access ANY moduleONLY IF:1. company owns it2. membership is granted itRoles DO NOT automatically give module access.
Matrix A.1 — Role / Delegation Meaning
Section titled “Matrix A.1 — Role / Delegation Meaning”| Role | Can Manage Users | Can Grant Modules | Can Grant Permissions | Can Buy Add-ons |
|---|---|---|---|---|
| TENANT_SUPERADMIN | Yes | Yes | Yes | Yes |
| ADMIN | Yes | Yes (restricted) | Yes (restricted) | No |
| MANAGER | Limited | Limited | Limited | No |
| USER / SUBMITTER | No | No | No | No |
Matrix B — Endpoint / Role / Scope Access
Section titled “Matrix B — Endpoint / Role / Scope Access”(Original matrix preserved below — interpreted AFTER module access is validated)
AUTH_USER_MATRIX.md (GLOBAL)
Section titled “AUTH_USER_MATRIX.md (GLOBAL)”Purpose / How to read
Section titled “Purpose / How to read”This document defines system-wide access control across all modules.
Access is determined in layers, not only roles.
Access Resolution Order
Section titled “Access Resolution Order”A request is allowed ONLY if all are true:
- Company owns the module (Platform Core)
- Membership has the module (Auth)
- Membership has permission (Auth)
- Endpoint rule allows it (Backend)
Matrix A — Package / Module / Membership Access
Section titled “Matrix A — Package / Module / Membership Access”| Package | Module | Company Entitlement | Membership Grant | SA | AD | FN | MG | SU |
|---|---|---|---|---|---|---|---|---|
| Basic | basic | Yes | Yes | Yes | Yes | Optional | Optional | Optional |
| Finance | finance | Yes | Yes | Yes | Yes | Yes | Optional | Optional |
| Market | market | Yes | Yes | Yes | Optional | No | Optional | No |
| Venue | venue | Yes | Yes | Yes | Optional | No | Optional | No |
| AI | ai | Yes | Yes | Yes | Optional | No | Optional | No |
Matrix B — Endpoint / Role / Scope Access
Section titled “Matrix B — Endpoint / Role / Scope Access”(Original Finance matrix preserved below, now interpreted as post-module access layer)
Endpoint Permission Matrix
Section titled “Endpoint Permission Matrix”Related docs: README.md (product + onboarding roles) · README_API.md (HTTP auth, x-org, routes) · README_AUTH_API.md (Kisum Auth JWT, platform vs tenant roles) · README_DOCS.md (architecture, §24 role narrative)
This file holds the endpoint × role grid (PL / SA / AD / FN / MG / SU) plus Legend notes tied to src/lib/auth.ts, src/api/bills/[id]/route.ts, and src/lib/income-access.ts. It does not replace the API reference or architecture docs — cross-links above. Canonical HTTP paths: compare with swagger-output.json after route changes.
Code helpers: isPlatformAdmin (platform JWT), isOrganizationSuperAdminRole (tenant SA), isCompanyAdmin (company AD — includes both Auth ADMIN and TENANT_SUPERADMIN mappings to Finance CompanyRole.ADMIN; the SA column below is only TENANT_SUPERADMIN raw role), isCompanyFinance (FN — company membership only; there is no platform globalRole Finance), approver (MG), submitter (SU).
Legend
Section titled “Legend”| Shorthand | Meaning | Kisum Auth / Finance |
|---|---|---|
| PL | Platform staff — JWT globalRole is PLATFORM_SUPERADMIN, PLATFORM_ADMIN, or PLATFORM_MODERATOR. | isPlatformAdmin / isGlobalSuperAdminRole |
| SA | Tenant org superadmin — company_memberships.authCompanyRole === 'TENANT_SUPERADMIN' for that organization. | isOrganizationSuperAdminRole |
| AD | Company admin — Auth company role ADMIN (not the SA raw role). Maps to Finance CompanyRole.ADMIN alongside SA, but AD column = ordinary company admin unless the row is SA-specific. | isCompanyAdmin-style checks excluding SA-only powers where the row splits them |
| FN | Finance — company company_memberships.role === 'FINANCE' (organization level). Not a platform JWT role. | isCompanyFinance |
| MG | Manager — BU APPROVER (and related). | isApproverForBU |
| SU | Submitter — BU SUBMITTER (and invoiceViewScope). | isSubmitterForBU |
All = all records across system | Own Co = only their company | Own BU = only their business unit(s) | Own = only their own records
— = no access / not applicable for that column | Auth = any authenticated user
Tenant header (x-org)
Section titled “Tenant header (x-org)”Internal (non-vendor) API calls often use x-org to select the active organization. Allowed values: Finance Company.id (UUID), or kisumCompanyId when linked — resolved in src/lib/active-company.ts. Policy: src/lib/x-org-route-policy.ts.
| # | Endpoint | PL | SA | AD | FN | MG | SU | Status |
|---|---|---|---|---|---|---|---|---|
| ADMIN — COMPANIES | ||||||||
| 1 | GET /api/admin/companies | All, full detail | Own Co, full detail | Own Co, full detail | All, full detail | Own Co, limited (own BUs only, no stats) | Own Co, limited (own BUs only, no stats) | Correct |
| 2 | POST /api/admin/companies | Yes | Submit; platform approves | — | — | — | — | Correct |
| 3 | PUT /api/admin/companies | Yes | Own Co | — | — | — | — | Correct |
| 4 | DELETE /api/admin/companies | Yes | Delete; platform approves | — | — | — | — | Correct |
| 5 | GET /api/admin/companies/[id]/kisum | All | Own Co | Own Co | Own Co | — | — | Correct |
| 6 | POST /api/admin/companies/[id]/kisum | All | Own Co | Own Co | Own Co | — | — | Correct |
| 7 | DELETE /api/admin/companies/[id]/kisum | All | Own Co | Own Co | Own Co | — | — | Correct |
| 8 | GET /api/admin/companies/[id]/xero | All | Own Co | Own Co | Own Co | — | — | Correct (excluded) |
| 9 | POST /api/admin/companies/[id]/xero | All | Own Co | Own Co | Own Co | — | — | Correct (excluded) |
| 10 | DELETE /api/admin/companies/[id]/xero | All | Own Co | Own Co | Own Co | — | — | Correct (excluded) |
| ADMIN — BUSINESS UNITS | ||||||||
| 11 | GET /api/admin/business-units | All | Own Co, all BUs | Own Co, all BUs | All | Own BU(s) only | Own BU(s) only | Correct |
| 12 | GET /api/admin/business-units/by-company | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 13 | POST /api/admin/business-units | All | Own Co | Own Co | — | — | — | Correct |
| 14 | PUT /api/admin/business-units | All | Own Co | Own Co | — | — | — | Correct |
| 15 | DELETE /api/admin/business-units | All | Own Co | Own Co | — | — | — | Correct |
| ADMIN — USERS | ||||||||
| 16 | GET /api/admin/users | All | Own Co | Own Co | All (read-only) | Own BU(s) | — | Correct |
| 17 | POST /api/admin/users | All | Own Co | Own Co | — | Own BU (submitters only) | — | Correct |
| 18 | PUT /api/admin/users | All | Own Co | Own Co | — | Own BU (submitters only) | — | Correct |
| 19 | DELETE /api/admin/users | Full delete | Own Co/BU removal | Own Co/BU removal | — | Own BU removal only | — | Correct |
| 20 | POST /api/admin/users/[id]/reset-password | Yes | — | — | — | — | — | Correct |
| ADMIN — VENDORS | ||||||||
| 21 | GET /api/admin/vendors | All | Own Co invoice-scoped | Own Co invoice-scoped | All invoice-scoped | Own BU invoice-scoped | — | Correct |
| 22 | GET /api/admin/vendors/search | All | Own Co scoped | Own Co scoped | All scoped | Own BU scoped | — | Correct |
| 23 | POST /api/admin/vendors | All | Own Co | Own Co | — | Own Co | — | Correct |
| 24 | GET /api/admin/vendors/[id] | All | Own Co invoice-scoped | Own Co invoice-scoped | All invoice-scoped | Own BU invoice-scoped | — | Correct |
| 25 | PUT /api/admin/vendors/[id] | All | Own Co scoped | Own Co scoped | — | Own Co scoped | — | Correct |
| 26 | DELETE /api/admin/vendors/[id] | Yes | — | — | — | — | — | Correct |
| 27 | GET /api/admin/vendors/[id]/dashboard | All | Own Co invoice-scoped | Own Co invoice-scoped | All invoice-scoped | Own BU invoice-scoped | — | Correct |
| 28 | POST /api/admin/vendors/[id]/link-company | All | Own Co | Own Co | — | — | — | Correct |
| 29 | PUT /api/admin/vendors/[id]/link-company | All | Own Co | Own Co | — | — | — | Correct |
| 30 | DELETE /api/admin/vendors/[id]/link-company | All | Own Co | Own Co | — | — | — | Correct |
| 31 | POST /api/admin/vendors/[id]/activate-portal | Yes | — | — | Yes | — | — | Correct |
| 32 | DELETE /api/admin/vendors/[id]/activate-portal | Yes | — | — | — | — | — | Correct |
| ADMIN — OTHER | ||||||||
| 33 | GET /api/admin/stats | Governance + scoped stats with x-org | Own Co | Own Co | All | Own BU | Own invoice scope | Correct |
| 34 | GET /api/admin/audit-logs | All | Own Co | Own Co | All | — | — | Correct |
| 35 | GET /api/admin/sync-errors | Yes | — | — | — | — | — | Correct |
| 36 | GET /api/admin/kisum/status | Yes | — | — | — | — | — | Correct |
| 37 | PATCH /api/admin/expense-categories/[id] | Yes | — | — | — | — | — | Correct |
| 37b | POST /api/admin/expense-categories | Yes | Own Co | Own Co | — | — | — | Correct |
| 37c | GET /api/admin/customers/search | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 37d | POST /api/admin/income-categories | Yes | Own Co | Own Co | — | — | — | Correct |
| 37e | `PATCH | PUT /api/admin/income-categories/[id]` | Yes | Own Co | Own Co | — | — | — |
| BILLS (expense / AP) | ||||||||
| 38 | GET /api/bills | — | Own Co | Own Co | All | Own BU | Own / Own BU / Own Co (per invoiceViewScope) | Correct |
| 39 | POST /api/bills | — | Own Co | Own Co | Own Co: create (BU if required) | Own BU (managers if no submitters) | Own BU (submitter) | Correct |
| 40 | GET /api/bills/[id] | — | Own Co | Own Co | All | Own BU | Own / scoped (per invoiceViewScope) | Correct |
| 41 | PATCH /api/bills/[id] | — | Escalation chain + privileged update (org supreme) | Approve, reject, urgent, pay, update (within admin rules) | Mark paid, update (privileged on APPROVED/paid) | Approve BU, reject, urgent; waterfall update | Submit, update own / scoped | Correct |
| 42 | GET /api/bills/[id]/signed-pdf | — | Own Co | Own Co | All | Own BU | Own / scoped | Correct |
| 43 | POST /api/bills/[id]/sync-errors | Yes (ops) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | — | — | Correct |
| 44 | POST /api/bills/[id]/xero-align-payment-ids | — | Own Co (canMarkPaid) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | — | — | Correct |
| 45 | POST /api/bills/[id]/xero-align-and-create-payment | — | Own Co (canMarkPaid) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | — | — | Correct |
| 46 | POST /api/bills/[id]/xero-rebuild-local-master | — | Own Co (canMarkPaid) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | — | — | Correct |
| 47 | POST /api/bills/moved-merge | Yes (internal bearer) | — | — | — | — | — | Correct |
| INCOME (revenue / AR) | ||||||||
| 48 | `GET | POST /api/income` | — | Own Co | Own Co | All | — | — |
| 49 | `GET | PATCH | DELETE /api/income/[id]` | — | Own Co | Own Co | All | — |
| 50 | POST /api/income/[id]/payments | — | Own Co | Own Co | All | — | — | Correct |
| 51 | `PUT | DELETE /api/income/[id]/payments/[paymentId]` | — | Own Co | Own Co | All | — | — |
| 52 | GET /api/income/reconcile-xero | — | Own Co | Own Co | All | — | — | Correct |
| 53 | GET /api/income-categories | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| KISUM | ||||||||
| 54 | GET /api/kisum/[co] | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 55 | GET /api/kisum/[co]/events | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 56 | GET /api/kisum/[co]/events/[ev] | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 57 | GET /api/kisum/[co]/events/[ev]/invoices | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 58 | GET /api/kisum/[co]/events/[ev]/invoices/highlights | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 59 | GET /api/kisum/[co]/events/[ev]/invoices/[inv] | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 60 | GET /api/kisum/[co]/events/[ev]/income/highlight | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 61 | GET /api/kisum/[co]/events/[ev]/income/summary | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 62 | GET /api/kisum/event-expense/find-by-event/[id] | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| 63 | GET /api/kisum/expense-categories | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| NOTIFICATIONS | ||||||||
| 64 | GET /api/notifications | Own | Own | Own | Own | Own | Own | Correct |
| 65 | PATCH /api/notifications | Own | Own | Own | Own | Own | Own | Correct |
| FILES & UPLOADS | ||||||||
| 66 | POST /api/upload | — | Own Co/BU | Own Co/BU | Own Co/BU | Own Co/BU | Own Co/BU | Correct |
| 67 | POST /api/upload/payment-slip | Yes (ops) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | Own Co (canMarkPaid) | — | — | Correct |
| 68 | POST /api/files/ai | Yes | Yes | Yes | Yes | Yes | Yes (membership) | Correct |
| 69 | GET /api/files/[id]/signed-url | — | Own Co | Own Co | All | Own BU | Own / scoped | Correct |
| DASHBOARD & EXCHANGE | ||||||||
| 70 | GET /api/dashboard/convert | — | Own Co | Own Co | All | Own BU | Own invoice scope | Correct |
| 71 | GET /api/exchange-rates/convert | Auth | Auth | Auth | Auth | Auth | Auth | Correct |
| 72 | POST /api/exchange-rates/convert-batch | Auth | Auth | Auth | Auth | Auth | Auth | Correct |
| EXPENSE CATEGORIES | ||||||||
| 73 | GET /api/expense-categories | All | Own Co | Own Co | All | Own Co | Own Co | Correct |
| COMPANIES (non-admin) | ||||||||
| 74 | GET /api/companies/[id]/kisum-link | — | Own Co | Own Co | All | Own Co | Own Co | Correct |
| XERO (excluded from security fixes) | ||||||||
| 75 | GET /api/xero/connect | — | Own Co | Own Co | Own Co | — | — | Correct |
| 76 | GET /api/xero/callback | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 77 | GET /api/xero/tenants | — | Own Co | Own Co | Own Co | — | — | Correct |
| 78 | PUT /api/xero/tenants | — | Own Co | Own Co | Own Co | — | — | Correct |
| 79 | POST /api/xero/sync | — | Own Co | Own Co | Own Co | — | — | Correct |
| 80 | GET /api/xero/sync | Public | Public | Public | Public | Public | Public | Correct |
| 81 | GET /api/xero/currencies | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 82 | GET /api/xero/bank-accounts | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 83 | GET /api/xero/liability-accounts | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 84 | GET /api/xero/revenue-accounts | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 85 | GET /api/xero/tax-rates | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 86 | GET /api/xero/expense-accounts (legacy: /api/xero/accounts) | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 87 | GET /api/xero/expense-accounts/cached (legacy: /api/xero/accounts/cached) | Auth | Auth | Auth | Auth | Auth | Auth | Correct (excluded) |
| 88 | `GET | POST /api/xero/import-from-xero` | Auth | Auth | Auth | Auth | Auth | Auth |
| 89 | `GET | POST /api/xero/sync/cron` | Auth | Auth | Auth | Auth | Auth | Auth |
| CRON | ||||||||
| 90 | POST /api/cron/invoice-reminders | CRON_SECRET | — | — | — | — | — | Correct |
| 91 | GET /api/cron/invoice-reminders | CRON_SECRET | — | — | — | — | — | Correct |
| AUTH (Finance API) | ||||||||
| 92 | GET /api/auth/profile | Own | Own | Own | Own | Own | Own | Correct |
| AUTH (Kisum Auth service) — not implemented on this API; use Auth base URL | ||||||||
| — | POST /auth/login, POST /auth/refresh, password reset, etc. | Public on Auth | Public on Auth | Public on Auth | Public on Auth | Public on Auth | Public on Auth | See README_AUTH_API.md |
| VENDOR AUTH (public, separate auth domain) | ||||||||
| 93 | POST /api/auth/vendor/login | Public | Public | Public | Public | Public | Public | Correct |
| 94 | POST /api/auth/vendor/forgot-password | Public | Public | Public | Public | Public | Public | Correct |
| VENDOR PORTAL (vendor JWT required) | ||||||||
| 95 | GET /api/vendor/me | — | — | — | — | — | Own (vendor) | Correct |
| 96 | PATCH /api/vendor/me | — | — | — | — | — | Own (vendor) | Correct |
| 97 | POST /api/vendor/me/password | — | — | — | — | — | Own (vendor) | Correct |
| 98 | GET /api/vendor/users | — | — | — | — | — | Vendor SA only | Correct |
| 99 | POST /api/vendor/users | — | — | — | — | — | Vendor SA only | Correct |
| 100 | PUT /api/vendor/users | — | — | — | — | — | Vendor SA only | Correct |
| 101 | GET /api/vendor/invoices | — | — | — | — | — | Own vendor | Correct |
| 102 | POST /api/vendor/invoices | — | — | — | — | — | Own vendor | Correct |
| 103 | GET /api/vendor/invoices/[id] | — | — | — | — | — | Own vendor | Correct |
| 104 | PUT /api/vendor/invoices/[id] | — | — | — | — | — | Own vendor | Correct |
| 105 | GET /api/vendor/stats | — | — | — | — | — | Own vendor | Correct |
| 106 | POST /api/vendor/upload | — | — | — | — | — | Own vendor | Correct |
| DOCS / OPENAPI | ||||||||
| 107 | GET /api/docs | Public | Public | Public | Public | Public | Public | Correct |
| 108 | GET /api/openapi | Public | Public | Public | Public | Public | Public | Correct |
Bills PATCH (row 41) — detail
Section titled “Bills PATCH (row 41) — detail”- PL: no tenant bill actions on platform JWT alone.
- SA:
TENANT_SUPERADMIN— completes escalation / full chain in one step where applicable; privileged update on APPROVED/paid (amount locked after APPROVED); override-class actions persrc/api/bills/[id]/route.ts. - AD: Company admin — approve, reject, urgent, record payment, update per limits / waterfall; not the same as SA escalation supremacy.
- FN / MG / SU: As in
canProvideNextApproval,canMarkPaid, and waterfall rules.
Total: 108 numbered rows (logical endpoints / route groups), plus one Kisum Auth reference row. Verify against swagger-output.json when changing routes.