P-02 · Multi-tenant SaaS · Web + iOS/Android
Velvet Studio
Salon CRM and booking platform with SMS marketing, loyalty tiers, in-person payments and AI assistants.
- Role
- Solo — product design, development, deployment
- Timeline
- Feb 2026 — May 2026
- Platforms
- Web app · iOS & Android (Capacitor + Expo) · Embeddable booking widget
- Links
- velvetstudio.ca ↗
TL;DR
Velvet Studio is a CRM for salons, studios and spas: online booking, a day/week calendar, checkout, SMS campaigns, loyalty tiers and AI assistants, on web and native mobile. Jin Oh designed, built and deployed it alone — 32 Supabase Edge Functions, 129 migrations and nine third-party integrations including Stripe, Clover, Twilio, Plaid and Gemini.
Key facts
- 01One sign-in serves three kinds of user — owner, staff and customer — resolved to the right tenant by a single database function.
- 02Public booking works without an account, yet anonymous users can never read the salon’s tables: all public writes go through SECURITY DEFINER RPCs.
- 03Automated SMS reminders run from a job queue processed by a scheduled Edge Function, cutting no-shows.
- 04Loyalty tiers are earned by spend or visit count and applied automatically at booking and checkout.
- 05Web subscriptions (Stripe) and in-app subscriptions (RevenueCat) are reconciled into one plan state: trial, active, past-due grace, comped or suspended.
- 06Admin UI in English, Korean, Spanish and Chinese.
- 32Supabase Edge Functions
- 129schema migrations
- 9third-party integrations
- 4admin languages
The problem
Small salons run on paper books, Instagram DMs and a card terminal that knows nothing about the client. No-shows cost them real money every week.
Velvet Studio replaces all of it: clients book online, reminders go out automatically, loyal clients are recognised at checkout, and the owner gets a daily AI briefing of the day ahead.
Who it serves — and what they needed
- Salon ownersFewer no-shows, repeat clients, and one place to see the business.
- Stylists & staffTheir own schedule and pricing, on their phone.
- ClientsBook in under a minute, get reminded, get rewarded.
How it fits together
- 01A client books; staff see it instantlyClient → Booking web app → Supabase Postgres → Owner & staff apps
- 02Reminders go out automaticallySupabase Postgres → Edge Functions · Scheduler → Edge Functions → Twilio SMS
- 03In-person checkout through CloverOwner & staff apps → Edge Functions → Clover
- 04Plans, billing and AIEdge Functions → Stripe · Edge Functions → Gemini
What’s inside
Calendar & booking
- Day/week calendar; one booking can hold several services, each with its own stylist
- Server-side conflict checks against working hours and schedule blocks
- Public booking flow, embeddable by iframe, plus a customer portal
Checkout
- Discounts, tax, retail add-ons and deposits
- Clover in-person payments with device sync and refunds
- Per-staff pricing
Marketing
- Segmented SMS campaigns: inactive, high-spend, frequent
- Inbound replies and opt-out handling
- Google review link and merchant SMTP email
Loyalty
- Membership tiers by spend or visits
- Auto-assigned and applied in calendar and public booking
AI
- Multilingual support chatbot over an owner and sales knowledge base (hCaptcha-protected)
- AI daily booking briefing
Platform
- Stripe plans with trial, comped, past-due grace and suspended states
- RevenueCat for app-store subscriptions
- Platform admin console, audit log, rate-limited demo workspaces
Hard parts, solved
- C-01
Three kinds of user, one login
ProblemOwners, staff and customers share one auth system but must land in different tenants with different powers.
ApproachThe tenant key is the owner’s auth id; a single function, rpc_resolve_session_tenant, resolves any session to its role and merchant, and RLS policies — including separate employee-level ones — hang off that.
ResultEvery screen asks one question and gets one answer; no role logic duplicated in the UI.
- C-02
Public booking without public tables
ProblemAnyone must be able to book, but anonymous access to bookings or clients would expose personal data.
ApproachAnonymous roles have no table access at all; booking goes through SECURITY DEFINER RPCs that validate staff, service, time and overlap on the server before writing.
ResultOpen booking, closed data.
- C-03
Two billing systems, one truth
ProblemWeb users pay through Stripe; app-store users must pay through Apple / Google via RevenueCat. Both must unlock the same features.
ApproachWebhooks from both providers write into one plan-state model with explicit trial, grace and suspended transitions.
ResultFeature gating reads one field, regardless of where the customer paid.
Screens
