sellify · system design

Architecture

66

Tables

143

Routes

113

Modules

9

Event topics

4

Job types

12

API scopes

12

Permissions

16

Email templates

8

Flow triggers

9

First-party apps

next 16.4.3react 19.3.0drizzle-orm 0.45.4postgres 3.4.9liquidjs 10.30.0graphql 16.14.2nodemailer 10.2.1zod 4.6.7sanitize-html 2.18.1@aws-sdk/client-s3 3.1158.0bcryptjs 3.0.3
PersonBrowser / clientEdge / proxyNext.js serverPostgresExternal serviceBackground job

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 + architecture
admin.root→/admin/*+ x-sellify-path
dev.root→/dev/*docs · explorer
{store}.root→/s/{store}/*+ x-sellify-path
custom 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 API
any host /api/*→app/api/* (no rewrite)GraphQL · OAuth · webhooks · cron

Flow

Request lifecycle

Every page request

Browserproxy.tsNext.js serversrc/libPostgres1GET mystore.root/products/tee2resolveHost(x-forwarded-host?? host)3primaryDomainFor (globalThiscache)4rewrite /s/mystore/products/tee5static shell (PPR) streamed6getStorefrontContext /getAdminContext7Drizzle, always WHERE store_id= …8Suspense boundaries stream in

Admin server action (mutation)

MerchantBrowserNext.js serversrc/libPostgres1click Save2POST + Next-Action header3getAdminContext(handle, perm)4session → member → access mapNext-Action present ⇒ needs Edit; pages need View5zod-validated write (tx)6refresh() / redirect()7result + fresh RSC payload

Flow

Auth, staff & permissions

Login → permission check

MerchantBrowserNext.js serverPostgres1email + password2login action3bcrypt verify (users)4sessions: sha256(token),expiry5sellify_session (host-only)6/store/{handle}/orders7store_members + staff_rolesowner = full · staff = preset / custom role → none · view · edit per area8page · or /no-access · or?denied=

Continue with Google (OIDC + PKCE)

Browseradmin hostGoogleroot hostPostgres1GET /auth/google?next2google_logins: sha256(state),verifier, bind3bind cookie + 302 to Google4consent (S256 challenge)5/auth/google/callback?code&stateGoogle rejects *.localhost redirect URIs ⇒ root host6code + secret + verifier →id_token7by sub · link verified email ·or create user8302 /auth/google/finish?ticket(one-time)9ticket + bind cookie match ⇒session10sellify_session → next /onboarding

Flow

Cart, checkout & payments

Cart → order

ShopperBrowserNext.js serverPostgresPayment provider1Add to cart2POST /cart/add (or/cart/add.js)3carts + cart_lines (cookietoken)4quoteCheckout (address, code)5discounts · gift cards · zones· GST · FX6recordCheckout →abandoned_checkouts7placeOrderCOD / gift card → createOrder now8checkouts snapshot (amount,currency)9create order / session10pay (Razorpay modal ·Stripe/PayPal page)11return URL · or webhookbackup12verify signature + amount vssnapshot13createOrder (one transaction)14full page load →/orders/{token}

createOrder transaction

Lock & price

lines · totals · tax lines

→

Upsert customer

orders_count · total_spent

→

Reserve inventory

committed +qty per location

↳oversold → event note
→

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

↳HMAC-SHA256 · 10s timeout · webhook_deliveries log
→

App endpoint

Flow automation run

runFlows(trigger)

after() the response

→

Conditions

all / any · typed operators

↳false → Otherwise branch
→

Actions

tag · note · email · webhook …

→

Wait

flow.resume job

↳resumes at step N
→

flow_runs

status + log

Flow

Order operations

Fulfil → deliver → thank-you

MerchantNext.js serverPostgresJob runnerCustomer inbox1Fulfill items (+ tracking)2fulfillments · committed −,on_hand −3shipping_confirmation (ifenabled)4fully fulfilled →post_purchase job5Mark out for delivery /delivered6shipment_status · delivered_at· event7out_for_delivery / delivered8Send update (custom message)9email_messages log on theorder

Order editing

MerchantNext.js serverPostgresThird-party app1previewOrderEdit (dry run)2changes · new totals · balance3commitOrderEdit4lines · inventory · totals(tx)balance > 0 ⇒ partially_paid · < 0 ⇒ refund owed5orders/edited webhook

Flow

Email delivery

sendNotification → sendEmail

Template

store override ?? default

→

Liquid render

body escaped · subject plain

↳template error → default template
→

Marketing?

+ unsubscribe_url

↳no → transactional
→

Store mailbox

Gmail · Workspace · SMTP

↳not verified → platform
→

Reserve quota

sent_today < daily_limit

↳full → platform (campaigns: pause)
→

Platform sender

SMTP_URL, as the store

↳no SMTP_URL → console log
→

email_messages

via · status · order_id

Flow

Email automations

Order updates (transactional)

Order event

placed · shipped · delivered · edited · cancelled · refunded

→

automationEnabled?

email_automations

↳off → skip
→

sendNotification

{ orderId }

→

Order page log

Marketing automations

Trigger

subscribe · fulfilled/delivered · order

→

queue*

welcome · post_purchase · win_back

↳win-back: one job per customer, pushed back each order
→

email.automation job

after delay

→

Re-check

enabled · subscribed · not sent · no newer order

↳any fails → skip
→

Send + unsubscribe

Abandoned checkout recovery

Checkout with email

recordCheckout

→

checkout.recovery job

delay 1–24h

→

Still open?

↳order placed → markRecovered cancels job
→

Email

/cart/recover/{token}

→

Cart restored

→ order → recovered_order_id

Flow

Email campaigns

Compose → send → track

MerchantNext.js serverPostgresJob runnerSMTPCustomer inbox1edit blocks (autosave 700ms)2server-rendered preview +audience3Send now / Schedule4snapshot recipients (+ tokeneach)5campaign.send6next 40 pending7sendEmail(fallback: false)mailbox at daily limit ⇒ pause, re-queue next UTC day8more pending → next batch(+1s)9GET /email/o/{token} (pixel)10GET /email/c/{token}?u&s(signed)11opened_at · clicked_at ·clicks

Flow

Apps platform

Public app install (OAuth 2.0)

MerchantNext.js serverPostgresThird-party app1App Store → Install2App URL ?store&timestamp&hmac3/oauth/authorize?client_id&scope&redirect_uri&state4consent: scopes on this store5Install app6oauth_codes (sha256, 10 min)7redirect_uri ?code&state&hmac8POST /api/oauth/access_token9custom_apps row (token hash,scopes)10{ access_token: sfat_…, scope}11Uninstall12app/uninstalled (signed)

Admin GraphQL API call

Third-party appNext.js serversrc/libUpstash RedisPostgres1POST /api/admin/{version}/graphql2sha256(token) → custom_apps +store3parse · depth ≤ 8 · cost4leaky bucket per app (atomicscript)Redis unset or down ⇒ per-instance in-memory bucketover budget ⇒ THROTTLED with retry info5resolvers (scope-checked,store-scoped)6data + extensions.cost

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

↳GA4 · Pixel · WhatsApp …
→

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)

sellify CLINext.js serversrc/libPostgres1themes · themeCreate(Development theme)2theme(id).files { pathchecksum }3diff local MD5s4themeFilesUpsert (≤ 50 files ·5 MB)5theme-files: validate Liquid /schema / JSON6theme_files +theme_file_versions7upsertedPaths · userErrors ·previewUrlfs.watch ⇒ upload each save · live theme needs --allow-live

App development (sellify app init · app dev)

sellify CLINext.js serverPostgresThird-party appBrowser1app init → starter (server.mjs· sellify.app.json)2POST /api/cli/apps (first run)→ client id + secret → .env3PATCH app URL · redirect ·scopes · uninstall webhook4public_apps (partner-apps:validate · review rules)5spawn dev_command (PORT · URLs· credentials)6/install?store →/oauth/authorize → Install app7POST /api/oauth/access_token →token8webhookSubscriptionCreate(webhook_topics)9signed webhooks ·app/uninstalledcloudflared quick tunnel (or --tunnel-url) when Sellify isn't local · listed apps refused

sellify login (browser, loopback + PKCE)

sellify CLI127.0.0.1:portBrowseradmin hostPostgres1listen on a random port2open /cli/authorize?port&state&challenge&device3signed in · Online store edit· Allow4cli_auth_codes (5 min,one-time)5302 127.0.0.1:port/callback?code&state6code (state checked)7POST /api/cli/token { code,verifier }8custom_apps (cli_user_id,theme scopes)9token → ~/.config/sellify(0600)each API call re-checks the member still has Online store edit

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-bridge.js ⇄ postMessage: toast · navigate · title · fresh token
→

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

↳test install = signed launch link → app → consent
→

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

→

Email

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

↳merchants never send raw SQL
→

Live view

poll 5s

Flow

Sidekick (admin assistant)

A guided task (e.g. fulfil an order)

MerchantBrowserNext.js serversrc/libPostgres1“fulfil #1042 via Delhivery”2sidekickAsk(text)3trigger → task · extractfields from the sentence4next question + buttons +draft5answer + draft (untrusted:re-validated)6summary + Confirm card7Confirm8sidekickRun(run_task) → checkedit access + every value again9the admin page's own function(fulfillOrder …)refunds, cancellations and deletes are not tasks — they stay in the admin

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

↳draft round-trips, re-validated every turn
→

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

↳several equal matches → “Which one?”
→

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

↳key unique while pending → dedupe
→

jobs

→

Trigger

local poller 20s · Vercel Cron /api/cron/jobs

→

Claim

UPDATE … FOR UPDATE SKIP LOCKED

→

Handler by type

→

done (key → null)

↳error → backoff retry ×5 → failed

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

pendingpaidpartially_refundedrefundedvoidedpartially_paid
  • 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

unfulfilledpartially_fulfilledfulfilled
  • unfulfilled—fulfil some→partially_fulfilled
  • partially_fulfilled—fulfil rest→fulfilled
  • unfulfilled—fulfil all→fulfilled

Order · status

openclosedcancelled
  • open—archive→closed
  • closed—unarchive→open
  • open—cancel (nothing fulfilled)→cancelled

Fulfillment · shipment_status

in_transitout_for_deliverydeliveredfailed_delivery
  • 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

draftscheduledsendingsent
  • 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

pendingrunningdonefailedcancelled
  • 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

stores
53
users
8
products
4
product_variants
4
orders
4
locations
3
customers
2
themes
2

Accounts & stores

auth_throttle3 cols · 0 idx
  • 🔑 keytext
  • countinteger
  • reset_attimestamp with time zone
domains9 cols · 1 idx

→ stores

  • 🔑 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

→ users

  • 🔑 state_hashtext
  • bind_hashtext
  • code_verifiertext
  • nexttext?
  • user_iduuid?
  • new_userboolean
  • ticket_hashtext?
  • expires_attimestamp with time zone
password_resets4 cols · 0 idx

→ users

  • 🔑 token_hashtext
  • user_iduuid
  • expires_attimestamp with time zone
  • created_attimestamp with time zone
sessions4 cols · 1 idx

→ users

  • 🔑 idtext
  • user_iduuid
  • expires_attimestamp with time zone
  • created_attimestamp with time zone
staff_roles7 cols · 1 idx

→ stores

  • 🔑 iduuid
  • store_iduuid
  • nametext
  • descriptiontext
  • permissionstext[]
  • created_attimestamp with time zone
  • updated_attimestamp with time zone
store_members6 cols · 1 idx

→ stores→ users

  • 🔑 store_iduuid
  • 🔑 user_iduuid
  • rolemember_role
  • role_keytext?
  • permissionstext[]
  • created_attimestamp with time zone
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
  • 🔑 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

→ collections→ products

  • 🔑 collection_iduuid
  • 🔑 product_iduuid
  • positioninteger
collections14 cols · 1 idx

→ stores

  • 🔑 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

→ stores→ products

  • 🔑 iduuid
  • store_iduuid
  • product_iduuid
  • urltext
  • alttext
  • positioninteger
product_variants21 cols · 2 idx

→ stores→ products

  • 🔑 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

→ stores

  • 🔑 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

→ stores→ product_variants→ locations

  • 🔑 iduuid
  • store_iduuid
  • variant_iduuid
  • location_iduuid
  • fieldtext
  • deltainteger
  • reasontext
  • referencetext?
  • actortext
  • created_attimestamp with time zone
inventory_levels7 cols · 1 idx

→ product_variants→ locations→ stores

  • 🔑 variant_iduuid
  • 🔑 location_iduuid
  • store_iduuid
  • on_handinteger
  • committedinteger
  • incominginteger
  • updated_attimestamp with time zone
locations12 cols · 0 idx

→ stores

  • 🔑 iduuid
  • store_iduuid
  • nametext
  • address1text
  • citytext
  • statetext
  • pincodetext
  • phonetext
  • is_defaultboolean
  • activeboolean
  • created_attimestamp with time zone
  • updated_attimestamp with time zone

Online store

menus7 cols · 1 idx

→ stores

  • 🔑 iduuid
  • store_iduuid
  • handletext
  • titletext
  • itemsjsonb
  • created_attimestamp with time zone
  • updated_attimestamp with time zone
pages10 cols · 1 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 iduuid
  • store_iduuid
  • nametext
  • presettext
  • roletheme_role
  • enginetext
  • datajsonb
  • created_attimestamp with time zone
  • updated_attimestamp with time zone

Customers

customers15 cols · 1 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 store_iduuid
  • 🔑 keytext
  • enabledboolean
  • delay_hoursinteger
  • updated_attimestamp with time zone
email_campaign_recipients13 cols · 2 idx

→ email_campaigns→ stores

  • 🔑 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

→ stores

  • 🔑 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

→ stores

  • 🔑 iduuid
  • store_iduuid
  • order_iduuid?
  • totext
  • template_keytext
  • subjecttext
  • viatext
  • statustext
  • errortext?
  • created_attimestamp with time zone
email_senders15 cols · 0 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 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

→ stores→ shipping_zones

  • 🔑 iduuid
  • store_iduuid
  • zone_iduuid
  • nametext
  • priceinteger
  • conditiontext
  • mininteger?
  • maxinteger?
shipping_zones7 cols · 0 idx

→ stores

  • 🔑 iduuid
  • store_iduuid
  • nametext
  • countriestext[]
  • statestext[]
  • created_attimestamp with time zone
  • updated_attimestamp with time zone

Discounts

discounts20 cols · 1 idx

→ stores

  • 🔑 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

→ carts→ product_variants

  • 🔑 cart_iduuid
  • 🔑 variant_iduuid
  • quantityinteger
  • created_attimestamp with time zone
carts5 cols · 0 idx

→ stores

  • 🔑 iduuid
  • store_iduuid
  • tokentext
  • created_attimestamp with time zone
  • updated_attimestamp with time zone
checkouts15 cols · 0 idx

→ stores

  • 🔑 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

→ orders→ stores

  • 🔑 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

→ orders→ stores

  • 🔑 iduuid
  • order_iduuid
  • store_iduuid
  • kindtext
  • messagetext
  • authortext
  • created_attimestamp with time zone
order_lines18 cols · 2 idx

→ orders→ stores→ products→ product_variants→ locations

  • 🔑 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

→ stores→ customers

  • 🔑 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
transactions9 cols · 2 idx

→ orders→ stores

  • 🔑 iduuid
  • order_iduuid
  • store_iduuid
  • kindtext
  • gatewaytext
  • statustext
  • amountinteger
  • referencetext?
  • created_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
  • 🔑 keytext
  • content_typetext
  • databytea
  • sizeinteger
  • created_attimestamp with time zone

Theme code (Liquid sections/snippets, custom assets)

theme_file_versions6 cols · 1 idx

→ themes

  • 🔑 iduuid
  • theme_iduuid
  • pathtext
  • contenttext
  • authortext
  • created_attimestamp with time zone
theme_files5 cols · 0 idx

→ themes→ stores

  • 🔑 theme_iduuid
  • store_iduuid
  • 🔑 pathtext
  • contenttext
  • updated_attimestamp with time zone

Apps

app_installations6 cols · 0 idx

→ stores

  • 🔑 store_iduuid
  • 🔑 app_keytext
  • enabledboolean
  • configjsonb
  • created_attimestamp with time zone
  • updated_attimestamp with time zone
cli_auth_codes6 cols · 0 idx

→ stores→ users

  • 🔑 code_hashtext
  • store_iduuid
  • user_iduuid
  • code_challengetext
  • devicetext
  • expires_attimestamp with time zone
custom_apps13 cols · 2 idx

→ stores→ public_apps→ users

  • 🔑 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

→ public_apps→ stores

  • 🔑 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

→ users

  • 🔑 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

→ stores→ webhook_subscriptions

  • 🔑 iduuid
  • store_iduuid
  • subscription_iduuid
  • topictext
  • response_statusinteger?
  • errortext?
  • duration_msinteger
  • created_attimestamp with time zone
webhook_subscriptions6 cols · 1 idx

→ stores→ custom_apps

  • 🔑 iduuid
  • store_iduuid
  • app_iduuid
  • topictext
  • addresstext
  • created_attimestamp with time zone

Analytics, reports, Flow

flow_runs7 cols · 1 idx

→ flows→ stores

  • 🔑 iduuid
  • flow_iduuid
  • store_iduuid
  • statustext
  • subjecttext
  • stepsjsonb
  • created_attimestamp with time zone
flows13 cols · 1 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 iduuid
  • store_iduuid
  • nametext
  • querytext
  • created_bytext
  • created_attimestamp with time zone
  • updated_attimestamp with time zone
storefront_events12 cols · 2 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 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

→ stores→ blogs

  • 🔑 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

→ stores

  • 🔑 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

→ stores

  • 🔑 iduuid
  • store_iduuid
  • emailtext
  • code_hashtext
  • attemptsinteger
  • expires_attimestamp with time zone
  • created_attimestamp with time zone
customer_sessions5 cols · 0 idx

→ stores→ customers

  • 🔑 idtext
  • store_iduuid
  • customer_iduuid
  • expires_attimestamp with time zone
  • created_attimestamp with time zone

Staff

staff_invites10 cols · 0 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 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_cards→ stores

  • 🔑 iduuid
  • gift_card_iduuid
  • store_iduuid
  • order_iduuid?
  • amountinteger
  • notetext
  • created_attimestamp with time zone
gift_cards14 cols · 1 idx

→ stores

  • 🔑 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

→ stores

  • 🔑 store_iduuid
  • 🔑 keytext
  • subjecttext
  • bodytext
  • updated_attimestamp with time zone

Translations (languages)

translations7 cols · 0 idx

→ stores

  • 🔑 store_iduuid
  • 🔑 resource_typetext
  • 🔑 resource_idtext
  • 🔑 localetext
  • 🔑 fieldtext
  • valuetext
  • updated_attimestamp with time zone

Platform trust & safety

abuse_reports8 cols · 1 idx

→ stores

  • 🔑 iduuid
  • store_iduuid?
  • subdomaintext
  • reasontext
  • detailstext
  • reporter_emailtext?
  • statustext
  • created_attimestamp with time zone
email_verifications3 cols · 0 idx

→ users

  • 🔑 token_hashtext
  • user_iduuid
  • expires_attimestamp with time zone
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_orders
  • orders/paidread_orders
  • orders/editedread_orders
  • orders/fulfilledread_orders
  • orders/cancelledread_orders
  • refunds/createread_orders
  • products/createread_products
  • products/updateread_products
  • products/deleteread_products

Background job types

  • flow.resumeflow/engine.ts → resumeFlowJob
  • checkout.recoveryabandoned.ts → sendRecoveryEmail
  • email.automationemail-automations.ts → runAutomationJob
  • campaign.sendcampaigns.ts → runCampaignJob

Admin API scopes

versions 2026-10
  • read_productsRead products, variants, images and collections
  • write_productsCreate, update and delete products and variants
  • read_inventoryRead inventory levels and locations
  • write_inventorySet inventory quantities
  • read_ordersRead orders, line items and fulfillments
  • write_ordersMark orders as paid and create fulfillments
  • read_customersRead customers
  • read_customer_piiSee customers' names, emails, phone numbers and street addresses
  • read_themesRead themes and their files
  • write_themesCreate, edit, publish and delete themes and their files
  • read_discountsRead discount codes
  • write_discountsCreate and delete discount codes

bucket 1000restore/s 50max cost 1000max depth 8max page 100

Email templates

  • order_confirmationCustomer
  • shipping_confirmationCustomer
  • order_cancelledCustomer
  • order_editedCustomer
  • out_for_deliveryCustomer
  • deliveredCustomer
  • refund_issuedCustomer
  • order_messageCustomer
  • welcome_subscriberCustomer (marketing)
  • post_purchaseCustomer (marketing)
  • win_backCustomer (marketing)
  • abandoned_checkoutCustomer
  • customer_login_codeCustomer
  • gift_card_issuedCustomer
  • staff_inviteStaff
  • new_order_staffStaff

Email automations

  • order_confirmationOrder updates · on
  • shipping_confirmationOrder updates · on
  • out_for_deliveryOrder updates · on
  • deliveredOrder updates · on
  • order_editedOrder updates · on
  • order_cancelledOrder updates · on
  • refund_issuedOrder updates · on
  • abandoned_checkoutRecover sales · delay 1/6/10/24h · on
  • welcomeMarketing · delay 0/1/24h · off
  • post_purchaseMarketing · delay 24/72/168/336h · off
  • win_backMarketing · delay 720/1440/2160/4320h · off

Flow

Triggers

  • orders/createorder
  • orders/paidorder
  • orders/fulfilledorder
  • orders/cancelledorder
  • refunds/createorder
  • products/createproduct
  • products/updateproduct
  • inventory/changedinventory

Actions

  • add_order_tagsorder
  • remove_order_tagsorder
  • add_order_noteorder
  • add_customer_tagsorder
  • add_product_tagsproduct · inventory
  • remove_product_tagsproduct · inventory
  • set_product_statusproduct · inventory
  • waitorder · product · inventory
  • send_internal_emailorder · product · inventory
  • send_http_requestorder · product · inventory

First-party apps

  • razorpayPayments
  • gmailEmail
  • google-workspaceEmail
  • google-analyticsAnalytics
  • google-tag-managerAnalytics
  • google-adsMarketing
  • meta-pixelMarketing
  • whatsapp-chatMarketing
  • free-shipping-barStore design

Environment

  • DATABASE_URLrequired
  • DB_POOL_MAXoptional
  • NEXT_PUBLIC_ROOT_DOMAINrequired
  • SESSION_SECRETrequired
  • ENCRYPTION_KEYrequired
  • FILE_STORAGErequired
  • S3_ENDPOINTrequired
  • S3_BUCKETrequired
  • S3_ACCESS_KEY_IDrequired
  • S3_SECRET_ACCESS_KEYrequired
  • S3_PUBLIC_URLrequired
  • SMTP_URLoptional
  • EMAIL_FROMoptional
  • CRON_SECREToptional
  • VERCEL_TOKENoptional
  • VERCEL_PROJECT_IDoptional
  • VERCEL_TEAM_IDoptional
  • CUSTOM_DOMAIN_CNAMEoptional
  • CUSTOM_DOMAIN_Aoptional
  • GOOGLE_CLIENT_IDrequired
  • GOOGLE_CLIENT_SECRETrequired
  • UPSTASH_REDIS_REST_URLoptional
  • UPSTASH_REDIS_REST_TOKENoptional
  • DISABLE_JOB_POLLERoptional
  • STRIPE_API_BASEoptional
  • PAYPAL_API_BASEoptional
  • FX_API_BASEoptional
  • GMAIL_SMTP_HOSToptional
  • GMAIL_SMTP_PORToptional
  • GOOGLE_OAUTH_BASEoptional
  • PLATFORM_ADMIN_EMAILSrequired
  • PLATFORM_CONTACT_EMAILrequired
  • LEGAL_ENTITY_NAMErequired
  • LEGAL_ADDRESSrequired
  • GRIEVANCE_OFFICER_NAMErequired
  • SENTRY_DSNoptional

Scripts

  • pnpm dev
  • pnpm build
  • pnpm start
  • pnpm lint
  • pnpm typecheck
  • pnpm db:generate
  • pnpm db:migrate
  • pnpm db:studio
  • pnpm db:seed
  • pnpm seed:store
  • pnpm simulate
  • pnpm sellify
  • pnpm deploy:vercel
  • pnpm db:migrate:prod

Roles & access

RoleOrdersProductsCustomersContentOnline StoreAnalyticsDiscountsMarketingAppsFlowSettingsStaff
Ownereditediteditediteditediteditediteditediteditedit
Admineditediteditediteditediteditediteditediteditedit
Store managerediteditediteditviewvieweditedit—editview—
Fulfillmenteditviewview—————————
Customer supportviewviewedit—————————
Marketing—viewviewedit—vieweditedit—edit——
Developerviewview——editview——editeditview—

Module map · 113 files

src/lib

88 files
abandoned.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.ts5L
admin-api/limits.ts117LCost-based throttling (leaky bucket per app), modelled on Shopify's GraphQL Admin API:
admin-api/resolvers.ts550L
admin-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's
analytics/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 it
apps/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 for
cart.ts130L
catalog.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 payment
customer-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 guided
domains.ts144LCustom domains (Settings → Domains). A merchant adds "shop.mybrand.com", points it at Sellify with DNS, and proves
email-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 check
email-verification.ts51LEmail verification for merchant accounts: a store can only go live once its owner confirmed their email, so
email.ts152LEmail delivery.
error-log.ts88LServer error monitoring without a third party: Next.js reports every server error (instrumentation.ts
events.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 with
geo.ts88LCountries for international selling (client-safe): ISO 3166-1 alpha-2 code, name, default currency, and how
gift-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 card
google-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 per
images.ts35LImage variants: every upload is stored as WebP at a few widths ({base}-400.webp, -1000.webp, -1800.webp) and
india.ts44L
inventory.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 workers
legal.ts12LWho runs Sellify, for the legal pages (/legal/*). Set these in the environment; unset values show as
market.ts34LPresentment currencies (client-safe). Prices are stored in the store currency; shoppers can browse
money.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.ts560L
partner-apps.ts122LCreating and configuring public apps, shared by the Partner dashboard (admin /partners) and `sellify app` in the CLI
password-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, send
permissions.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 a
pricing.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 renders
product-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 rule
sidekick/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, in
sidekick/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 token
stores.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-JSON
throttle.ts54LBrute-force protection for login, signup and password resets: fixed-window counters in Postgres, so every server
webhooks.ts109LWebhooks for custom apps. Payloads are JSON, signed with the app's webhook secret:

src/themes

13 files
analytics.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 all
liquid-page.ts202LFull-page rendering for Liquid themes (themes.engine = "liquid"): templates/<type>.liquid renders the
liquid-starter.ts1084LEvery Sellify theme (Aurora, Noir, Bloom, Dawnlight…) is a Liquid theme laid out like a Shopify Online Store 2.0
liquid-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 query
presets.ts134LTheme presets share one codebase and differ in colors, typography and starting layout.
render.tsx195L
schema.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 of

src/db

3 files
index.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 files
admin.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 the
overview-charts.tsx328LCharts for the Analytics overview. They render at the card's real pixel width (no SVG scaling, so text
sidekick.tsx168L
styles.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 files
sellify.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

LocalProduction
Appnext start (local)Vercel
DatabasePostgres 14 (Homebrew)Layerbase Postgres
Jobsinstrumentation.ts poller (20s)Vercel Cron → /api/cron/jobs
Uploads.uploads/ on diskS3 / Cloudflare R2
Emailstore mailbox · consolestore mailbox · SMTP_URL
Hosts*.localhost:3000*.root domain + custom domains
API throttlein-memory bucketsUpstash Redis (shared) · in-memory fallback