14 tables in packages/db/src/schema/merchTables.ts, migration 0017_conscious_sleepwalker.sql.
| Table | Columns | Purpose |
|---|---|---|
merch_campaign | 77 | Full campaign config (branding, copy, AI, legal, shipping, settings) |
merch_product | 11 | Products per campaign (tshirt, hoodie, plaque) |
merch_asset | 6 | S3 media assets (logo, hero, ai_template, overlay, backside_print) |
merch_session | 23 | Fan session (photo, AI art, art candidates, cart, shipping, payment state) |
merch_session_analytics | 28 | Session analytics sidecar (steps, milestones, generated assets) |
merch_order | 31 | Completed orders (customer, shipping, payment, fulfillment) |
merch_order_item | 8 | Line items per order |
merch_ai_model | 20 | AI model registry (endpoints, costs, parameters) |
merch_share | 5 | Shareable result links |
merch_tracking_link | 14 | Campaign tracking links (UTM-style) |
merch_tracking_event | 10 | Individual tracking visit events |
merch_campaign_visit | 5 | IP-deduplicated campaign visits |
merch_seed_test | 7 | AI seed consistency tests |
merch_seed_test_review | 6 | Seed test review judgments |
17 enums in packages/db/src/schema/merchEnums.ts:
| Enum | Values |
|---|---|
merch_consent_type | implied, checkbox |
merch_activation_status | DRAFT, LIVE, ENDED |
merch_shutdown_mode | NONE, SOFT_CLOSE, EMERGENCY_CLOSE, ENDED |
merch_user_role | customer, tester, preview |
merch_order_mode | LIVE, PREVIEW |
merch_preview_type | DEMO, TEST |
merch_order_status | pending, processing, completed, failed, cancelled, new, received, working_on, ready, shipped, delivered |
merch_product_type | tshirt, hoodie, plaque |
merch_asset_type | logo, hero, mockup, ai_template, backside_print, studio_carousel, overlay |
merch_payment_status | pending, paid, succeeded, failed, refunded |
merch_fulfillment_status | pending, processing, shipped, delivered, cancelled |
merch_face_error | no_face, multiple_faces, face_occluded, no_model |
merch_lighting_error | too_dark |
merch_ai_model_status | active, inactive, deprecated |
merch_speed_category | fast, medium, slow |
merch_progress_indicator_type | linear, spinner |
merch_source_type | selfie, upload |
#465d91 default), secondaryColor (#dce2f9 default){
"endpoint": "fal-ai/gpt-image-1.5/edit",
"modelName": "GPT-Image 1.5",
"imageField": "image_urls",
"costPerImage": 0.001,
"params": { "sync_mode": false, "aspect_ratio": "auto", "output_format": "png" }
}
[
{ "artKey": "merch/ai-candidates/sess-1-best-1234.png", "url": "https://...", "score": 0.92 },
{ "artKey": "merch/ai-candidates/sess-2-alt-1234.png", "url": "https://...", "score": 0.85 },
{ "artKey": "merch/ai-candidates/sess-3-alt-1234.png", "url": "https://...", "score": 0.71 }
]
Stores all generation candidates sorted by score (character consistency likeness, 0–1). The client uses this array to render the ArtSelectionSection carousel. The fan's selected candidate is written to aiArtKey via POST /api/merch/ai/select.
[{
"productId": "abc123",
"productName": "T-Shirt",
"type": "tshirt",
"size": "L",
"quantity": 1,
"unitPrice": 69.0,
"subtotal": 69.0,
"imageKey": "merch/ai-candidates/sess-1-best-1234.png"
}]
The imageKey field (optional) stores the S3 key of the AI art associated with a specific cart item, enabling per-item art display in the CartResultView.
{
"email": "fan@example.com",
"firstName": "John",
"lastName": "Doe",
"addressLine1": "123 Main St",
"city": "New York",
"state": "NY",
"postalCode": "10001",
"country": "US"
}
{
"vinyl": { "x": 200, "y": 200, "size": 280 },
"prompt": "Create a platinum vinyl record artwork.",
"fanName": "MARK THOMPSON",
"standardPricing": { "price": 299, "shipping": 35 },
"deluxePricing": { "price": 539, "shipping": 35 },
"standardImages": { "basePlaqueUrl": "...", "recordFrameUrl": "...", "artistRefUrl": "..." },
"deluxeImages": { "basePlaqueUrl": "...", "recordFrameUrl": "...", "artistRefUrl": "..." }
}
11 access files in packages/db/src/access/merch/:
| File | Functions |
|---|---|
merch-campaign.ts | getMerchCampaignBySlug, getMerchCampaignById, listActiveMerchCampaigns, getMerchCampaignLifecycle |
merch-product.ts | listMerchProductsByCampaign, getMerchProductById |
merch-asset.ts | getMerchAssetById, getMerchAssetsByIds, getMerchAssetByKey, createMerchAsset |
merch-session.ts | createMerchSession, getMerchSessionById, updateMerchSession |
merch-session-analytics.ts | createMerchSessionAnalytics, getMerchAnalyticsBySessionId, updateMerchAnalyticsStep, updateMerchAnalyticsMilestone, appendMerchGeneratedAsset |
merch-order.ts | createMerchOrder, getMerchOrderById, getMerchOrderBySession, updateMerchOrderStatus |
merch-order-item.ts | createMerchOrderItems, listMerchOrderItemsByOrder |
merch-share.ts | createMerchShare, getMerchShareById |
merch-tracking.ts | getMerchTrackingLinkByCampaignSlug, incrementMerchTrackingVisit, recordMerchTrackingEvent |
merch-campaign-visit.ts | recordMerchCampaignVisit |
merch-ai-model.ts | getActiveMerchAIModels, getMerchAIModelByEndpoint |
All exported from packages/db/src/index.ts.
merch_campaign(slug, talentName), merch_tracking_link(campaignId, slug), merch_campaign_visit(campaignId, ipAddress), merch_ai_model(endpointId), merch_session_analytics(sessionId)merch_campaign(deletedAt) — queries filter deletedAt IS NULLmerch_campaign_visit uses onConflictDoNothing() for upsertSET visitCount = visitCount + 1| Content | S3 Key Pattern |
|---|---|
| Selfies | merch/selfie/{filename} |
| AI art candidates | merch/ai-candidates/{sessionId}-{attempt}-{timestamp}.png |
| Final AI art | merch/ai-generated/{sessionId}-{timestamp}.png |
| Mockups | merch/mockup/{sessionId}-{productType}-{timestamp}.png |
| Plaque illustrations | merch/plaque-illustrations/{sessionId}-{timestamp}.png |
On This Page
Tables OverviewEnumsKey Table Detailsmerch_campaign (77 columns)merch_session (22 columns)merch_order (31 columns)JSON Column ShapesaiConfig (merch_campaign)aiArtCandidates (merch_session)cartItems (merch_session)shippingInfo (merch_session)plaqueConfig (merch_campaign)Access LayerIndexesS3 Key Patterns