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/extract-raw-xml/etl/docs/features/ETL/epics/06-source-adapter-brink-pos-api/source-task.md 17,791 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

Source Adapter: Brink POS API

  • ../../../../ETL.md - ETL architecture, metadata minimum, and S3 layout
  • ../../../../flows/etl-ingestion.md - ingestion flow and batch lifecycle
  • technical-integration-notes.md - transport, auth, retry, and checkpoint rules
  • poc-checklist.md - implementation checklist

Goal

Deliver a minimum viable Brink POS API pull adapter for one tenant-location that:

  • pulls Brink SOAP data with conservative polling
  • stores raw SOAP responses in S3
  • loads source-shaped staging tables into the tenant warehouse
  • populates the first curated tables already present in the warehouse bootstrap
  • reuses the existing ETL batch and checkpoint model from docs/ETL.md

Scope

In scope for this epic:

  • read-only Brink integration over Sales2.svc, Settings2.svc, Settings.svc, and Kitchen.svc
  • one tenant-location PoC
  • raw response capture in S3
  • Brink-specific staging tables needed for the current curated bootstrap
  • staging -> curated SQL for the first useful curated outputs
  • reuse of existing operational metadata instead of a new control plane

Out of scope for this epic:

  • write operations through Brink APIs
  • full Brink settings coverage
  • labor, loyalty, inventory, and hourly sales integrations
  • future-date order flows and deposit-specific publication
  • production-grade replay or pull-attempt control plane

Document Boundary

This document defines the PoC data contract for the Brink adapter:

  • what curated tables exist today
  • which Brink operations we need
  • how raw payloads should be stored
  • which staging tables should exist
  • which staging tables feed which curated tables
  • what minimum metadata must be persisted

Detailed transport rules, token handling, retry limits, and checkpoint behavior stay in technical-integration-notes.md.

Current Curated Tables

Current tenant curated bootstrap in backend/Kds.Ai.Warehouse/Sql/Bootstrap/Tenant/Curated/060_create_curated_canonical_tables.sql contains these tables:

Curated tableCurrent purposeBrink PoC status
curated.kitchen_item_status_eventskitchen item lifecycle eventstarget
curated.kitchen_order_itemskitchen-facing order itemstarget
curated.kitchen_orderskitchen-facing order headerstarget
curated.pos_transaction_itemsPOS item transaction factstarget
curated.pos_transactionsPOS transaction headerstarget
curated.pos_hourly_saleshourly sales aggregatesout of scope
curated.pos_transaction_tendersPOS payment/tender factstarget
curated.pos_tablesPOS table lookuptarget
curated.pos_table_definitionsPOS table hierarchy metadataout of scope
curated.pos_tenderstender lookuptarget
curated.customerscustomer masterout of scope for PoC
curated.loyalty_customersloyalty customer masterout of scope
curated.pos_catalog_itemscatalog item lookuptarget
curated.pos_modifiersmodifier group lookuptarget
curated.inventory_itemsinventory masterout of scope
curated.pos_menu_itemsmenu item lookupoptional
curated.pos_order_itemsorder line convenience tableoptional
curated.pos_ordersorder header convenience tableoptional

Brink Endpoint Plan

Primary Brink service endpoints:

  • https://{{server}}.brinkpos.net/Sales2.svc
  • https://{{server}}.brinkpos.net/Settings2.svc
  • https://{{server}}.brinkpos.net/Settings.svc
  • https://{{server}}.brinkpos.net/Kitchen.svc

Planned PoC operations:

ServiceOperationWhat we getWhy we need itDocs
Sales2.svcGetOrdersorder headers, order items, modifiers, taxes, payments, payment details, destination/employee/table references, business totals, timestampsmain source for POS and kitchen transactional curated tableshttps://brinkapiportal.parpos.com/sales2#GetOrders
Sales2.svcGetCurrentBusinessDatecurrent register business dateoptional helper for aligning BusinessDate windows and daily pullshttps://brinkapiportal.parpos.com/sales2#GetCurrentBusinessDate
Kitchen.svcGetOrderBumpTimesorder-level bump events with queue and send timingfeeds kitchen order status/timinghttps://brinkapiportal.parpos.com/kitchen#GetOrderBumpTimes
Kitchen.svcGetOrderItemBumpTimesitem-level bump/fire/send events with queue and order item idfeeds kitchen item status/timinghttps://brinkapiportal.parpos.com/kitchen#GetOrderItemBumpTimes
Settings2.svcGetLastModifiedTimelast settings and employee modification timestampslightweight settings freshness checkpointhttps://brinkapiportal.parpos.com/settings2#GetLastModifiedTime
Settings2.svcGetItemsitem master with price, tax, revenue center, modifier group links, kitchen namefeeds catalog and transactional enrichmenthttps://brinkapiportal.parpos.com/settings2#GetItems
Settings2.svcGetModifierGroupsmodifier group headers and group item membershipfeeds pos_modifiers and modifier enrichmenthttps://brinkapiportal.parpos.com/settings2#GetModifierGroups
Settings2.svcGetDestinationsdestination lookup such as dine-in, takeout, deliveryenriches orders and transactionshttps://brinkapiportal.parpos.com/settings2#GetDestinations
Settings2.svcGetTablestable lookup for table-service locationsfeeds pos_tableshttps://brinkapiportal.parpos.com/settings2#GetTables
Settings2.svcGetKitchenQueueskitchen queue lookupenriches bump events and kitchen projectionshttps://brinkapiportal.parpos.com/settings2#GetKitchenQueues
Settings2.svcGetTaxestax lookup and tax metadataenriches item and order tax mappingshttps://brinkapiportal.parpos.com/settings2#GetTaxes
Settings2.svcGetEmployeesemployee lookupenriches orders, transactions, and tendershttps://brinkapiportal.parpos.com/settings2#GetEmployees
Settings.svcGetTenderstender lookup and tender configurationfeeds pos_tenders and payment enrichmenthttps://brinkapiportal.parpos.com/settings#GetTenders

Implementation note:

  • Settings2.GetSettings may later consolidate some lookup pulls, but the PoC should prefer direct operations above because they map more clearly to raw files, staging tables, and checkpoints.

Proposed Raw S3 Files

Raw storage should stay 1:1 with the SOAP response body returned by each Brink call.

Recommended path shape under the ETL raw prefix:

text
raw/{tenant_id}/brink_pos/{yyyy}/{MM}/{dd}/{batch_id}/{stream}/
  sales2_get_orders_{window_start}_{window_end}.xml
  kitchen_get_order_bump_times_{window_start}_{window_end}.xml
  kitchen_get_order_item_bump_times_{window_start}_{window_end}.xml
  settings2_get_last_modified_time.xml
  settings2_get_items.xml
  settings2_get_modifier_groups.xml
  settings2_get_destinations.xml
  settings2_get_tables.xml
  settings2_get_kitchen_queues.xml
  settings2_get_taxes.xml
  settings2_get_employees.xml
  settings_get_tenders.xml

Rules:

  • one raw object per Brink call execution
  • store the SOAP body exactly as returned by Brink
  • do not mutate or normalize the raw XML before saving it
  • keep request window values in the file name or adjacent object metadata
  • never store AccessToken or LocationToken in file content, metadata, or logs

Proposed Staging Tables

Staging must be 1:1 with the raw files saved in S3 because the worker will COPY those load-ready files directly into Redshift. That means staging should stay wide and landing-oriented, with as little fragmentation as possible. Splitting nested Brink collections into many tables should happen only in curated transforms, not in the landing layer.

Important semantic rule for correct warehouse design:

  • Sales2.GetOrders, Kitchen.GetOrderBumpTimes, and Kitchen.GetOrderItemBumpTimes are event/fact-style feeds
  • Brink settings calls should be treated as snapshot-style reference feeds, not as a native history feed
  • GetLastModifiedTime only tells us that configuration changed; it does not provide a change log
  • lookup responses such as items, modifier groups, destinations, tables, taxes, tenders, employees, and kitchen queues represent the current known configuration at pull time
  • therefore staging for settings should store per-batch snapshots 1:1 with the source payloads
  • curated settings-backed tables should default to current-state reference tables unless we explicitly add historical/SCD behavior later
  • if we need history of configuration changes in the future, that history must be built by KDSAI from successive snapshots, not assumed to exist in Brink

Recommended PoC staging tables:

Staging tableSource operationRaw file / response shapePoC status
staging.brink_sales2_get_ordersSales2.GetOrdersone row per raw GetOrders response payload, preserving the Orders collection as source-shaped contentrequired
staging.brink_kitchen_get_order_bump_timesKitchen.GetOrderBumpTimesone row per raw GetOrderBumpTimes response payload, preserving the OrderBumpTimes collectionrequired
staging.brink_kitchen_get_order_item_bump_timesKitchen.GetOrderItemBumpTimesone row per raw GetOrderItemBumpTimes response payload, preserving the OrderItemBumpTimes collectionrequired
staging.brink_settings2_get_last_modified_timeSettings2.GetLastModifiedTimeone row per raw freshness response payloadrequired
staging.brink_settings2_get_itemsSettings2.GetItemsone row per raw GetItems response payload, preserving the Item collectionrequired
staging.brink_settings2_get_modifier_groupsSettings2.GetModifierGroupsone row per raw GetModifierGroups response payload, preserving the ModifierGroups collectionrequired
staging.brink_settings2_get_destinationsSettings2.GetDestinationsone row per raw GetDestinations response payload, preserving the Destination collectionrequired
staging.brink_settings2_get_tablesSettings2.GetTablesone row per raw GetTables response payload, preserving the Table collectionrequired
staging.brink_settings2_get_kitchen_queuesSettings2.GetKitchenQueuesone row per raw GetKitchenQueues response payload, preserving the KitchenQueue collectionrequired
staging.brink_settings2_get_taxesSettings2.GetTaxesone row per raw GetTaxes response payload, preserving the Tax collectionrequired
staging.brink_settings2_get_employeesSettings2.GetEmployeesone row per raw GetEmployees response payload, preserving the Employee collectionrequired
staging.brink_settings_get_tendersSettings.GetTendersone row per raw GetTenders response payload, preserving the Tender collectionrequired

Landing rules:

  • one staging table per Brink operation / raw file type
  • one load-ready file maps to one staging table
  • nested collections remain embedded in the landed payload shape
  • no separate staging tables for order entries, modifiers, taxes, payments, or payment details in the PoC
  • decomposition of nested collections happens in staging -> curated SQL only
  • event streams and settings snapshots should not be modeled the same way in curated

Every staging table should carry at least:

  • _etl_batch_id
  • _etl_source_type
  • _etl_ingested_at
  • _etl_raw_s3_path

Staging To Curated Matrix

Curated tableInput staging tablesBrink mapping notePoC status
curated.kitchen_item_status_eventsstaging.brink_kitchen_get_order_item_bump_times, staging.brink_sales2_get_orders, staging.brink_settings2_get_kitchen_queuesexplode item bump payloads and join back to order/item structures from GetOrders inside the curated transformtarget
curated.kitchen_order_itemsstaging.brink_sales2_get_orders, staging.brink_kitchen_get_order_item_bump_times, staging.brink_settings2_get_itemsexplode order entries from GetOrders, then enrich with bump timing and item lookuptarget
curated.kitchen_ordersstaging.brink_sales2_get_orders, staging.brink_kitchen_get_order_bump_times, staging.brink_settings2_get_destinations, staging.brink_settings2_get_kitchen_queuesexplode order headers from GetOrders, then add queue and bump timingtarget
curated.pos_transaction_itemsstaging.brink_sales2_get_orders, staging.brink_settings2_get_itemsderive item facts by exploding entries, modifiers, and taxes from the landed GetOrders payloadtarget
curated.pos_transactionsstaging.brink_sales2_get_orders, staging.brink_settings2_get_destinations, staging.brink_settings2_get_employeesderive one transaction row per Brink order from the landed GetOrders payloadtarget
curated.pos_hourly_salesnoneBrink PoC does not expose a direct hourly aggregate feedout of scope
curated.pos_transaction_tendersstaging.brink_sales2_get_orders, staging.brink_settings_get_tenders, staging.brink_settings2_get_employeesderive payment and payment-detail facts by exploding nested payments from the landed GetOrders payloadtarget
curated.pos_tablesstaging.brink_settings2_get_tablesdirect lookup projection from landed settings payloadtarget
curated.pos_table_definitionsnoneno Brink source selected in PoCout of scope
curated.pos_tendersstaging.brink_settings_get_tendersdirect lookup projection from landed settings payloadtarget
curated.customersnoneGetOrders exposes nullable CustomerId, but not a full customer master contractout of scope for PoC
curated.loyalty_customersnoneno Brink loyalty feed in PoCout of scope
curated.pos_catalog_itemsstaging.brink_settings2_get_items, staging.brink_settings2_get_taxesderive item lookup rows from landed item payload, enriched with tax metadatatarget
curated.pos_modifiersstaging.brink_settings2_get_modifier_groups, staging.brink_settings2_get_itemsexplode modifier group membership inside curated transformtarget
curated.inventory_itemsnoneno Brink inventory feed in PoCout of scope
curated.pos_menu_itemsstaging.brink_settings2_get_items, staging.brink_settings2_get_destinations, staging.brink_settings2_get_kitchen_queuesoptional convenience projection from landed settings payloadsoptional
curated.pos_order_itemsstaging.brink_sales2_get_orders, staging.brink_settings2_get_itemsoptional convenience projection by exploding order entries from the landed GetOrders payloadoptional
curated.pos_ordersstaging.brink_sales2_get_orders, staging.brink_settings2_get_destinations, staging.brink_settings_get_tendersoptional convenience projection by exploding order headers and nested payments from the landed GetOrders payloadoptional

Minimum Metadata Required By docs/ETL.md

The Brink adapter must satisfy the ETL baseline without adding new metadata tables in the PoC.

Operational metadata to reuse

  • TenantWarehouseMapping
  • IngestionBatch
  • IngestionCheckpoint

Minimum batch fields

For each Brink sync stream, IngestionBatch must preserve at least the fields explicitly required by docs/ETL.md:

  • batch_id
  • tenant_id
  • tenant_database
  • ingestion_mode = api_pull
  • source_type = brink_pos
  • received_at
  • load_started_at
  • load_completed_at
  • status
  • row counts and error details
  • raw S3 path
  • load S3 path

Recommended Brink-specific additions on the batch record or adjacent structured logs:

  • stream_name
  • location_key
  • service_name
  • operation_name
  • request_window_start
  • request_window_end
  • result_code

Minimum checkpoint keys

Use one checkpoint per location and stream:

  • location:{location_key}:orders:modified-time
  • location:{location_key}:order-bumps:bump-time
  • location:{location_key}:item-bumps:bump-time
  • location:{location_key}:settings:last-modified

Minimum raw and staging traceability

To satisfy replay and debugging requirements from docs/ETL.md, each successful Brink load must let us answer:

  • which raw SOAP file was pulled
  • which batch loaded it
  • which staging rows came from that file
  • which curated publication was produced from that batch

Minimum fields needed across raw storage, staging, and logs:

  • tenant_id
  • source_type
  • batch_id
  • stream_name
  • raw_response_s3_path
  • load_s3_path
  • request_window_start
  • request_window_end
  • checkpoint_before
  • checkpoint_after
  • ingested_at

Minimum connection metadata

Service-owned connection metadata for the PoC must include:

  • tenant identifier
  • Brink server or endpoint stack
  • Brink location identifier used by the adapter
  • encrypted AccessToken
  • encrypted LocationToken
  • connection status such as active or disabled

Minimum Acceptance Bar

This epic is complete enough for the PoC when all of the following are true:

  • one tenant can be resolved to one Brink endpoint stack and location
  • the adapter stores raw SOAP responses in S3 for each Brink call
  • required staging tables are populated from the selected Brink operations
  • target curated tables can be rebuilt from staging without reading raw XML again
  • one IngestionBatch exists per Brink sync stream run
  • checkpoints advance only after end-to-end success
  • failed runs keep raw payloads and do not advance checkpoints
  • logs and metadata are sufficient to replay or debug a failed time window