Skip to main content

Data model

Drape's primary datastore is MongoDB (accessed with motor async + pymongo). Roughly two dozen collections are in use; the ones that carry the product are below. Try-on jobs are not a collection — they live in Redis with a short TTL.

Core relationships

lists

fulfills

places

saves

generates

rates

owns

sold in

referenced by

users

stores

products

orders

saved_items

tryon_history

tryon_observations

body_photo fields
on the user doc

Collections

CollectionPurposeNotable fields
usersAccounts + fit profile + body photoid, email (unique), password_hash, role, is_admin, is_store_admin, consent fields, referral fields, account_status
productsCatalog itemsid, store_id, garment_style (drives AI routing), image_url, color_variants[], sizes_available, fashn_category
storesBrands / white-label storefrontsid, slug (unique), plan_type, subscription_status, Stripe Connect fields, BYOK API keys
ordersMarketplace ordersid, stripe_session_id (unique), amount / platform_fee / net_amount (cents), status, product_snapshot
tryon_historyPer-user render historyuser + product + result references
tryon_observationsQuality/rating recordsmirrored to a Supabase table of the same name
body_referencesANSUR II / sizing reference datano PII
render_logsPer-render audit trailone doc per render attempt
deletion_logBIPA deletion audittwo-phase deletion_confirmed flag

Others in use include training_events, previews, saved_items, saved_looks, outfits, brand_follows, drop_waitlist, brand_waitlist, onboarding_submissions, reset_tokens, verify_tokens, invite_codes, and bug_reports. Indexes are created at startup.

The body-photo fields

The most important — and most sensitive — data hangs off the user document. These fields are written by the pipeline (not declared in the Pydantic model), and body_photo_data is the single source of truth for try-on eligibility.

FieldMeaning
body_photo_dataBase64 composited JPEG — what try-on actually uses
body_photo_hash16-char hash; part of the render cache key
body_photo_owner_idBIPA ownership stamp; mismatch blocks try-on with 409
body_photo_originalSupabase path to the original upload
body_photo_cutoutBackground-removed cutout path
body_photo_mask_upper_url / body_photo_mask_lower_urlEVF-SAM garment masks
body_photo_quality_flags / body_photo_needs_reviewQuality signals
Known dual-write debt

analyze-body-photos and body-photo/upload both write overlapping body-photo fields (a dual-write pattern with a TODO in the code). Consolidation is deferred — see tech debt.

Why jobs live in Redis, not Mongo

A try-on job is ephemeral coordination state, not a record of truth. It is stored as `job:{job_id}` in Redis with a 10-minute to 1-hour TTL, carrying {status, user_id}. The user_id is what the status endpoint checks for ownership. The durable audit of a render is the render_logs Mongo document; the job key is allowed to expire.

Storage (Supabase)

Body photos and garment images are objects, not documents, so they live in Supabase Storage — storage only, no Supabase Auth. The biometric buckets (body-photos-original, body-photos-cutout, body-photos-mask) are private and accessed via server-side signed URLs; garment buckets are public. All access uses the service-role key, so authorization is enforced in FastAPI, not by Supabase RLS. See security for the full model.