System topology
Clients
Shopper
{store}.root · custom domain
Merchant & staff
admin.root
App developer
dev.root · admin.root/partners
Theme developer
sellify CLI (npm: sellify-cli)
Third-party app
GraphQL + OAuth + webhooks
Payment & mail providers
webhooks · SMTP
Edge
proxy.ts
host → route tree rewrite
Primary-domain 308
lib/primary-domain (cached)
AJAX rewrite
/cart.js → /s/{store}/ajax
Surfaces (App Router)
Storefront
app/s/[store] · Liquid themes
Admin
app/admin · RSC + server actions
Developer portal
app/dev · docs, explorer
Marketing site
app/site
HTTP APIs
app/api · GraphQL, OAuth, webhooks, cron
Domain services (src/lib)
Commerce
cart · checkout · pricing · orders · inventory
Payments
razorpay · stripe · paypal · fx
Messaging
email · notifications · automations · campaigns
Platform
admin-api · webhooks · events · flow · public-apps
Themes
themes/* · liquidjs
Analytics
analytics/* · SellifyQL
State & infra
Postgres
Drizzle ORM · all tenant data
jobs table
SKIP LOCKED queue
Object storage
S3 / R2 · or .uploads/
SMTP
store mailbox · platform sender
Razorpay · Stripe · PayPal
per-store credentials
Host routing
root (localhost:3000)→/site/*marketing + architectureadmin.root→/admin/*+ x-sellify-pathdev.root→/dev/*docs · explorer{store}.root→/s/{store}/*+ x-sellify-pathcustom domain→/s/~{hostname}/*resolved via domains{store}.root/admin/*→307 → admin.root/store/{store}/*like {shop}/admin{store}.root (GET) with primary domain→308 → https://{primary}previews exempt{store}.root/cart.js · /products/{h}.js→/s/{store}/ajax/*Shopify AJAX APIany host /api/*→app/api/* (no rewrite)GraphQL · OAuth · webhooks · cronFlow
Request lifecycle
Every page request
Admin server action (mutation)
Flow
Auth, staff & permissions
Login → permission check
Continue with Google (OIDC + PKCE)
Flow
Cart, checkout & payments
Cart → order
createOrder transaction
Lock & price
lines · totals · tax lines
Upsert customer
orders_count · total_spent
Reserve inventory
committed +qty per location
Gift card charge
gift_card_transactions
Order + lines + events
Clear cart
after()
emails · win-back · welcome · markRecovered
emitStoreEvent
orders/create · orders/paid
Flow
Events, webhooks & Flow
One event, every consumer
Domain change
createOrder · fulfill · refund · product save
emitStoreEvent(topic)
lib/events.ts
Webhooks
subscriptions × scope check
App endpoint
Flow automation run
runFlows(trigger)
after() the response
Conditions
all / any · typed operators
Actions
tag · note · email · webhook …
Wait
flow.resume job
flow_runs
status + log
Flow
Order operations
Fulfil → deliver → thank-you
Order editing
Flow
Email delivery
sendNotification → sendEmail
Template
store override ?? default
Liquid render
body escaped · subject plain
Marketing?
+ unsubscribe_url
Store mailbox
Gmail · Workspace · SMTP
Reserve quota
sent_today < daily_limit
Platform sender
SMTP_URL, as the store
email_messages
via · status · order_id
Flow
Email automations
Order updates (transactional)
Order event
placed · shipped · delivered · edited · cancelled · refunded
automationEnabled?
email_automations
sendNotification
{ orderId }
Order page log
Marketing automations
Trigger
subscribe · fulfilled/delivered · order
queue*
welcome · post_purchase · win_back
email.automation job
after delay
Re-check
enabled · subscribed · not sent · no newer order
Send + unsubscribe
Abandoned checkout recovery
Checkout with email
recordCheckout
checkout.recovery job
delay 1–24h
Still open?
/cart/recover/{token}
Cart restored
→ order → recovered_order_id
Flow
Email campaigns
Compose → send → track
Flow
Apps platform
Public app install (OAuth 2.0)
Admin GraphQL API call
Flow
Themes & storefront rendering
Liquid render pipeline
Storefront context
store · market · locale · currency · cart
Main theme
themes.role = main · ?preview_theme
templates/{type}.json
section order + settings
sections/*.liquid
{% schema %} · blocks
layout/theme.liquid
header/footer groups · snippets
liquidjs
globals: settings · shop · cart · request
App embeds
settings_data current.blocks
HTML + /api/theme-assets
Image pipeline
Admin upload
≤ 4 MB per file
sharp
rotate · strip metadata · WebP
Storage
{base}-400/-1000/-1800.webp · Supabase S3
Liquid render
addImageSrcSets · image_url: width
Browser
srcset picks the smallest sharp size
Theme editor
Editor UI
sections · theme settings · app embeds
liquid-sync
JSON ↔ editor model
theme_files
settings_data · templates/*.json
theme_file_versions
Preview iframe
?preview_theme (signed)
Flow
Sellify CLI
Local theme development (sellify theme dev)
App development (sellify app init · app dev)
sellify login (browser, loopback + PKCE)
Distribution & auth
npm: sellify-cli
packages/cli · zero deps · Node 20+
sellify login
browser + PKCE · or --token (CI)
~/.config/sellify
token · 0600
Admin GraphQL API
read_themes · write_themes
/api/cli/apps
your Partner apps · same rules as the dashboard
lib/theme-files
same validation as the code editor
theme_files
+ versions · preview link
Flow
Trust & safety
Report → review → suspend
Shopper
“Report this store” on every storefront
root /report
rate-limited per IP
abuse_reports
+ email to platform admins
admin /platform
PLATFORM_ADMIN_EMAILS only
stores.suspended_at
storefront → /unavailable · cart, checkout, API 403 · jobs paused
Embedded apps & app embeds
Apps → app
/store/{store}/apps/open/{handle}
iframe: app URL
signed link · embedded=1 · id_token (JWT, client secret)
App embeds
public_apps.embeds → Theme editor → App embeds
Storefront
<script src defer data-shop data-store> while installed + switched on
App Store review & kill switch
Partner lists app
review_status = pending · ≤ 10 test installs
Platform admin
install to test on own store · remarks · approve · request changes
App Store
listed ∧ approved ∧ not suspended
read_customer_pii
else names/emails/phones/streets null in API + webhooks
Suspend
tokens 403 · webhooks stop · installs blocked
Error monitoring
onRequestError
pages · actions · routes · final job failures
error_events
grouped by fingerprint · count
once per new error
Sentry (optional)
SENTRY_DSN · envelope API
Flow
Analytics
Collect → report
Storefront events
themes/analytics.tsx
POST /api/analytics/collect
session · page_view · add_to_cart …
storefront_events
Dashboards & reports
lib/analytics/*
SellifyQL
→ parameterized, store-scoped, read-only SQL
Live view
poll 5s
Flow
Sidekick (admin assistant)
A guided task (e.g. fulfil an order)
Guided tasks, human in the loop
Merchant
“create a discount” · “fulfil #1042” · “add a product”
lib/sidekick/engine
rule-based: reports & lookups, or start a task
tasks/index.ts
8 tasks declared as fields (no per-task conversation code)
tasks/engine.ts
pre-fill from the sentence → ask what's missing → Back / Change / Cancel
Confirm card
nothing changes until the merchant clicks
Same code as the admin pages
saveProductRecord · prepareDiscount · fulfillAction · issueCard …
Questions about the store's records (retrieval, no AI)
Merchant
“list my products” · “how many customers” · “price of linen kurta”
lookup.listOrCount
record type + filters (status, price, COD…); leftover words → not this
lookup.productQuestion
price / stock / about + product words scored against titles
Postgres
counts, lists, stock = on_hand − committed, sold in 30 days
Last resort: searchStore
after typo fixes: products, customers, collections, discount codes
Flow
Background jobs
Durable queue in Postgres
enqueue()
type · payload · run_at · key
jobs
Trigger
local poller 20s · Vercel Cron /api/cron/jobs
Claim
UPDATE … FOR UPDATE SKIP LOCKED
Handler by type
done (key → null)
Flow
Custom domains & markets
Custom domain
Add domain
TXT verification
*.localhost auto-verified locally
Set primary
proxy
subdomain GET → 308 primary
Unknown host
/s/~host → domains lookup
Selling worldwide
Country / currency
/localization · geo
Shipping zone
by country + state
Tax
GST in India · exports zero-rated
FX
presentment currency · daily rates
Gateway
COD India-only · Razorpay Intl · Stripe · PayPal
Lifecycles
Order · financial_status
- pending—payment captured / mark paid→paid
- pending—edit raises total→partially_paid
- partially_paid—balance collected→paid
- paid—refund < paid→partially_refunded
- paid—refund = paid→refunded
- pending—cancel unpaid→voided
Order · fulfillment_status
- unfulfilled—fulfil some→partially_fulfilled
- partially_fulfilled—fulfil rest→fulfilled
- unfulfilled—fulfil all→fulfilled
Order · status
- open—archive→closed
- closed—unarchive→open
- open—cancel (nothing fulfilled)→cancelled
Fulfillment · shipment_status
- in_transit—mark (email)→out_for_delivery
- out_for_delivery—mark (email + post-purchase)→delivered
- out_for_delivery—mark→failed_delivery
- failed_delivery—retry→out_for_delivery
Email campaign · status
- draft—schedule→scheduled
- scheduled—unschedule→draft
- draft—send now→sending
- scheduled—job at time→sending
- sending—paused (daily limit)→sending
- sending—no pending left→sent
Job · status
- pending—claimed (SKIP LOCKED)→running
- running—handler ok→done
- running—error, retry with backoff→pending
- running—5th error→failed
- pending—cancelJobs(key)→cancelled
enum collection_type
manualsmart
enum discount_type
percentagefixed_amountfree_shippingbuy_x_get_y
enum financial_status
pendingpaidpartially_refundedrefundedvoidedpartially_paid
enum fulfillment_status
unfulfilledpartially_fulfilledfulfilled
enum inventory_policy
denycontinue
enum member_role
ownerstaff
enum order_status
openclosedcancelled
enum product_status
draftactivearchived
enum store_status
draftpublished
enum theme_role
mainunpublished
Data model · 66 tables
Most referenced
stores53users8products4product_variants4orders4locations3customers2themes2Accounts & stores
auth_throttle3 cols · 0 idx
auth_throttle3 cols · 0 idx- 🔑 keytext
- countinteger
- reset_attimestamp with time zone
domains9 cols · 1 idx
domains9 cols · 1 idx- 🔑 iduuid
- store_iduuid
- hostnametext
- verification_tokentext
- statustext
- verified_attimestamp with time zone?
- last_checked_attimestamp with time zone?
- last_errortext?
- created_attimestamp with time zone
google_logins8 cols · 0 idx
google_logins8 cols · 0 idx- 🔑 state_hashtext
- bind_hashtext
- code_verifiertext
- nexttext?
- user_iduuid?
- new_userboolean
- ticket_hashtext?
- expires_attimestamp with time zone
password_resets4 cols · 0 idx
password_resets4 cols · 0 idx- 🔑 token_hashtext
- user_iduuid
- expires_attimestamp with time zone
- created_attimestamp with time zone
sessions4 cols · 1 idx
sessions4 cols · 1 idx- 🔑 idtext
- user_iduuid
- expires_attimestamp with time zone
- created_attimestamp with time zone
staff_roles7 cols · 1 idx
staff_roles7 cols · 1 idx- 🔑 iduuid
- store_iduuid
- nametext
- descriptiontext
- permissionstext[]
- created_attimestamp with time zone
- updated_attimestamp with time zone
store_members6 cols · 1 idx
store_members6 cols · 1 idx- 🔑 store_iduuid
- 🔑 user_iduuid
- rolemember_role
- role_keytext?
- permissionstext[]
- created_attimestamp with time zone
stores26 cols · 0 idx
stores26 cols · 0 idx- 🔑 iduuid
- nametext
- subdomaintext
- contact_emailtext
- currencytext
- countrytext
- timezonetext
- statusstore_status
- storefront_passwordtext
- published_attimestamp with time zone?
- seo_titletext
- seo_descriptiontext
- statetext
- tax_settingsjsonb
- order_counterinteger
- consentjsonb
- currenciesjsonb
- localestext[]
- recovery_email_delay_hoursinteger
- businessjsonb
- policiesjsonb
- primary_domaintext?
- suspended_attimestamp with time zone?
- suspended_reasontext?
- created_attimestamp with time zone
- updated_attimestamp with time zone
users8 cols · 0 idx
users8 cols · 0 idx- 🔑 iduuid
- emailtext
- nametext
- password_hashtext
- google_subtext?
- email_verified_attimestamp with time zone?
- created_attimestamp with time zone
- updated_attimestamp with time zone
Catalog
collection_products3 cols · 1 idx
collection_products3 cols · 1 idx- 🔑 collection_iduuid
- 🔑 product_iduuid
- positioninteger
collections14 cols · 1 idx
collections14 cols · 1 idx- 🔑 iduuid
- store_iduuid
- titletext
- handletext
- description_htmltext
- image_urltext?
- typecollection_type
- rulesjsonb
- sort_ordertext
- publishedboolean
- seo_titletext
- seo_descriptiontext
- created_attimestamp with time zone
- updated_attimestamp with time zone
product_media6 cols · 1 idx
product_media6 cols · 1 idx- 🔑 iduuid
- store_iduuid
- product_iduuid
- urltext
- alttext
- positioninteger
product_variants21 cols · 2 idx
product_variants21 cols · 2 idx- 🔑 iduuid
- store_iduuid
- product_iduuid
- titletext
- option1text?
- option2text?
- option3text?
- skutext
- barcodetext
- priceinteger
- compare_at_priceinteger?
- cost_per_iteminteger?
- taxableboolean
- requires_shippingboolean
- weight_gramsinteger
- track_inventoryboolean
- inventory_policyinventory_policy
- positioninteger
- image_urltext?
- created_attimestamp with time zone
- updated_attimestamp with time zone
products14 cols · 2 idx
products14 cols · 2 idx- 🔑 iduuid
- store_iduuid
- titletext
- handletext
- description_htmltext
- vendortext
- product_typetext
- statusproduct_status
- tagstext[]
- optionsjsonb
- seo_titletext
- seo_descriptiontext
- created_attimestamp with time zone
- updated_attimestamp with time zone
Inventory
inventory_adjustments10 cols · 1 idx
inventory_adjustments10 cols · 1 idx- 🔑 iduuid
- store_iduuid
- variant_iduuid
- location_iduuid
- fieldtext
- deltainteger
- reasontext
- referencetext?
- actortext
- created_attimestamp with time zone
inventory_levels7 cols · 1 idx
inventory_levels7 cols · 1 idx- 🔑 variant_iduuid
- 🔑 location_iduuid
- store_iduuid
- on_handinteger
- committedinteger
- incominginteger
- updated_attimestamp with time zone
locations12 cols · 0 idx
locations12 cols · 0 idx- 🔑 iduuid
- store_iduuid
- nametext
- address1text
- citytext
- statetext
- pincodetext
- phonetext
- is_defaultboolean
- activeboolean
- created_attimestamp with time zone
- updated_attimestamp with time zone
Online store
pages10 cols · 1 idx
pages10 cols · 1 idx- 🔑 iduuid
- store_iduuid
- titletext
- handletext
- body_htmltext
- publishedboolean
- seo_titletext
- seo_descriptiontext
- created_attimestamp with time zone
- updated_attimestamp with time zone
themes9 cols · 1 idx
themes9 cols · 1 idx- 🔑 iduuid
- store_iduuid
- nametext
- presettext
- roletheme_role
- enginetext
- datajsonb
- created_attimestamp with time zone
- updated_attimestamp with time zone
Customers
customers15 cols · 1 idx
customers15 cols · 1 idx- 🔑 iduuid
- store_iduuid
- emailtext
- first_nametext
- last_nametext
- phonetext
- accepts_marketingboolean
- default_addressjsonb?
- addressesjsonb
- notetext
- tagstext[]
- orders_countinteger
- total_spentinteger
- created_attimestamp with time zone
- updated_attimestamp with time zone
Settings: shipping & payments
email_automations5 cols · 0 idx
email_automations5 cols · 0 idx- 🔑 store_iduuid
- 🔑 keytext
- enabledboolean
- delay_hoursinteger
- updated_attimestamp with time zone
email_campaign_recipients13 cols · 2 idx
email_campaign_recipients13 cols · 2 idx- 🔑 iduuid
- campaign_iduuid
- store_iduuid
- customer_iduuid?
- emailtext
- first_nametext
- tokentext
- statustext
- errortext?
- sent_attimestamp with time zone?
- opened_attimestamp with time zone?
- clicked_attimestamp with time zone?
- clicksinteger
email_campaigns14 cols · 1 idx
email_campaigns14 cols · 1 idx- 🔑 iduuid
- store_iduuid
- nametext
- subjecttext
- preview_texttext
- blocksjsonb
- audiencejsonb
- statustext
- scheduled_attimestamp with time zone?
- sent_attimestamp with time zone?
- paused_reasontext?
- created_bytext
- created_attimestamp with time zone
- updated_attimestamp with time zone
email_messages10 cols · 2 idx
email_messages10 cols · 2 idx- 🔑 iduuid
- store_iduuid
- order_iduuid?
- totext
- template_keytext
- subjecttext
- viatext
- statustext
- errortext?
- created_attimestamp with time zone
email_senders15 cols · 0 idx
email_senders15 cols · 0 idx- 🔑 store_iduuid
- providertext
- sender_nametext
- sender_emailtext
- reply_totext
- smtp_hosttext
- smtp_portinteger
- smtp_usertext
- smtp_passwordtext
- daily_limitinteger
- sent_todayinteger
- sent_daytext
- verified_attimestamp with time zone?
- last_errortext?
- updated_attimestamp with time zone
payment_settings20 cols · 0 idx
payment_settings20 cols · 0 idx- 🔑 store_iduuid
- razorpay_enabledboolean
- razorpay_key_idtext
- razorpay_key_secrettext
- razorpay_webhook_secrettext
- cod_enabledboolean
- cod_feeinteger
- cod_min_totalinteger?
- cod_max_totalinteger?
- cod_pincodestext[]
- razorpay_internationalboolean
- stripe_enabledboolean
- stripe_publishable_keytext
- stripe_secret_keytext
- stripe_webhook_secrettext
- paypal_enabledboolean
- paypal_client_idtext
- paypal_client_secrettext
- paypal_modetext
- updated_attimestamp with time zone
shipping_rates8 cols · 0 idx
shipping_rates8 cols · 0 idx- 🔑 iduuid
- store_iduuid
- zone_iduuid
- nametext
- priceinteger
- conditiontext
- mininteger?
- maxinteger?
shipping_zones7 cols · 0 idx
shipping_zones7 cols · 0 idx- 🔑 iduuid
- store_iduuid
- nametext
- countriestext[]
- statestext[]
- created_attimestamp with time zone
- updated_attimestamp with time zone
Discounts
discounts20 cols · 1 idx
discounts20 cols · 1 idx- 🔑 iduuid
- store_iduuid
- titletext
- codetext?
- typediscount_type
- valueinteger
- applies_tojsonb
- min_requirementtext
- min_valueinteger
- buy_quantityinteger
- buy_targetjsonb
- get_quantityinteger
- usage_limitinteger?
- once_per_customerboolean
- usage_countinteger
- starts_attimestamp with time zone
- ends_attimestamp with time zone?
- activeboolean
- created_attimestamp with time zone
- updated_attimestamp with time zone
Cart & checkout
cart_lines4 cols · 0 idx
cart_lines4 cols · 0 idx- 🔑 cart_iduuid
- 🔑 variant_iduuid
- quantityinteger
- created_attimestamp with time zone
carts5 cols · 0 idx
carts5 cols · 0 idx- 🔑 iduuid
- store_iduuid
- tokentext
- created_attimestamp with time zone
- updated_attimestamp with time zone
checkouts15 cols · 0 idx
checkouts15 cols · 0 idx- 🔑 iduuid
- store_iduuid
- tokentext
- cart_tokentext
- inputjsonb
- snapshotjsonb
- totalinteger
- razorpay_order_idtext?
- providertext
- provider_reftext?
- charge_currencytext?
- charge_amountinteger?
- order_iduuid?
- created_attimestamp with time zone
- updated_attimestamp with time zone
Orders
fulfillments10 cols · 1 idx
fulfillments10 cols · 1 idx- 🔑 iduuid
- order_iduuid
- store_iduuid
- linesjsonb
- tracking_companytext
- tracking_numbertext
- tracking_urltext
- shipment_statustext
- delivered_attimestamp with time zone?
- created_attimestamp with time zone
order_events7 cols · 1 idx
order_events7 cols · 1 idx- 🔑 iduuid
- order_iduuid
- store_iduuid
- kindtext
- messagetext
- authortext
- created_attimestamp with time zone
order_lines18 cols · 2 idx
order_lines18 cols · 2 idx- 🔑 iduuid
- order_iduuid
- store_iduuid
- product_iduuid?
- variant_iduuid?
- location_iduuid?
- titletext
- variant_titletext?
- skutext
- image_urltext?
- priceinteger
- quantityinteger
- discount_allocationinteger
- taxableboolean
- requires_shippingboolean
- track_inventoryboolean
- fulfilled_quantityinteger
- refunded_quantityinteger
orders37 cols · 3 idx
orders37 cols · 3 idx- 🔑 iduuid
- store_iduuid
- numberinteger
- tokentext
- customer_iduuid?
- emailtext
- phonetext
- financial_statusfinancial_status
- fulfillment_statusfulfillment_status
- statusorder_status
- currencytext
- subtotalinteger
- discount_totalinteger
- shipping_totalinteger
- tax_totalinteger
- taxes_includedboolean
- payment_feeinteger
- totalinteger
- gift_card_amountinteger
- presentment_currencytext?
- presentment_totalinteger?
- localetext
- total_paidinteger
- total_refundedinteger
- tax_linesjsonb
- discountsjsonb
- shipping_linejsonb?
- shipping_addressjsonb?
- gatewaytext
- sourcetext
- notetext
- tagstext[]
- cancelled_attimestamp with time zone?
- cancel_reasontext?
- closed_attimestamp with time zone?
- created_attimestamp with time zone
- updated_attimestamp with time zone
Uploaded files (when no S3/R2 is configured on a read-only host such as Vercel)
stored_files5 cols · 0 idx
stored_files5 cols · 0 idx- 🔑 keytext
- content_typetext
- databytea
- sizeinteger
- created_attimestamp with time zone
Theme code (Liquid sections/snippets, custom assets)
Apps
app_installations6 cols · 0 idx
app_installations6 cols · 0 idx- 🔑 store_iduuid
- 🔑 app_keytext
- enabledboolean
- configjsonb
- created_attimestamp with time zone
- updated_attimestamp with time zone
cli_auth_codes6 cols · 0 idx
cli_auth_codes6 cols · 0 idx- 🔑 code_hashtext
- store_iduuid
- user_iduuid
- code_challengetext
- devicetext
- expires_attimestamp with time zone
custom_apps13 cols · 2 idx
custom_apps13 cols · 2 idx- 🔑 iduuid
- store_iduuid
- nametext
- scopestext[]
- token_hashtext?
- token_hinttext?
- webhook_secrettext
- last_used_attimestamp with time zone?
- created_bytext
- public_app_iduuid?
- cli_user_iduuid?
- created_attimestamp with time zone
- updated_attimestamp with time zone
oauth_codes8 cols · 0 idx
oauth_codes8 cols · 0 idx- 🔑 code_hashtext
- public_app_iduuid
- store_iduuid
- scopestext[]
- redirect_uritext
- user_nametext
- expires_attimestamp with time zone
- used_attimestamp with time zone?
public_apps27 cols · 1 idx
public_apps27 cols · 1 idx- 🔑 iduuid
- developer_iduuid
- developer_nametext
- handletext
- nametext
- taglinetext
- descriptiontext
- categorytext
- colortext
- pricingtext
- support_emailtext
- app_urltext
- redirect_urlstext[]
- scopestext[]
- uninstall_webhook_urltext
- client_idtext
- client_secrettext
- listedboolean
- review_statustext
- review_notetext?
- reviewed_attimestamp with time zone?
- pii_accessboolean
- suspended_attimestamp with time zone?
- embedsjsonb
- embeddedboolean
- created_attimestamp with time zone
- updated_attimestamp with time zone
webhook_deliveries8 cols · 1 idx
webhook_deliveries8 cols · 1 idx- 🔑 iduuid
- store_iduuid
- subscription_iduuid
- topictext
- response_statusinteger?
- errortext?
- duration_msinteger
- created_attimestamp with time zone
webhook_subscriptions6 cols · 1 idx
webhook_subscriptions6 cols · 1 idx- 🔑 iduuid
- store_iduuid
- app_iduuid
- topictext
- addresstext
- created_attimestamp with time zone
Analytics, reports, Flow
flow_runs7 cols · 1 idx
flow_runs7 cols · 1 idx- 🔑 iduuid
- flow_iduuid
- store_iduuid
- statustext
- subjecttext
- stepsjsonb
- created_attimestamp with time zone
flows13 cols · 1 idx
flows13 cols · 1 idx- 🔑 iduuid
- store_iduuid
- nametext
- enabledboolean
- triggertext
- matchtext
- conditionsjsonb
- actionsjsonb
- else_actionsjsonb
- run_countinteger
- last_run_attimestamp with time zone?
- created_attimestamp with time zone
- updated_attimestamp with time zone
saved_reports7 cols · 0 idx
saved_reports7 cols · 0 idx- 🔑 iduuid
- store_iduuid
- nametext
- querytext
- created_bytext
- created_attimestamp with time zone
- updated_attimestamp with time zone
storefront_events12 cols · 2 idx
storefront_events12 cols · 2 idx- 🔑 iduuid
- store_iduuid
- visitor_idtext
- session_idtext
- nametext
- pathtext
- referrertext
- sourcetext
- devicetext
- product_iduuid?
- valueinteger?
- created_attimestamp with time zone
Background jobs (Flow waits, recovery emails)
jobs10 cols · 2 idx
jobs10 cols · 2 idx- 🔑 iduuid
- store_iduuid
- typetext
- payloadjsonb
- run_attimestamp with time zone
- statustext
- attemptsinteger
- last_errortext?
- keytext?
- created_attimestamp with time zone
Blogs
articles16 cols · 2 idx
articles16 cols · 2 idx- 🔑 iduuid
- store_iduuid
- blog_iduuid
- titletext
- handletext
- authortext
- body_htmltext
- excerpttext
- image_urltext?
- tagstext[]
- publishedboolean
- published_attimestamp with time zone
- seo_titletext
- seo_descriptiontext
- created_attimestamp with time zone
- updated_attimestamp with time zone
blogs6 cols · 1 idx
blogs6 cols · 1 idx- 🔑 iduuid
- store_iduuid
- titletext
- handletext
- created_attimestamp with time zone
- updated_attimestamp with time zone
Customer accounts (passwordless email codes)
customer_login_codes7 cols · 1 idx
customer_login_codes7 cols · 1 idx- 🔑 iduuid
- store_iduuid
- emailtext
- code_hashtext
- attemptsinteger
- expires_attimestamp with time zone
- created_attimestamp with time zone
customer_sessions5 cols · 0 idx
customer_sessions5 cols · 0 idx- 🔑 idtext
- store_iduuid
- customer_iduuid
- expires_attimestamp with time zone
- created_attimestamp with time zone
Staff
staff_invites10 cols · 0 idx
staff_invites10 cols · 0 idx- 🔑 iduuid
- store_iduuid
- emailtext
- role_keytext?
- permissionstext[]
- token_hashtext
- invited_bytext
- expires_attimestamp with time zone
- accepted_attimestamp with time zone?
- created_attimestamp with time zone
Abandoned checkouts
abandoned_checkouts16 cols · 2 idx
abandoned_checkouts16 cols · 2 idx- 🔑 iduuid
- store_iduuid
- cart_tokentext
- emailtext
- nametext
- phonetext
- accepts_marketingboolean
- linesjsonb
- subtotalinteger
- recovery_tokentext
- recovery_email_sent_attimestamp with time zone?
- recovered_order_iduuid?
- shipping_addressjsonb?
- discount_codetext
- created_attimestamp with time zone
- updated_attimestamp with time zone
Gift cards
gift_card_transactions7 cols · 1 idx
gift_card_transactions7 cols · 1 idx- 🔑 iduuid
- gift_card_iduuid
- store_iduuid
- order_iduuid?
- amountinteger
- notetext
- created_attimestamp with time zone
gift_cards14 cols · 1 idx
gift_cards14 cols · 1 idx- 🔑 iduuid
- store_iduuid
- code_hashtext
- last_characterstext
- initial_valueinteger
- balanceinteger
- currencytext
- customer_emailtext
- notetext
- expires_ontimestamp with time zone?
- disabled_attimestamp with time zone?
- created_bytext
- created_attimestamp with time zone
- updated_attimestamp with time zone
Notification templates
notification_templates5 cols · 0 idx
notification_templates5 cols · 0 idx- 🔑 store_iduuid
- 🔑 keytext
- subjecttext
- bodytext
- updated_attimestamp with time zone
Translations (languages)
translations7 cols · 0 idx
translations7 cols · 0 idx- 🔑 store_iduuid
- 🔑 resource_typetext
- 🔑 resource_idtext
- 🔑 localetext
- 🔑 fieldtext
- valuetext
- updated_attimestamp with time zone
Platform trust & safety
abuse_reports8 cols · 1 idx
abuse_reports8 cols · 1 idx- 🔑 iduuid
- store_iduuid?
- subdomaintext
- reasontext
- detailstext
- reporter_emailtext?
- statustext
- created_attimestamp with time zone
email_verifications3 cols · 0 idx
email_verifications3 cols · 0 idx- 🔑 token_hashtext
- user_iduuid
- expires_attimestamp with time zone
error_events10 cols · 0 idx
error_events10 cols · 0 idx- 🔑 fingerprinttext
- messagetext
- routetext
- kindtext
- stacktext
- digesttext?
- countinteger
- first_seentimestamp with time zone
- last_seentimestamp with time zone
- resolved_attimestamp with time zone?
Route map · 143 routes
Admin
admin.root- ACTION/ · 6 actions
- ACTION/ · 7 actions
- ACTION/cli/authorize · 2 actions
- ACTION/invite · 3 actions
- ACTION/oauth/authorize · 2 actions
- ACTION/partners · 5 actions
- ACTION/platform · 10 actions
- ACTION/sidekick · 2 actions
- ACTION/store/[handle]/analytics/live · 1 action
- ACTION/store/[handle]/analytics/query · 3 actions
- ACTION/store/[handle]/apps · 12 actions
- ACTION/store/[handle]/apps/open/[app] · 1 action
- ACTION/store/[handle]/code/[themeId] · 4 actions
- ACTION/store/[handle]/collections · 2 actions
- ACTION/store/[handle]/content · 4 actions
- ACTION/store/[handle]/customers · 1 action
- ACTION/store/[handle]/discounts · 2 actions
- ACTION/store/[handle]/flow · 4 actions
- ACTION/store/[handle]/gift-cards · 3 actions
- ACTION/store/[handle]/inventory · 1 action
- ACTION/store/[handle]/marketing/automations · 1 action
- ACTION/store/[handle]/marketing/campaigns · 7 actions
- ACTION/store/[handle]/online-store/themes · 6 actions
- ACTION/store/[handle]/orders · 12 actions
- ACTION/store/[handle]/orders/abandoned · 2 actions
- ACTION/store/[handle]/products · 3 actions
- ACTION/store/[handle]/settings · 11 actions
- ACTION/store/[handle]/settings/domains · 0 actions
- ACTION/store/[handle]/settings/email · 3 actions
- ACTION/store/[handle]/settings/languages · 2 actions
- ACTION/store/[handle]/settings/markets · 2 actions
- ACTION/store/[handle]/settings/notifications · 4 actions
- ACTION/store/[handle]/settings/policies · 2 actions
- ACTION/store/[handle]/settings/users · 7 actions
- HTTP/auth/google
- HTTP/auth/google/finish
- HTTP/store/[handle]/customers/export
- HTTP/store/[handle]/products/export
- HTTP/verify-email
- PAGE/
- PAGE/cli/authorize
- PAGE/forgot-password
- PAGE/invite/[token]
- PAGE/login
- PAGE/no-access
- PAGE/oauth/authorize
- PAGE/partners
- PAGE/partners/apps/[id]
- PAGE/platform
- PAGE/platform/apps
- PAGE/platform/errors
- PAGE/platform/reports
- PAGE/platform/stores
- PAGE/reset-password
- PAGE/signup
- PAGE/store/[handle]
- PAGE/store/[handle]/[...section]
- PAGE/store/[handle]/analytics
- PAGE/store/[handle]/analytics/live
- PAGE/store/[handle]/analytics/query
- PAGE/store/[handle]/analytics/reports
- PAGE/store/[handle]/apps
- PAGE/store/[handle]/apps/[key]
- PAGE/store/[handle]/apps/develop
- PAGE/store/[handle]/apps/develop/[id]
- PAGE/store/[handle]/apps/installed/[id]
- PAGE/store/[handle]/apps/listing/[app]
- PAGE/store/[handle]/apps/open/[app]
- PAGE/store/[handle]/code/[themeId]
- PAGE/store/[handle]/collections
- PAGE/store/[handle]/collections/[id]
- PAGE/store/[handle]/collections/new
- PAGE/store/[handle]/content
- PAGE/store/[handle]/content/blog
- PAGE/store/[handle]/content/blog/[id]
- PAGE/store/[handle]/content/blog/new
- PAGE/store/[handle]/content/menus
- PAGE/store/[handle]/content/menus/[id]
- PAGE/store/[handle]/content/menus/new
- PAGE/store/[handle]/content/pages
- PAGE/store/[handle]/content/pages/[id]
- PAGE/store/[handle]/content/pages/new
- PAGE/store/[handle]/customers
- PAGE/store/[handle]/customers/[id]
- PAGE/store/[handle]/discounts
- PAGE/store/[handle]/discounts/[id]
- PAGE/store/[handle]/discounts/new
- PAGE/store/[handle]/editor/[themeId]
- PAGE/store/[handle]/flow
- PAGE/store/[handle]/flow/[id]
- PAGE/store/[handle]/flow/new
- PAGE/store/[handle]/gift-cards
- PAGE/store/[handle]/gift-cards/[id]
- PAGE/store/[handle]/inventory
- PAGE/store/[handle]/marketing
- PAGE/store/[handle]/marketing/automations
- PAGE/store/[handle]/marketing/campaigns
- PAGE/store/[handle]/marketing/campaigns/[id]
- PAGE/store/[handle]/online-store
- PAGE/store/[handle]/online-store/preferences
- PAGE/store/[handle]/online-store/themes
- PAGE/store/[handle]/orders
- PAGE/store/[handle]/orders/[id]
- PAGE/store/[handle]/orders/[id]/edit
- PAGE/store/[handle]/orders/abandoned
- PAGE/store/[handle]/orders/abandoned/[id]
- PAGE/store/[handle]/orders/new
- PAGE/store/[handle]/products
- PAGE/store/[handle]/products/[id]
- PAGE/store/[handle]/products/import
- PAGE/store/[handle]/products/new
- PAGE/store/[handle]/settings
- PAGE/store/[handle]/settings/domains
- PAGE/store/[handle]/settings/email
- PAGE/store/[handle]/settings/languages
- PAGE/store/[handle]/settings/languages/[locale]
- PAGE/store/[handle]/settings/locations
- PAGE/store/[handle]/settings/markets
- PAGE/store/[handle]/settings/notifications
- PAGE/store/[handle]/settings/notifications/[key]
- PAGE/store/[handle]/settings/payments
- PAGE/store/[handle]/settings/policies
- PAGE/store/[handle]/settings/shipping
- PAGE/store/[handle]/settings/taxes
- PAGE/store/[handle]/settings/users
- PAGE/stores
- PAGE/stores/new
Storefront
{store}.root- ACTION/ · 1 action
- ACTION/account · 6 actions
- ACTION/checkout · 3 actions
- ACTION/password · 1 action
- HTTP/ajax/[...path]
- HTTP/cart/add
- HTTP/cart/change
- HTTP/cart/recover/[token]
- HTTP/checkout/paypal/return
- HTTP/checkout/stripe/return
- HTTP/contact
- HTTP/email/c/[token]
- HTTP/email/o/[token]
- HTTP/localization
- HTTP/unsubscribe/confirm
- PAGE/
- PAGE/account
- PAGE/account/addresses
- PAGE/account/login
- PAGE/blogs/[blog]
- PAGE/blogs/[blog]/[article]
- PAGE/cart
- PAGE/checkout
- PAGE/collections
- PAGE/collections/[handle]
- PAGE/orders/[token]
- PAGE/pages/[handle]
- PAGE/password
- PAGE/policies/[handle]
- PAGE/products/[handle]
- PAGE/search
- PAGE/unavailable
- PAGE/unsubscribe
Developer portal
dev.root- PAGE/
- PAGE/docs/[[...slug]]
- PAGE/explorer
Marketing site
root- ACTION/report · 1 action
- HTTP/auth/google/callback
- PAGE/
- PAGE/architecture
- PAGE/legal/[doc]
- PAGE/report
HTTP API
any host- HTTP/api/admin/[version]/graphql
- HTTP/api/admin/uploads
- HTTP/api/analytics/collect
- HTTP/api/cli/apps
- HTTP/api/cli/apps/[id]
- HTTP/api/cli/token
- HTTP/api/cron/jobs
- HTTP/api/files/[...key]
- HTTP/api/health
- HTTP/api/oauth/access_token
- HTTP/api/theme-assets/[themeId]/[file]
- HTTP/api/webhooks/razorpay/[storeId]
- HTTP/api/webhooks/stripe/[storeId]
Contracts
Store events → webhooks & Flow
orders/createread_ordersorders/paidread_ordersorders/editedread_ordersorders/fulfilledread_ordersorders/cancelledread_ordersrefunds/createread_ordersproducts/createread_productsproducts/updateread_productsproducts/deleteread_products
Background job types
flow.resumeflow/engine.ts → resumeFlowJobcheckout.recoveryabandoned.ts → sendRecoveryEmailemail.automationemail-automations.ts → runAutomationJobcampaign.sendcampaigns.ts → runCampaignJob
Admin API scopes
versions 2026-10read_productsRead products, variants, images and collectionswrite_productsCreate, update and delete products and variantsread_inventoryRead inventory levels and locationswrite_inventorySet inventory quantitiesread_ordersRead orders, line items and fulfillmentswrite_ordersMark orders as paid and create fulfillmentsread_customersRead customersread_customer_piiSee customers' names, emails, phone numbers and street addressesread_themesRead themes and their fileswrite_themesCreate, edit, publish and delete themes and their filesread_discountsRead discount codeswrite_discountsCreate and delete discount codes
bucket 1000restore/s 50max cost 1000max depth 8max page 100
Email templates
order_confirmationCustomershipping_confirmationCustomerorder_cancelledCustomerorder_editedCustomerout_for_deliveryCustomerdeliveredCustomerrefund_issuedCustomerorder_messageCustomerwelcome_subscriberCustomer (marketing)post_purchaseCustomer (marketing)win_backCustomer (marketing)abandoned_checkoutCustomercustomer_login_codeCustomergift_card_issuedCustomerstaff_inviteStaffnew_order_staffStaff
Email automations
order_confirmationOrder updates · onshipping_confirmationOrder updates · onout_for_deliveryOrder updates · ondeliveredOrder updates · onorder_editedOrder updates · onorder_cancelledOrder updates · onrefund_issuedOrder updates · onabandoned_checkoutRecover sales · delay 1/6/10/24h · onwelcomeMarketing · delay 0/1/24h · offpost_purchaseMarketing · delay 24/72/168/336h · offwin_backMarketing · delay 720/1440/2160/4320h · off
Flow
Triggers
orders/createorderorders/paidorderorders/fulfilledorderorders/cancelledorderrefunds/createorderproducts/createproductproducts/updateproductinventory/changedinventory
Actions
add_order_tagsorderremove_order_tagsorderadd_order_noteorderadd_customer_tagsorderadd_product_tagsproduct · inventoryremove_product_tagsproduct · inventoryset_product_statusproduct · inventorywaitorder · product · inventorysend_internal_emailorder · product · inventorysend_http_requestorder · product · inventory
First-party apps
razorpayPaymentsgmailEmailgoogle-workspaceEmailgoogle-analyticsAnalyticsgoogle-tag-managerAnalyticsgoogle-adsMarketingmeta-pixelMarketingwhatsapp-chatMarketingfree-shipping-barStore design
Environment
DATABASE_URLrequiredDB_POOL_MAXoptionalNEXT_PUBLIC_ROOT_DOMAINrequiredSESSION_SECRETrequiredENCRYPTION_KEYrequiredFILE_STORAGErequiredS3_ENDPOINTrequiredS3_BUCKETrequiredS3_ACCESS_KEY_IDrequiredS3_SECRET_ACCESS_KEYrequiredS3_PUBLIC_URLrequiredSMTP_URLoptionalEMAIL_FROMoptionalCRON_SECREToptionalVERCEL_TOKENoptionalVERCEL_PROJECT_IDoptionalVERCEL_TEAM_IDoptionalCUSTOM_DOMAIN_CNAMEoptionalCUSTOM_DOMAIN_AoptionalGOOGLE_CLIENT_IDrequiredGOOGLE_CLIENT_SECRETrequiredUPSTASH_REDIS_REST_URLoptionalUPSTASH_REDIS_REST_TOKENoptionalDISABLE_JOB_POLLERoptionalSTRIPE_API_BASEoptionalPAYPAL_API_BASEoptionalFX_API_BASEoptionalGMAIL_SMTP_HOSToptionalGMAIL_SMTP_PORToptionalGOOGLE_OAUTH_BASEoptionalPLATFORM_ADMIN_EMAILSrequiredPLATFORM_CONTACT_EMAILrequiredLEGAL_ENTITY_NAMErequiredLEGAL_ADDRESSrequiredGRIEVANCE_OFFICER_NAMErequiredSENTRY_DSNoptional
Scripts
pnpm devpnpm buildpnpm startpnpm lintpnpm typecheckpnpm db:generatepnpm db:migratepnpm db:studiopnpm db:seedpnpm seed:storepnpm simulatepnpm sellifypnpm deploy:vercelpnpm db:migrate:prod
Roles & access
| Role | Orders | Products | Customers | Content | Online Store | Analytics | Discounts | Marketing | Apps | Flow | Settings | Staff |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Owner | edit | edit | edit | edit | edit | edit | edit | edit | edit | edit | edit | edit |
| Admin | edit | edit | edit | edit | edit | edit | edit | edit | edit | edit | edit | edit |
| Store manager | edit | edit | edit | edit | view | view | edit | edit | — | edit | view | — |
| Fulfillment | edit | view | view | — | — | — | — | — | — | — | — | — |
| Customer support | view | view | edit | — | — | — | — | — | — | — | — | — |
| Marketing | — | view | view | edit | — | view | edit | edit | — | edit | — | — |
| Developer | view | view | — | — | edit | view | — | — | edit | edit | view | — |
Module map · 113 files
src/lib
88 filesabandoned.ts73LAbandoned checkout recovery: once a shopper enters their email at checkout we keep a snapshot,abuse.ts51LAbuse reports ("Report this store" on every storefront) and store suspension, handled by platform admins.admin-api/execute.ts96LThe App Store kill switch: a suspended public app's tokens stop working everywhere at once.admin-api/graphql-schema.ts5Ladmin-api/limits.ts117LCost-based throttling (leaky bucket per app), modelled on Shopify's GraphQL Admin API:admin-api/resolvers.ts550Ladmin-api/schema.ts518LGraphQL Admin API schema (SDL). Descriptions here are rendered in the developer portal reference.admin-api/scopes.ts44LAccess scopes and webhook topics for custom apps. Client-safe: used by the admin UI and the dev portal.admin-api/serialize.ts156LJSON shapes shared by the GraphQL Admin API and webhook payloads.analytics/dashboard.ts98LVisitors active in the last 5 minutes and their most recent pages.analytics/dates.ts151LDate ranges in the store's timezone. Ranges are half-open: [from, to).analytics/format.ts13LShared (server + client) number formatting for analytics.analytics/overview.ts314LData for the Analytics overview (Shopify-style dashboard). Every series is bucketed in the store'sanalytics/ql.ts425LSellifyQL — a ShopifyQL-style query language for analytics:analytics/reports.ts36LBuilt-in report library. Each report is a SellifyQL query you can open, edit and save.app-review-defs.ts12LClient-safe labels for an app's App Store state (see lib/app-review.ts for the rules).app-review.ts54LApp Store review and the app kill switch. A public app is shown in the App Store only when its developer lists itapps/logos.ts11LBrand marks from Simple Icons (CC0). Trademarks belong to their owners; shown to identify the integration.apps/registry.ts217LFirst-party apps in the Sellify App Store. Client-safe (no server imports).apps/server.ts79LPublic tracking IDs for enabled analytics/marketing apps.auth.ts70LSession expiry compares against the current time, so this must render per request.campaigns.ts326LEmail campaigns (Marketing → Campaigns): a one-off email to subscribed customers, built from blocks.cart-ajax.ts55LShopify-compatible AJAX Cart API shapes (/cart.js, /cart/add.js, …) so theme JavaScript written forcart.ts130Lcatalog.ts247LStorefront catalog queries. Smart collection rules compile to SQL so they stay live.checkout.ts239LCountries the store ships to (from its shipping zones), India first.cli-auth.ts97L`sellify login` without copying tokens (OAuth 2.0 for native apps: loopback redirect + PKCE, RFC 8252/7636).crypto.ts37LAES-256-GCM for merchant secrets (payment gateway keys). Format: iv.tag.ciphertext (base64url).currencies.ts52LISO 4217 currencies (client-safe). `decimals` is the number of minor-unit digits the currency uses, which paymentcustomer-auth.ts85LStorefront customer accounts: passwordless sign-in with a 6-digit code sent by email.discount-input.ts78LValidating a discount before it's saved. Shared by the Discounts page (saveDiscount) and Sidekick's guideddomains.ts144LCustom domains (Settings → Domains). A merchant adds "shop.mybrand.com", points it at Sellify with DNS, and provesemail-automation-defs.ts44LCustomer email automations (client-safe): what each one does, when it fires and its default timing.email-automations.ts148LEmail automations (Marketing → Email automations, also inside the Gmail app). Order-update emails checkemail-verification.ts51LEmail verification for merchant accounts: a store can only go live once its owner confirmed their email, soemail.ts152LEmail delivery.error-log.ts88LServer error monitoring without a third party: Next.js reports every server error (instrumentation.tsevents.ts20LOne entry point for store events: delivers app webhooks and runs Flow automations.flow/definitions.ts150LFlow automations: triggers, condition fields, actions and templates. Client-safe.flow/engine.ts288LRule-based automations: trigger → conditions → actions. No AI; fully deterministic.flow/validate.ts37LShape and rules for a flow, shared by the Flow editor and Sidekick. Client-safe.format.ts13LShared display helpers for dates and ids. Client-safe (no server imports).fx.ts33LDaily market exchange rates for "automatic" presentment currencies. open.er-api.com covers ~160 currencies withgeo.ts88LCountries for international selling (client-safe): ISO 3166-1 alpha-2 code, name, default currency, and howgift-cards.ts84LGift cards: 16-character codes, stored only as a hash plus the last 4 characters.go-live.ts39LWhat payment providers (Razorpay, Stripe, PayPal) review before they activate live payments, and what cardgoogle-auth.ts149L"Continue with Google" for the admin (OpenID Connect, authorization code + PKCE).hosts.ts127LHost-based routing shared by the proxy (rewrites) and app code (absolute links).i18n.ts61LStorefront UI strings. English and Hindi are built in; merchants can override any string perimages.ts35LImage variants: every upload is stored as WebP at a few widths ({base}-400.webp, -1000.webp, -1800.webp) andindia.ts44Linventory.ts80LApplies a delta to on_hand or committed and records it in the adjustment history.jobs.ts65LDurable background jobs in Postgres. Due jobs are claimed with SKIP LOCKED, so several workerslegal.ts12LWho runs Sellify, for the legal pages (/legal/*). Set these in the environment; unset values show asmarket.ts34LPresentment currencies (client-safe). Prices are stored in the store currency; shoppers can browsemoney.ts25LAmounts are integers in minor units (paise). These helpers are safe on client and server.notification-defs.ts213LDefault notification templates (Liquid). Client-safe so the editor can show defaults and sample data.notifications.ts56LMerchant-editable email templates (Settings → Notifications), written in Liquid.order-edit.ts181LEditing an order after it was placed, like Shopify's "Edit order": change quantities, remove items, add products.orders.ts560Lpartner-apps.ts122LCreating and configuring public apps, shared by the Partner dashboard (admin /partners) and `sellify app` in the CLIpassword-reset.ts76L"Forgot password" for merchant/staff accounts (also how Google-only accounts set a password): a one-use link,payments.ts224LOnline payments. Each provider follows the same shape:paypal.ts68LMinimal PayPal REST client (Orders v2, https://developer.paypal.com/docs/api/orders/v2/): create an order, sendpermissions.ts90LStaff permissions, like Shopify: each area of the admin is None, View or Edit for a staff member.platform-admin.ts23LPlatform admins (the people running Sellify, not store owners): review App Store listings, suspend stores and apps,policies.ts73LStore policies (client-safe). Payment providers (Razorpay, Stripe, PayPal) review these before approving apricing.ts158LPure pricing math (no I/O) so it can be unit-tested and reused by checkout, orders and admin.primary-domain.ts25LUsed by the proxy to 308-redirect {subdomain}.ROOT to a store's primary custom domain before any page rendersproduct-save.ts168LSaving a product (create or update), shared by the Products page (saveProduct) and Sidekick's "add a product".public-apps.ts172LPublic apps: built in the Partner dashboard, listed in the App Store, installed with OAuth 2.0 (authorization code),razorpay.ts50LMinimal Razorpay REST client (https://razorpay.com/docs/api/). Amounts are in paise.sanitize.ts14LCleans merchant-authored rich text before it is stored and rendered on the storefront.sidekick/engine.ts423LSidekick: a deterministic, rule-based assistant. Every answer comes from an intent rulesidekick/flow-builder.ts214LTurns a plain-English automation request into a Flow, with rules rather than AI:sidekick/lookup.ts335LQuestions about the store's own records, answered by looking them up (retrieval, no AI):sidekick/smalltalk.ts251LThe human side of Sidekick: greetings, "how are you", thanks, "who are you", jokes, goodbyes and frustration, insidekick/tasks/engine.ts330LSidekick's guided tasks ("create a discount", "add a product", "fulfil order #1042", …), human in the loop:sidekick/tasks/index.ts884LThe tasks Sidekick can do for a merchant, each as a list of fields (see ./engine for how the conversation runs).storage.ts101LFile storage, served at /api/files/* unless S3 is used (older database-stored files are still served after a move):store-defaults.ts67LCreates everything a new store needs to be sellable. Idempotent, so it also backfills older stores.storefront.ts170LTheme previews (from the editor or "Preview" in admin) are authorized by a signed, expiring tokenstores.ts127LLoads the signed-in user and the store they are administering; non-members get the "no access" page.stripe.ts69LMinimal Stripe REST client (https://docs.stripe.com/api): Checkout Sessions for payment, refunds, webhooks.theme-code.ts662LTheme code: Liquid sections (with a {% schema %} block, like Shopify), snippets, and custom CSS/JS.theme-files.ts104LTheme file writes shared by the code editor and the Admin API (and so the CLI): validation, the Liquid-or-JSONthrottle.ts54LBrute-force protection for login, signup and password resets: fixed-window counters in Postgres, so every serverwebhooks.ts109LWebhooks for custom apps. Payloads are JSON, signed with the app's webhook secret:src/themes
13 filesanalytics.tsx263LStorefront event layer. Every event is:client.tsx85L---------- Cart line quantity ----------components.tsx76LShared presentational pieces for storefront sections (server components).events.ts19LBuilders for GA4-format ecommerce events (values in major currency units, as GA4 expects).install.ts18LEvery theme a store gets — the first one and the free themes in the library — goes through here, so they allliquid-page.ts202LFull-page rendering for Liquid themes (themes.engine = "liquid"): templates/<type>.liquid renders theliquid-starter.ts1084LEvery Sellify theme (Aurora, Noir, Bloom, Dawnlight…) is a Liquid theme laid out like a Shopify Online Store 2.0liquid-sync.ts110LFor Liquid themes the files are the source of truth, exactly like Shopify:page-data.ts43LWhat a storefront page hands its theme (product, collection, URL params), and the collection listing querypresets.ts134LTheme presets share one codebase and differ in colors, typography and starting layout.render.tsx195Lschema.ts378LClient-safe theme schema: used by the editor and to fill defaults at render time.types.ts60LTheme documents follow the "JSON templates" model: each template is an ordered list ofsrc/db
3 filesindex.ts51LReuse the connection across hot reloads in dev.schema.ts1402LMoney is stored as integers in minor units (paise for INR).sql.ts11LA fully qualified column ("table"."column") for correlated subqueries in sql`` templates.src/components
8 filesadmin.tsx249LUploads images to /api/admin/uploads, one request per file (hosts cap request bodies), and returns their URLs.charts.tsx144LLightweight SVG charts for analytics. Conventions (see dataviz guidance):dialogs.tsx181LIn-app replacements for window.confirm / prompt / alert, plus toasts. Promise-based so call sites stay one line:logo.tsx39LSellify logo: a green tile with a geometric "S" and a vermilion tag-hole dot; the wordmark repeats the dot over theoverview-charts.tsx328LCharts for the Analytics overview. They render at the card's real pixel width (no SVG scaling, so textsidekick.tsx168Lstyles.ts12LPlain (non-client) helpers so server components can use them too.ui.tsx143LSubmit button that disables itself while its form's server action is pending.packages/cli/bin
1 filessellify.mjs843LSellify CLI: develop themes locally (pull, push, live-sync with `theme dev`), build apps (`app init`, `app dev`,Security
Hashed (one-way)
- ●passwords · bcrypt
- ●session tokens · sha256
- ●Admin API tokens · sha256
- ●OAuth codes · sha256
Encrypted (AES-256-GCM)
- ●Razorpay / Stripe / PayPal secrets
- ●SMTP & app passwords
- ●webhook secrets
- ●app client secrets
OAuth (PKCE S256)
- ●Continue with Google · state + bind cookie + one-time ticket
- ●sellify login · loopback + one-time code
- ●public apps · signed query + code exchange
Signed (HMAC)
- ●webhooks · X-Sellify-Hmac-Sha256
- ●OAuth redirects & app URLs
- ●unsubscribe links
- ●campaign click links
- ●theme preview tokens
Trust & safety
- ●login limits · 5/email, 30/IP per 15 min
- ●email verified before a store goes live
- ●Report this store → platform admins
- ●store suspension · storefront, cart, API, jobs stop
- ●App Store review · PII scope · app kill switch
- ●error log · email per new error · optional Sentry
Isolation
- ●every query scoped by store_id
- ●permission per area (none/view/edit)
- ●SellifyQL → parameterized read-only SQL
- ●rich text sanitized · email output escaped
- ●webhook URLs: no private IPs in prod
Deployment
| Local | Production | |
|---|---|---|
| App | next start (local) | Vercel |
| Database | Postgres 14 (Homebrew) | Layerbase Postgres |
| Jobs | instrumentation.ts poller (20s) | Vercel Cron → /api/cron/jobs |
| Uploads | .uploads/ on disk | S3 / Cloudflare R2 |
| store mailbox · console | store mailbox · SMTP_URL | |
| Hosts | *.localhost:3000 | *.root domain + custom domains |
| API throttle | in-memory buckets | Upstash Redis (shared) · in-memory fallback |