Backend Core API Contract (OpenAPI-Style)
Contract Specification for Platform Core Internal API
Section titled “Contract Specification for Platform Core Internal API”Audience: backend engineers, platform-admin backend engineers, Auth backend engineers, QA, DevOps, integrators
Status: contract specification
Scope: this document defines the API contract for Platform Core. It focuses on:
- exact endpoints
- methods
- required headers
- auth model
- request parameters
- request bodies
- response bodies
- error responses
- examples
- integration rules
This is the API contract companion to:
- 2.-Backend-implementation.md → master backend plan
- 2.1.-Backend-Auth.md → Auth backend/API specification
- 2.2.-Backend-Core.md → full engineering/backend specification
0. API overview
Section titled “0. API overview”Platform Core is an internal-only backend service.
It is not intended for direct browser/frontend access.
Its main responsibilities are:
- returning company commercial entitlements
- returning catalog definitions (modules/packages/add-ons)
- storing and updating package/add-on/subscription state
- bumping entitlement versions for Auth cache invalidation
0.1 Runtime position in the platform
Section titled “0.1 Runtime position in the platform”Platform Core is the commercial entitlement backend.
It is positioned in the runtime architecture as follows:
- Auth Backend = identity, memberships, grants, permissions, delegation
- Platform Core Backend = packages, add-ons, subscriptions, company entitlements
- Base / Module Backends = business logic and enforcement
- Frontend = consumes resolved access for UX, but is not the source of truth
Main runtime rule
Section titled “Main runtime rule”Platform Core does not compute final user access.
The main access rule remains:
effective_access = company_entitlements ∩ membership_grantsWhere:
company_entitlementscome from Platform Coremembership_grantscome from Auth- final
effective_accessis computed by Auth
Important implication
Section titled “Important implication”Platform Core must not be used by frontend or business backends as a replacement for Auth when making user-level access decisions.
Platform Core only answers:
What does this company commercially own right now?
It does not answer:
- whether a specific user can use a module
- whether a specific permission is granted
- whether a specific actor can delegate access
0.2 Request ownership and primary callers
Section titled “0.2 Request ownership and primary callers”Frontend should not call Platform Core directly
Section titled “Frontend should not call Platform Core directly”Frontend should normally call:
- Auth Backend
- Base Backend
- module backends
- admin backend (where applicable)
Frontend should not call Platform Core directly to determine access.
Primary caller
Section titled “Primary caller”The primary caller of Platform Core is:
- Auth Backend
Especially for:
GET /auth/me/access- company-switch access refresh
- access recomputation after entitlement changes
Secondary callers
Section titled “Secondary callers”Allowed secondary callers:
- Platform Admin Backend
- approved internal jobs/services
Non-primary callers
Section titled “Non-primary callers”Business backends such as Base / Finance / Market / Touring / Venue / AI should not call Platform Core to compute final user authorization.
If a business backend needs user-level authorization, it must use Auth-resolved access context, not Core entitlements directly.
Clean runtime pattern
Section titled “Clean runtime pattern”Frontend -> Auth -> Auth DB -> Platform Core <- resolved access resultThis keeps Auth as the access aggregation layer and keeps Core focused on commercial entitlement truth.
0.3 Naming clarification
Section titled “0.3 Naming clarification”To avoid confusion across teams, use these terms consistently:
- Basic Package → commercial product / subscription concept
- Core App → the frontend/base application experience
- Basic Backend → business backend for core platform features
- Platform Core Backend → commercial entitlement backend
This avoids mixing:
- Basic Package
- Basic Backend
- Platform Core Backend
which are not the same thing.
1. Base URL and exposure model
Section titled “1. Base URL and exposure model”1.1 Recommended base URL
Section titled “1.1 Recommended base URL”https://core.kisum.io1.2 Exposure model
Section titled “1.2 Exposure model”All routes in this spec are internal routes.
Allowed callers:
- Auth Backend
- Platform Admin Backend
- approved internal jobs/services
Not allowed:
- public frontend
- public mobile apps
- arbitrary public clients
Integration note
Section titled “Integration note”Platform Core is an internal service in the middle of an access-resolution chain.
Typical runtime chain:
Frontend -> Auth -> Platform Coreor for admin changes:
Platform Admin UI -> Platform Admin Backend -> Platform CoreBusiness backends should not bypass Auth and use Core as a user-authorization oracle.
2. Content type and response conventions
Section titled “2. Content type and response conventions”2.1 Request content type
Section titled “2.1 Request content type”All request bodies use:
Content-Type: application/jsonunless explicitly noted otherwise.
2.2 Success envelope
Section titled “2.2 Success envelope”All successful responses return:
{ "success": true, "data": {}}2.3 Error envelope
Section titled “2.3 Error envelope”All error responses return:
{ "success": false, "error": { "code": "string_code", "message": "Human readable message" }}2.4 Standard error codes
Section titled “2.4 Standard error codes”Common error codes used by Platform Core:
unauthorizedforbiddenvalidation_errornot_foundconflictnot_readyinternal_errorservice_unavailable
3. Internal authentication contract
Section titled “3. Internal authentication contract”3.1 Required authentication
Section titled “3.1 Required authentication”All /internal/* routes must require internal service authentication.
Recommended first implementation:
X-Internal-API-Key: <secret>Alternative future implementation:
Authorization: Bearer <internal-service-token>3.2 Required behavior
Section titled “3.2 Required behavior”If internal auth is missing or invalid:
- return
401 unauthorized - do not expose data
- do not leak internal routing details
Example error
Section titled “Example error”{ "success": false, "error": { "code": "unauthorized", "message": "missing or invalid internal credentials" }}4. Common object schemas
Section titled “4. Common object schemas”4.1 Module object
Section titled “4.1 Module object”{ "id": "uuid", "key": "finance", "name": "Finance", "type": "addon", "description": "Finance module", "isActive": true}Fields
Section titled “Fields”id: UUIDkey: canonical module keyname: display nametype:baseoraddondescription: optional descriptionisActive: boolean
4.2 Package object
Section titled “4.2 Package object”{ "id": "uuid", "key": "basic", "name": "Basic", "description": "Basic subscription that enables Core App", "isActive": true, "modules": ["basic"]}Fields
Section titled “Fields”idkeynamedescriptionisActivemodules: array of module keys currently mapped to this package
4.3 Add-on object
Section titled “4.3 Add-on object”{ "id": "uuid", "key": "finance", "name": "Finance", "description": "Finance add-on", "isActive": true, "modules": ["finance"]}4.4 Company add-on summary
Section titled “4.4 Company add-on summary”{ "key": "finance", "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z"}4.5 Company entitlement object
Section titled “4.5 Company entitlement object”{ "companyId": "cmp_001", "hasBasic": true, "basePackage": "basic", "addons": [ { "key": "finance", "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z" } ], "enabledModules": ["basic", "finance"], "entitlementVersion": 8, "updatedAt": "2026-04-16T05:00:00Z"}4.6 Entitlement history object
Section titled “4.6 Entitlement history object”{ "id": "uuid", "changeType": "addon_activated", "entityType": "addon", "entityKey": "finance", "previousStatus": "inactive", "newStatus": "active", "source": "platform_admin", "changedBy": "admin_user_uuid", "createdAt": "2026-04-16T05:00:00Z"}5. Health endpoints
Section titled “5. Health endpoints”5.1 GET /health
Section titled “5.1 GET /health”Purpose
Section titled “Purpose”Simple liveness probe.
No internal auth required, unless your infra policy requires all routes behind private network anyway.
Response — 200
Section titled “Response — 200”{ "success": true, "data": { "status": "ok" }}5.2 GET /ready
Section titled “5.2 GET /ready”Purpose
Section titled “Purpose”Readiness probe.
Readiness checks
Section titled “Readiness checks”Must verify:
- Core DB reachable
- required config loaded
- service initialized
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "status": "ready" }}Failure — 503
Section titled “Failure — 503”{ "success": false, "error": { "code": "not_ready", "message": "core database unavailable" }}6. Catalog read endpoints
Section titled “6. Catalog read endpoints”6.1 GET /internal/catalog/modules
Section titled “6.1 GET /internal/catalog/modules”Purpose
Section titled “Purpose”Return full module catalog.
Internal auth required.
Who can call
Section titled “Who can call”- Platform Admin Backend
- Auth Backend (optional)
- internal jobs
Query params
Section titled “Query params”None.
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "modules": [ { "id": "11111111-1111-1111-1111-111111111111", "key": "basic", "name": "Core App", "type": "base", "description": "Core App / Basic product module", "isActive": true }, { "id": "22222222-2222-2222-2222-222222222222", "key": "finance", "name": "Finance", "type": "addon", "description": "Finance module", "isActive": true } ] }}Errors
Section titled “Errors”401 unauthorized500 internal_error
6.2 GET /internal/catalog/packages
Section titled “6.2 GET /internal/catalog/packages”Purpose
Section titled “Purpose”Return full package catalog.
Internal auth required.
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "packages": [ { "id": "33333333-3333-3333-3333-333333333333", "key": "basic", "name": "Basic", "description": "Basic subscription that enables Core App", "isActive": true, "modules": ["basic"] } ] }}Errors
Section titled “Errors”401 unauthorized500 internal_error
6.3 GET /internal/catalog/addons
Section titled “6.3 GET /internal/catalog/addons”Purpose
Section titled “Purpose”Return full add-on catalog.
Internal auth required.
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "addons": [ { "id": "44444444-4444-4444-4444-444444444444", "key": "finance", "name": "Finance", "description": "Finance add-on", "isActive": true, "modules": ["finance"] }, { "id": "55555555-5555-5555-5555-555555555555", "key": "market", "name": "Market", "description": "Market add-on", "isActive": true, "modules": ["market"] } ] }}Errors
Section titled “Errors”401 unauthorized500 internal_error
7. Entitlement read endpoints
Section titled “7. Entitlement read endpoints”7.1 GET /internal/companies/{companyId}/entitlements
Section titled “7.1 GET /internal/companies/{companyId}/entitlements”Purpose
Section titled “Purpose”Return the current normalized commercial entitlement state for one company.
Main consumer
Section titled “Main consumer”Auth Backend, during /auth/me/access.
Runtime role of this endpoint
Section titled “Runtime role of this endpoint”This endpoint returns company commercial entitlement state only.
It is intentionally designed for Auth to consume and merge with membership grants.
It must not be treated as a user-access endpoint by callers.
Internal auth required.
Who can call
Section titled “Who can call”- Auth Backend
- Platform Admin Backend
- internal jobs
Path params
Section titled “Path params”companyId— required UUID
Validation rules
Section titled “Validation rules”companyIdmust be valid UUID- if company has no active Basic and no active add-ons, response still succeeds with empty entitlement state
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "hasBasic": false, "basePackage": null, "addons": [ { "key": "finance", "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z" }, { "key": "market", "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z" } ], "enabledModules": ["finance", "market"], "entitlementVersion": 7, "updatedAt": "2026-04-16T05:00:00Z" }}Example: Basic + Finance
Section titled “Example: Basic + Finance”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "hasBasic": true, "basePackage": "basic", "addons": [ { "key": "finance", "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z" } ], "enabledModules": ["basic", "finance"], "entitlementVersion": 8, "updatedAt": "2026-04-16T05:00:00Z" }}Example: No entitlements
Section titled “Example: No entitlements”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "hasBasic": false, "basePackage": null, "addons": [], "enabledModules": [], "entitlementVersion": 1, "updatedAt": "2026-04-16T05:00:00Z" }}Errors
Section titled “Errors”400 validation error
Section titled “400 validation error”{ "success": false, "error": { "code": "validation_error", "message": "invalid companyId" }}401 unauthorized
Section titled “401 unauthorized”{ "success": false, "error": { "code": "unauthorized", "message": "missing or invalid internal credentials" }}404 not found
Section titled “404 not found”Use only if your system requires company existence to be pre-validated in Core.
{ "success": false, "error": { "code": "not_found", "message": "company not found" }}500 internal error
Section titled “500 internal error”{ "success": false, "error": { "code": "internal_error", "message": "failed to load entitlements" }}7.2 GET /internal/companies/{companyId}/subscription-summary
Section titled “7.2 GET /internal/companies/{companyId}/subscription-summary”Purpose
Section titled “Purpose”Return admin-friendly company subscription summary.
Internal auth required.
Who can call
Section titled “Who can call”- Platform Admin Backend
- internal jobs
- Auth Backend (optional)
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "hasBasic": true, "basePackage": "basic", "addons": ["finance", "market"], "status": "active", "entitlementVersion": 7 }}statushere is summary-level, not intended to replace detailed addon/package statuses- useful for admin dashboards and company overview pages
7.3 GET /internal/companies/{companyId}/history
Section titled “7.3 GET /internal/companies/{companyId}/history”Purpose
Section titled “Purpose”Return entitlement history for a company.
Internal auth required.
Who can call
Section titled “Who can call”- Platform Admin Backend
- internal audit/reporting jobs
Query params (optional)
Section titled “Query params (optional)”limitoffset
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "history": [ { "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "changeType": "addon_activated", "entityType": "addon", "entityKey": "finance", "previousStatus": "inactive", "newStatus": "active", "source": "platform_admin", "changedBy": "admin_user_uuid", "createdAt": "2026-04-16T05:00:00Z" } ] }}8. Catalog write endpoints
Section titled “8. Catalog write endpoints”These are usually called by the Platform Admin Backend, not directly from UI.
8.1 POST /internal/catalog/modules
Section titled “8.1 POST /internal/catalog/modules”Purpose
Section titled “Purpose”Create a new module catalog entry.
Internal auth required.
Who can call
Section titled “Who can call”- Platform Admin Backend only
Request body
Section titled “Request body”{ "key": "finance", "name": "Finance", "type": "addon", "description": "Finance module", "isActive": true}Validation rules
Section titled “Validation rules”keyrequiredkeyuniquekeylowercase slug recommendednamerequiredtyperequired and must bebaseoraddon
Success — 201
Section titled “Success — 201”{ "success": true, "data": { "id": "22222222-2222-2222-2222-222222222222", "key": "finance", "name": "Finance", "type": "addon", "description": "Finance module", "isActive": true }}Conflict — 409
Section titled “Conflict — 409”{ "success": false, "error": { "code": "conflict", "message": "module key already exists" }}8.2 PATCH /internal/catalog/modules/{moduleId}
Section titled “8.2 PATCH /internal/catalog/modules/{moduleId}”Purpose
Section titled “Purpose”Update existing module metadata.
Internal auth required.
Path params
Section titled “Path params”moduleId— required UUID
Request body
Section titled “Request body”All fields optional, but at least one required:
{ "name": "Finance", "description": "Finance module updated", "isActive": true}keyshould be immutable after creation in first version- patch should update
updated_at
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "id": "22222222-2222-2222-2222-222222222222", "key": "finance", "name": "Finance", "type": "addon", "description": "Finance module updated", "isActive": true }}8.3 POST /internal/catalog/packages
Section titled “8.3 POST /internal/catalog/packages”Purpose
Section titled “Purpose”Create a new package.
Internal auth required.
Request body
Section titled “Request body”{ "key": "basic", "name": "Basic", "description": "Basic subscription that enables Core App", "isActive": true, "moduleKeys": ["basic"]}Validation rules
Section titled “Validation rules”keyuniquemoduleKeysmust all exist inmodules- package may map to one or more modules
Success — 201
Section titled “Success — 201”{ "success": true, "data": { "id": "33333333-3333-3333-3333-333333333333", "key": "basic", "name": "Basic", "description": "Basic subscription that enables Core App", "isActive": true, "modules": ["basic"] }}8.4 PATCH /internal/catalog/packages/{packageId}
Section titled “8.4 PATCH /internal/catalog/packages/{packageId}”Purpose
Section titled “Purpose”Update package metadata and/or mapping.
Path params
Section titled “Path params”packageId— UUID
Request body
Section titled “Request body”{ "name": "Basic", "description": "Updated Basic description", "isActive": true, "moduleKeys": ["basic"]}Behavior
Section titled “Behavior”- if
moduleKeysis present, replace package mapping set with provided keys - mapping updates may require entitlement-version impact handling for active companies
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "id": "33333333-3333-3333-3333-333333333333", "key": "basic", "name": "Basic", "description": "Updated Basic description", "isActive": true, "modules": ["basic"] }}8.5 POST /internal/catalog/addons
Section titled “8.5 POST /internal/catalog/addons”Purpose
Section titled “Purpose”Create a new add-on.
Request body
Section titled “Request body”{ "key": "finance", "name": "Finance", "description": "Finance add-on", "isActive": true, "moduleKeys": ["finance"]}Success — 201
Section titled “Success — 201”{ "success": true, "data": { "id": "44444444-4444-4444-4444-444444444444", "key": "finance", "name": "Finance", "description": "Finance add-on", "isActive": true, "modules": ["finance"] }}8.6 PATCH /internal/catalog/addons/{addonId}
Section titled “8.6 PATCH /internal/catalog/addons/{addonId}”Purpose
Section titled “Purpose”Update add-on metadata and/or mapping.
Path params
Section titled “Path params”addonId— UUID
Request body
Section titled “Request body”{ "name": "Finance", "description": "Finance updated", "isActive": true, "moduleKeys": ["finance"]}Success — 200
Section titled “Success — 200”{ "success": true, "data": { "id": "44444444-4444-4444-4444-444444444444", "key": "finance", "name": "Finance", "description": "Finance updated", "isActive": true, "modules": ["finance"] }}9. Company entitlement write endpoints
Section titled “9. Company entitlement write endpoints”9.1 POST /internal/companies/{companyId}/basic
Section titled “9.1 POST /internal/companies/{companyId}/basic”Purpose
Section titled “Purpose”Create or update Basic subscription state for a company.
Internal auth required.
Who can call
Section titled “Who can call”- Platform Admin Backend
- optional billing/reconciliation jobs
Path params
Section titled “Path params”companyId— UUID
Request body
Section titled “Request body”{ "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z", "source": "platform_admin", "externalReference": "sub_123"}Valid status values
Section titled “Valid status values”activeinactivecancelledexpiredtrialpaused
Behavior
Section titled “Behavior”- resolve package
basic - upsert
company_subscriptions - bump
entitlementVersion - write entitlement history
- return normalized summary
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "hasBasic": true, "basePackage": "basic", "entitlementVersion": 8 }}Example deactivation request
Section titled “Example deactivation request”{ "status": "inactive", "source": "platform_admin"}9.2 POST /internal/companies/{companyId}/addons
Section titled “9.2 POST /internal/companies/{companyId}/addons”Purpose
Section titled “Purpose”Create or update add-on state for one company/add-on pair.
Internal auth required.
Who can call
Section titled “Who can call”- Platform Admin Backend
- optional billing/reconciliation jobs
Path params
Section titled “Path params”companyId— UUID
Request body
Section titled “Request body”{ "addonKey": "finance", "status": "active", "startsAt": "2026-04-16T00:00:00Z", "endsAt": "2026-05-16T00:00:00Z", "source": "platform_admin", "externalReference": "addon_sub_123"}Behavior
Section titled “Behavior”- resolve addon by
addonKey - upsert
company_addons - bump
entitlementVersion - write entitlement history
- return normalized addon result
Success — 200
Section titled “Success — 200”{ "success": true, "data": { "companyId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "addonKey": "finance", "status": "active", "entitlementVersion": 9 }}Example deactivate addon
Section titled “Example deactivate addon”{ "addonKey": "finance", "status": "inactive", "source": "platform_admin"}Validation errors
Section titled “Validation errors”Missing addonKey
Section titled “Missing addonKey”{ "success": false, "error": { "code": "validation_error", "message": "addonKey is required" }}Unknown addonKey
Section titled “Unknown addonKey”{ "success": false, "error": { "code": "not_found", "message": "addon not found" }}10. Versioning and invalidation contract
Section titled “10. Versioning and invalidation contract”10.1 Version source
Section titled “10.1 Version source”Core returns entitlementVersion in all company entitlement responses.
10.2 Mandatory version bumps
Section titled “10.2 Mandatory version bumps”Core must bump entitlementVersion when:
- Basic activated
- Basic deactivated
- Basic expired
- add-on activated
- add-on deactivated
- add-on expired
- package mapping changed in a way affecting active companies
- add-on mapping changed in a way affecting active companies
- manual entitlement repair
10.3 Contract expectation for Auth
Section titled “10.3 Contract expectation for Auth”Auth will use:
access:{companyId}:{membershipId}:{accessVersion}:{entitlementVersion}or equivalent cache strategy.
Core does not manage Auth cache directly via this API contract.
But Core must always return the correct current version.
11. Request/response behavior rules
Section titled “11. Request/response behavior rules”11.1 Time format
Section titled “11.1 Time format”All timestamps are ISO-8601 UTC strings, example:
2026-04-16T05:00:00Z11.2 Nullability
Section titled “11.2 Nullability”basePackagemay benullstartsAt/endsAtmay benull- arrays should be empty arrays, not null, unless there is a strong reason otherwise
11.3 Normalization
Section titled “11.3 Normalization”Responses must be normalized and not leak DB implementation details.
{ "addons": [ { "key": "finance", "status": "active" } ]}{ "addons": [ { "addon_id": "uuid", "company_addon_status": "A" } ]}12. HTTP status map
Section titled “12. HTTP status map”| Status | Meaning |
|---|---|
| 200 | Successful read/update |
| 201 | Successful creation |
| 400 | Validation error |
| 401 | Missing/invalid internal auth |
| 403 | Authenticated internal caller but not allowed |
| 404 | Resource not found |
| 409 | Conflict |
| 500 | Internal server error |
| 503 | Dependency not ready / service unavailable |
13. Typical integration flows
Section titled “13. Typical integration flows”13.1 Auth access aggregation flow
Section titled “13.1 Auth access aggregation flow”- Frontend calls
/auth/me/access - Auth loads user grants from Auth DB
- Auth calls:
GET /internal/companies/{companyId}/entitlements
- Core returns normalized company entitlements
- Auth merges:
- entitlements ∩ membership grants
- Auth returns effective access to frontend
13.2 Platform Admin catalog flow
Section titled “13.2 Platform Admin catalog flow”- Platform Admin UI calls Admin Backend
- Admin Backend validates platform-admin access
- Admin Backend calls Core internal catalog write endpoint
- Core writes DB rows and mapping rows
- Core returns normalized object
13.3 Platform Admin subscription flow
Section titled “13.3 Platform Admin subscription flow”- Platform Admin UI calls Admin Backend
- Admin Backend validates admin rights
- Admin Backend calls
POST /internal/companies/{companyId}/basicor/addons - Core updates state
- Core bumps version
- Auth cache becomes stale and must rebuild on next access resolution
14. Contract notes for QA
Section titled “14. Contract notes for QA”QA should validate at minimum:
GET /healthGET /ready- unauthorized internal call returns 401
- module/package/addon reads return normalized arrays
- company with no Basic but active addon returns valid entitlements
- company with Basic + addon returns both in enabled modules
- activating/deactivating Basic bumps
entitlementVersion - activating/deactivating addon bumps
entitlementVersion - patching catalog records preserves immutable keys
- invalid UUID returns validation error
- unknown addon/module/package returns not found where appropriate
15. Final summary
Section titled “15. Final summary”This contract defines how internal services interact with Platform Core.Core exposes catalog APIs and company entitlement APIs.Auth reads company entitlements from Core.Platform Admin writes company commercial state into Core.Core returns normalized enabled modules and entitlementVersion.Core does not return user permissions, roles, or grants.Frontend should not call Core directly for access decisions.Business backends should not use Core as a user-authorization service.16. Access model layers (REFERENCE — DO NOT IMPLEMENT HERE)
Section titled “16. Access model layers (REFERENCE — DO NOT IMPLEMENT HERE)”Platform Core participates in a 3-layer access model but only owns Level 1.
Level 1 — Company entitlements (OWNED BY CORE)
Section titled “Level 1 — Company entitlements (OWNED BY CORE)”Defines what the company has purchased:
- Basic
- Finance
- Market
- Touring
- Venue
- AI
Source of truth: Platform Core DB
Level 2 — Membership module grants (OWNED BY AUTH)
Section titled “Level 2 — Membership module grants (OWNED BY AUTH)”Defines which modules a user can access. Core MUST NOT store or evaluate this.
Level 3 — Permissions (OWNED BY AUTH)
Section titled “Level 3 — Permissions (OWNED BY AUTH)”Defines actions inside modules. Core MUST NOT store or evaluate this.
Critical rule
Section titled “Critical rule”Core returns entitlements ONLYCore never computes user access17. Relationship with Auth (STRICT)
Section titled “17. Relationship with Auth (STRICT)”Platform Core is a dependency of Auth.
Auth → Core → Auth → FrontendResponsibilities
Section titled “Responsibilities”Core:
- returns company entitlements
- returns enabled modules
- returns entitlementVersion
Auth:
- merges entitlements with user grants
- computes effective access
- returns final access model
Forbidden usage
Section titled “Forbidden usage”Core must NEVER:
- return user-level access
- validate permissions
- validate roles
- decide if a request is allowed
18. Effective access boundary
Section titled “18. Effective access boundary”Core MUST NOT implement:
effective_access = entitlements ∩ grantsThis logic belongs ONLY to Auth.
19. Delegation awareness (READ-ONLY CONTEXT)
Section titled “19. Delegation awareness (READ-ONLY CONTEXT)”Core is aware that delegation exists, but:
- does not enforce delegation
- does not validate delegation
- does not store delegation rules
Delegation is owned by Auth.
20. Frontend & Backend interaction rules
Section titled “20. Frontend & Backend interaction rules”Frontend
Section titled “Frontend”Frontend must NOT call Platform Core directly for access.
Allowed frontend calls:
- Auth
- Base backend
- module backends
- admin backend
Business backends
Section titled “Business backends”Business backends must NOT:
- call Core for user access decisions
- use Core as authorization service
They must rely on:
Auth → /auth/me/access21. Final enforcement rule
Section titled “21. Final enforcement rule”Core = commercial truthAuth = access truthBackend = enforcementFrontend = UX only