Data Model and Migrations
The authoritative schema lives in packages/db/src/schema/index.ts and is migrated through the SQL files in packages/db/drizzle.
Migration Progression
Current migrations in this repo are:
0000_puzzling_proudstar.sql- baseline schema0001_lovely_patch.sql- tenant onboarding fields0002_tired_cannonball.sql- Square polling watermark0003_redemption_operators.sql- redemption and store-operator support0004_tenant_branding.sql- tenant branding storage path0005_pos_connections_tenant_pos_unique.sql- one connection per tenant/provider0006_merchant_campaigns.sql- campaigns and marketing preferences0007_campaign_signature_banner.sql- campaign banner and signature fields0008_clover_payments_watermark.sql- Clover polling watermark0009_brand_colors_program_name.sql- program name and brand colors0010_promotion_bonuses.sql- earn multipliers, promotion bonuses, and consumer date of birth0011_promotion_rls.sql- row level security on promotion tables
Current Schema Domains
Identity and tenancy
userstenantstenant_users
POS integration
pos_connectionspos_webhook_events_raw
Transactions and rewards
transactionsreward_rulesreward_events
Wallets and redemption
walletswallet_ledgerredemption_codes
Merchant operations
tenant_operator_invitesmerchant_campaignscampaign_recipientsmarketing_preferencesearn_multiplier_windowspromotion_bonus_configspromotion_bonus_grants
Governance and reliability
idempotency_keysadmin_audit_logs
High-Value Constraints
Transaction deduplication
transactions is protected by the unique key on (tenant_id, provider, provider_transaction_id).
Single wallet per user per tenant
wallets is protected by the unique key on (tenant_id, user_id).
Append-only ledger semantics
wallet_ledger enforces:
entry_type IN ('credit', 'debit')points > 0
Single POS connection per tenant/provider
pos_connections is protected by the unique key on (tenant_id, pos_type).
Campaign recipient materialization
campaign_recipients stores send state per recipient so scheduled campaigns can be resumed and audited.
Schema Facts Worth Remembering
users.idis the Supabase auth UUID.- Monetary values are stored in minor units.
- OAuth tokens are encrypted at rest in
pos_connections. - Raw webhook payloads are persisted for replay and recovery.
- Campaign and marketing data are tenant-scoped.
ORM Client
packages/db/src/client.ts exposes the Drizzle client used by the API.
Runtime uses DATABASE_URL. The API expects a pooled connection string; migrations can use a direct database URL where required.
Data Integrity Conventions
- Tenant ID appears on all tenant-scoped aggregate tables.
- Mutable snapshots are paired with immutable ledger/event records.
- Deduplication is enforced by the database, not by memory.
- Audit logs are additive.
- Campaign recipient rows are materialized before send so the sweep job is deterministic.
Written byDhruv Doshi