Data Model

Data Model

All data lives in Supabase Postgres. Access is server-side only via the admin client (lib/server/supabase-admin.ts, service-role key). Storage uses three buckets (input / output / merchant images).

Core tables

TableWhat it holds
storesOne row per installed store: shop_domain, plan/billing state, trial dates, generation provider/model, custom-plan fields, usage counters
platform_connectionsShopify access tokens (encrypted) + scopes per store
catalog_productsSynced Shopify products
catalog_product_imagesProduct images (incl. primary reference image)
product_tryon_settingsPer-product widget enable/disable + overrides
store_widget_configWidget appearance/config per store
storefront_theme_settingsTheme/embed detection state

Try-on flow tables

TableWhat it holds
tryon_sessionsA shopper try-on session (status, shopper identity, product)
tryon_input_imagesUploaded person images (storage bucket + path, expiry)
tryon_provider_jobsThe provider task (KIE/fal task id, status, payloads)
tryon_outputsGenerated result images (storage bucket + path)
analytics_eventsFunnel events (widget click, upload, generation, result view, add-to-cart, purchase)
attributed_ordersOrders linked to try-on sessions (conversion attribution)
shopper_rate_limit_usagePer-shopper rate-limit counters + email-gate bonus state

Growth / ops tables

TableWhat it holds
captured_emailsEmails captured via the widget email gate and landing demo (source=landing_demo) and contact inquiries (source=contact_inquiry). Conflict key (store_id, email)
discount_codesShopify discount codes created for post-try-on incentives
audit_eventsIP/abuse audit + rate-limit window enforcement
webhook_eventsReceived Shopify webhooks (idempotency/log)
sync_jobsProduct sync job tracking
app_logsStructured app logs (queryable from the admin Logs tab)
app_configMisc app-level config (e.g. log level)
billing_eventsAppend-only billing audit ledger — plan changes (from→to), per-period try-on usage snapshots, overage charges, subscription status transitions, and failures (success/error_message). Written by recordBillingEvent() from every billing path; see Billing Pipeline

Notable patterns

  • captured_emails is reused as the store for demo leads and contact inquiries (no dedicated tables) — distinguished by the source column and metadata JSON. The demo store id (teststoretryon) is used as the store_id for these “system” records.
  • stores.custom_plan + custom_price/custom_try_on_limit/custom_overage_rate drive custom plans; getPlanLimit() / getOverageRate() in lib/billing/plans.ts read them. reconcileStoreBilling is the authority: it keeps these set while the active sub is the custom one and clears them (CLEARED_CUSTOM_PLAN_FIELDS) the moment the store lands on a standard tier or trial.
  • Usage/period columns on stores: try_ons_used + overage_pending are the live counters (atomic RPC increment_try_ons); current_period_starts_at/current_period_ends_at track the active 30-day Shopify cycle. The daily billing-overage cron resets the counters when current_period_ends_at advances (Shopify fires no webhook on renewal).
  • Trial dates are written once on first install in upsertStore (only when trial_starts_at IS NULL).