01 — Flex Programs
What it is: The top-level configuration container for a Flex store. Everything a team configures — how fans pick events, how packs are structured, what pricing is used — hangs off a program.
Core idea
A store can have many published programs but only one active at a time. The active program drives the whole store. To run a flash sale or change mid-season, a team pre-configures a second program and flips which one is active — no code changes, no manual edits.
Three program types:
- Build Your Own (BYO) — fan picks any events within a min/max range
- Game Packs — structured packs, each with one or more tiers
- Preset Packages — fixed or flexible bundles of specific events
Tables
tbl_flex_programs — the root
| Field | Notes |
|---|---|
programType | build_your_own, game_packs, preset_packages |
isActive | only ONE should be true at a time (app enforced) |
status | draft, published, archived |
tbl_flex_program_configs — per-program settings (JSONB)
One row per key per program. Keys:
| Key | What it controls |
|---|---|
inventory | inventoryType (seat vs suite level), ADA filter, multi-pricing, block limiter |
ledger | which ledger this program uses |
pack_structure | packs_with_tiers or packs_only |
payment_plan | enabled / required toggles |
tbl_flex_program_class_names — which class names (access groups) this program sells
Previously stored as JSONB; moved to its own table. TM uses className, other vendors use classNameId.
tbl_flex_program_pricing_labels — for multi-pricing per seat
Only used when multiplePricingPerSeat.isEnabled = true. Defines labels like "Cash Price" / "Flex Credit" with optional price/ticket-type code filters.
BYO tables
tbl_build_your_own_config
Just minEvents + maxEvents per program. Eligible events are not "all of tbl_events" — they come from the tbl_build_your_own_events junction below, intersected with the live catalog (findByProgramIdWithEvents + filterAndSortEvents).
tbl_build_your_own_events
The program's event eligibility list (one row per eligible event) — also carries each event's buildStatus (not_built → building → built) + isSoldOut. Only events present here (and live in the catalog) are offered to fans.
Game Pack tables
tbl_game_packs
| Field | Notes |
|---|---|
selectionMode | exact (min = max) or range (min < max) |
minEvents / maxEvents | pack-level constraints |
isDefaultPack | pre-selected in UI; only one per program |
tbl_game_pack_tiers
Every pack must have at least one tier. Tiers have their own minEvents/maxEvents. Sum of tier minEvents must be ≥ pack minEvents (and ≤ pack maxEvents when set).
tbl_pack_tier_events
Links events to tiers. An event can appear in multiple packs/tiers across packs, but not in two tiers of the same pack.
Preset Package tables
tbl_preset_packages
Two types:
- preset — fixed event list; optionally allow replacement of sold-out events
- flexible — fan selects from an event pool (min/max)
Both can allow allowOptionalEvents (fan adds extra events from tbl_events outside the package).
tbl_package_events
For preset: these are the fixed events. For flexible: this is the pool to choose from.
Unlock Special Games ⚠️ (spec only — not implemented)
⚠️ These tables are a design spec, not yet built — don't expect them in the running schema or code.
tbl_unlock_special_games
Special/exclusive events hidden from normal listings. Unlock when conditions are met. Can be configured as removable or locked to cart.
tbl_unlock_conditions
Conditions that trigger an unlock. Logic rules:
- Multiple rows for same unlock = OR (any row satisfying = unlocks)
- Multiple non-null fields in one row = AND (all must be true)
Condition types: min_event_count, specific_pack, specific_package, custom.
Key rules
- Only one active program at a time (app enforced, not DB)
- Every game pack must have at least one tier
- Unlocks (once built) would be re-evaluated on every cart change; if conditions fail, the special game is removed
- Class names migrated from JSONB to
tbl_flex_program_class_names— don't look for them in the config JSONB