Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)

Branches (328)

Branch-specific documentation across extract-raw-xml, feature-fleet-import, kds_cdc_improvments, life-cycle, and tenat-admin.
branches/tenat-admin/architecture/docs/features/tenant-admin-control-plane/api-contract.md 9,850 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

Tenant Control Plane API Contract

Purpose

This document defines the PoC API contract for the tenant control-plane flows described in feature-proposal.md.

Scope:

  • admin create/list clients
  • client create/list tenants for the authenticated client
  • client get tenant by id for the authenticated client
  • client bootstrap trigger for authenticated-client tenants

This document focuses on request/response contracts and expected behavior.

Conventions

  • API keeps the technical word tenant
  • business meaning of tenant is restaurant
  • all identifiers are uuid
  • codes are generated by the system
  • databaseName is generated by the system
  • timestamps use ISO 8601 UTC format

Tenant Systems

For the PoC, tenant system configuration should favor flexibility over strict classification.

The tenant stores a simple collection of system names:

  • systems: string[]

Examples:

  • Brink
  • LogicControls

Rules:

  • values are simple names used as bootstrap input
  • values are normalized case-insensitively
  • duplicates are not allowed after normalization
  • order is not significant
  • the exact catalog can evolve later
  • classification such as POS/KDS/Inventory is intentionally deferred

Normalization rule for the PoC:

  • trim leading/trailing whitespace
  • compare case-insensitively
  • keep one canonical stored/display value per system entry

TenantStatus

  • PendingBootstrap
  • Active
  • BootstrapFailed

API Surfaces

Two endpoint groups should exist over the same domain model.

Admin surface

Used by platform operators.

Characteristics:

  • may operate across all clients
  • limited to client management in this PoC

Client surface

Used by customer self-service users.

Characteristics:

  • scoped to the authenticated client
  • creates and lists tenants for the authenticated client
  • reads basic details for a specific tenant owned by the authenticated client
  • may only operate on tenants owned by the authenticated client

1. Create Client

POST /admin/clients

Request

json
{
  "name": "Acme Restaurants"
}

Behavior

  • creates a new client
  • generates code automatically from name
  • sets isActive = true
  • code is immutable after creation

Success Response

201 Created

json
{
  "clientId": "d3fd91d5-8dc2-4d1b-a6f0-10a260da4b2e",
  "name": "Acme Restaurants",
  "code": "acme_restaurants",
  "isActive": true,
  "createdAt": "2026-05-14T11:00:00Z",
  "updatedAt": "2026-05-14T11:00:00Z"
}

Validation Errors

400 Bad Request

Cases:

  • missing name
  • name too short or too long
  • generated code conflicts and cannot be resolved according to system rules

2. List Clients

GET /admin/clients

Behavior

  • returns a simple admin list
  • no paging or filtering in PoC

Success Response

200 OK

json
[
  {
    "clientId": "d3fd91d5-8dc2-4d1b-a6f0-10a260da4b2e",
    "name": "Acme Restaurants",
    "code": "acme_restaurants",
    "isActive": true,
    "tenantCount": 2,
    "createdAt": "2026-05-14T11:00:00Z",
    "updatedAt": "2026-05-14T11:00:00Z"
  }
]

3. Create Tenant For Current Client

POST /client/tenants

Request

json
{
  "name": "KFC Warsaw Zlote Tarasy",
  "systems": [
    "Brink",
    "LogicControls"
  ]
}

Behavior

  • resolves the current client from authentication context
  • creates a tenant under that client
  • generates code automatically from name
  • derives databaseName automatically from code
  • validates databaseName against Redshift naming rules
  • sets status = PendingBootstrap
  • sets isActive = false
  • stores system names directly on tenant
  • normalizes systems for duplicate detection before save

Success Response

201 Created

json
{
  "tenantId": "0f4bf6cf-18b0-4ec3-9628-d8ff0db96855",
  "clientId": "d3fd91d5-8dc2-4d1b-a6f0-10a260da4b2e",
  "name": "KFC Warsaw Zlote Tarasy",
  "code": "kfc_warsaw_zlote_tarasy",
  "databaseName": "kfc_warsaw_zlote_tarasy",
  "systems": [
    "Brink",
    "LogicControls"
  ],
  "status": "PendingBootstrap",
  "isActive": false,
  "createdAt": "2026-05-14T11:10:00Z",
  "updatedAt": "2026-05-14T11:10:00Z"
}

Validation Errors

400 Bad Request

Cases:

  • authenticated client cannot be resolved
  • tenant name missing
  • tenant name already exists within the current client
  • generated tenant code conflicts and cannot be resolved according to system rules
  • derived databaseName violates Redshift naming rules
  • systems contains duplicate values after normalization

4. List Tenants For Current Client

GET /client/tenants

Behavior

  • resolves the current client from authentication context
  • returns only tenants owned by that client
  • no paging or filtering in PoC

Success Response

200 OK

json
[
  {
    "tenantId": "0f4bf6cf-18b0-4ec3-9628-d8ff0db96855",
    "name": "KFC Warsaw Zlote Tarasy",
    "code": "kfc_warsaw_zlote_tarasy",
    "databaseName": "kfc_warsaw_zlote_tarasy",
    "systems": [
      "Brink",
      "LogicControls"
    ],
    "status": "PendingBootstrap",
    "isActive": false,
    "createdAt": "2026-05-14T11:10:00Z",
    "updatedAt": "2026-05-14T11:10:00Z"
  }
]

5. Get Tenant By Id For Current Client

GET /client/tenants/{tenantId}

Behavior

  • resolves the current client from authentication context
  • ensures the tenant belongs to that client
  • returns basic tenant details for tenant overview and restaurant-worker access

Success Response

200 OK

json
{
  "tenantId": "0f4bf6cf-18b0-4ec3-9628-d8ff0db96855",
  "name": "KFC Warsaw Zlote Tarasy",
  "code": "kfc_warsaw_zlote_tarasy",
  "databaseName": "kfc_warsaw_zlote_tarasy",
  "systems": [
    "Brink",
    "LogicControls"
  ],
  "status": "PendingBootstrap",
  "isActive": false,
  "createdAt": "2026-05-14T11:10:00Z",
  "updatedAt": "2026-05-14T11:10:00Z"
}

Failure Response

404 Not Found or 403 Forbidden

Cases:

  • tenant does not exist
  • tenant does not belong to the authenticated client

6. Bootstrap Tenant For Current Client

POST /client/tenants/{tenantId}/bootstrap

Behavior

  • resolves the current client from authentication context
  • ensures the tenant belongs to that client
  • reads generated databaseName
  • reads systems stored on tenant
  • rejects bootstrap when systems is empty
  • triggers bootstrap with required parameters
  • endpoint is idempotent
  • repeated calls must not duplicate infrastructure

Success Response

200 OK

json
{
  "tenantId": "0f4bf6cf-18b0-4ec3-9628-d8ff0db96855",
  "databaseName": "kfc_warsaw_zlote_tarasy",
  "status": "Active",
  "isActive": true,
  "bootstrapTriggered": true,
  "updatedAt": "2026-05-14T11:15:00Z"
}

Failure Response

409 Conflict or 400 Bad Request

Cases:

  • tenant has no configured systems
  • tenant does not belong to the authenticated client
  • tenant cannot be bootstrapped from current state
  • current bootstrap flow rejects the request

Idempotent Re-run Response

200 OK

json
{
  "tenantId": "26b1c5b3-221e-4cb7-af8a-474531b7db1b",
  "databaseName": "burger_house_downtown",
  "status": "Active",
  "isActive": true,
  "bootstrapTriggered": false,
  "updatedAt": "2026-05-14T12:20:00Z"
}

Suggested DTO Shapes

CreateClientRequest

json
{
  "name": "string"
}

ClientResponse

json
{
  "clientId": "uuid",
  "name": "string",
  "code": "string",
  "isActive": true,
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}

ClientListItemResponse

json
{
  "clientId": "uuid",
  "name": "string",
  "code": "string",
  "isActive": true,
  "tenantCount": 0,
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}

CreateTenantRequest

json
{
  "name": "string",
  "systems": [
    "string"
  ]
}

TenantResponse

json
{
  "tenantId": "uuid",
  "clientId": "uuid",
  "name": "string",
  "code": "string",
  "databaseName": "string",
  "systems": [
    "string"
  ],
  "status": "PendingBootstrap",
  "isActive": false,
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}

TenantListItemResponse

json
{
  "tenantId": "uuid",
  "name": "string",
  "code": "string",
  "databaseName": "string",
  "systems": [
    "string"
  ],
  "status": "PendingBootstrap",
  "isActive": false,
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}

BootstrapTenantResponse

json
{
  "tenantId": "uuid",
  "databaseName": "string",
  "status": "Active",
  "isActive": true,
  "bootstrapTriggered": true,
  "updatedAt": "timestamp"
}

TenantDetailsResponse

json
{
  "tenantId": "uuid",
  "name": "string",
  "code": "string",
  "databaseName": "string",
  "systems": [
    "string"
  ],
  "status": "PendingBootstrap",
  "isActive": false,
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}

Validation Rules Summary

Client

  • name required
  • generated code unique
  • code immutable

Tenant

  • authenticated client must exist and be resolvable
  • name required
  • name unique within a client
  • generated code unique globally
  • code immutable
  • databaseName derived from code
  • databaseName must satisfy Redshift naming rules
  • databaseName immutable
  • systems may be empty
  • systems values must not repeat after normalization

Tenant detail access

  • GET /client/tenants/{tenantId} must return only tenants owned by the authenticated client
  • tenant detail response should contain only the basic tenant fields needed by the tenant-facing UI

Bootstrap precondition

  • tenant must contain at least one configured system before bootstrap can be triggered

Out of Scope

The following are intentionally not defined in this PoC contract:

  • authentication and authorization model
  • user-to-tenant access model
  • pagination and filtering
  • bootstrap internal orchestration details
  • final full vendor catalog