Welcome — what MerchOS is
The business
YourCustomMerch.ca is a Canadian custom-merchandise business serving companies across nine active verticals. Founded and run by two sisters — Jess and Joy. The business has two products:
| Product | What it is | Status |
|---|---|---|
| Marketing site | Public-facing site at yourcustommerch.ca (Astro, separate repo) | Live on Vercel |
| MerchOS Platform | Private operational platform for running the business | Live, gated behind pre-launch password until launch day |
MerchOS is YCM’s internal operating system. YCM’s client companies pay YCM per a pricing model (see below) for a branded merch storefront and get charged margin on every order. MerchOS powers it all internally — it is not a multi-tenant SaaS product YCM sells to other merch businesses.
If you internalize one sentence about the project: MerchOS is the operating system for one specific custom-merch business, not a SaaS platform for many. That single fact rules out a lot of architectural temptations (per-tenant white-labeling, dynamic pricing-tier configuration, generic supplier abstraction) that would otherwise be obvious moves.
The five user roles
| Role | Interface | Description |
|---|---|---|
| Super Admin | Internal Admin (internal.yourcustommerch.ca) | YCM team — full platform access |
| Internal Designer | Internal Admin → Design Queue | Reviews print-file revisions |
| Company Admin | Company Admin Portal (admin.yourcustommerch.ca) | HR/ops manager at a client company |
| Company Manager | Company Admin Portal (limited) | Team lead with partial admin access |
| Employee | Storefront (store.yourcustommerch.ca/[slug]) | Orders merch (gated companies only) |
For ungated stores (Travel & Tourism, Events, etc.), shoppers are anonymous — no employee account, no login, pay via Stripe Checkout. That mode is not a fifth role; it’s the absence of one, and it changes a lot about how those storefronts are built.
The nine active verticals
Canonical source: prisma/migrations/20260425_seed_canonical_verticals_and_suppliers/migration.sql (the original eight) plus prisma/migrations/20260511_phase7a_departments_and_member_pays/migration.sql (Departments, the ninth). When you need an authoritative list, query the Vertical table or read those migrations — never trust prose elsewhere in the codebase.
The nine fall into three pricing-model families:
MONTHLY_SUBSCRIPTION (gated B2B)
Employees log into a private storefront with invites, spend limits, and company-funded orders. Three verticals:
businesses-corporatetrades-contractorscreators-collectives
This is the original YCM product. Tier ladder (STARTER / GROWTH / PRO / ENTERPRISE) charges monthly; orders are invoiced to the company at end of month.
Per-company split-tender (ADR-0104, Phase 0141 — shipped 2026-05-19). When an employee’s cart exceeds their remaining allowance, they can opt to pay the overage themselves with their own card via Stripe Elements at checkout. Company credit covers up to the limit, employee tops up the rest — one cart, one order, one fulfillment, split payment. Per-Company toggle on Company.allowEmployeeTopUp; vertical defaults are default-on for trades-contractors, default-off for businesses-corporate + creators-collectives. The spend-limit invariant (CLAUDE.md §6) is preserved — split-tender adds a parallel employee-paid rail, it doesn’t soften the company’s hard cap. Refunds apply LIFO (employee portion first, then company); the employee’s personal payment is private from the company per CLAUDE.md §6.
ONE_TIME_SETUP (ungated, GivesBack-eligible)
Public storefronts. Anonymous shoppers self-pay at Stripe Checkout. allowsGivesBack: true so the company can layer a fundraising markup that YCM holds as a custodial liability and remits monthly. Five verticals:
churchesteamscommunity-causeeventstravel-tourism
There is no monthly subscription for these. A one-time $49 setup fee at intake; after that, YCM’s revenue is per-order margin only.
MEMBER_PAYS (gated, self-pay)
Gated storefronts (invite-only employees) but employees self-pay at Stripe Checkout. No company-side billing post-setup. One vertical:
departments
This family was added in Phase 7a / ADR-0094 to serve municipal and educational departments whose budgets can’t fund subscriptions but whose employees can still buy.
Plus three post-launch stubs (active=false)
celebrations-occasions, restaurants-hospitality, sports-teams-uniforms. Schema entries exist; no go-to-market.
The five non-negotiables
Read these as if they were physical laws. They’re enforced at the service layer and the database layer — not just in the UI:
- Print files gate orders. No order can ship until every print file for every enabled SKU is APPROVED. Enforced as a hard block in
OrdersService, not a soft warning. - Spend limits are hard limits. Order submission acquires a row-level lock on
SpendRecord. An employee never overshoots their cap, even under concurrent submissions. - Currency never changes mid-contract.
Company.billingCurrencyis set at intake from the country field and is immutable through normal flows. - Cross-tenant isolation is absolute. Every query on tenant data includes a
companyIdfilter. PostgreSQL row-level security is a backstop — application code must independently be correct. - Employees never see payment information. No credit card fields, no invoice totals, no billing details of any kind in the storefront. Commerce is between YCM and the company only — except in MEMBER_PAYS, where it’s between YCM and the individual employee directly (still no cross-employee visibility).
A sixth, learned the expensive way after Phase 7a: fundraising markup is custodial, not revenue. YCM holds it as a balance-sheet liability until monthly remittance to the host org. It does not appear in MRR. It does not generate T4A from YCM.
What’s next
- Architecture at a glance — the monorepo, frontends, API, DB, and clouds.
- Domain model — the data that underpins everything else.
Canonical sources
CLAUDE.md§2-§6 — the business, the verticals, the five non-negotiables.prisma/migrations/20260425_seed_canonical_verticals_and_suppliers/migration.sql— the original eight verticals.prisma/migrations/20260511_phase7a_departments_and_member_pays/migration.sql— Departments + MEMBER_PAYS.- ADR-0094 — the MEMBER_PAYS family.
- ADR-0104 — split-tender employee top-up (Accepted, not yet shipped).
Triggers for update
Update this chapter if you:
- Add or retire a vertical (active or stub).
- Add or change a pricing-model family.
- Ship a “build not yet started” or “build queued” feature mentioned in this chapter (currently: split-tender employee top-up). When it ships, drop the “build not yet started” parenthetical.
- Add a sixth user role.
- Change a non-negotiable in CLAUDE.md §6.