Jake Rosow
Founder-Operator Case Study
case-study · gifted

GIFTED HQ + Academics

Two live sites for one business. HQ runs the brand and the store, Academics runs the courses, and a customer only ever sees one account.

Role
Founder + architect + integrator
Status
Live
Started
2026-03
URL
gifted-hq.com ↗
Source
Private · live URL + screenshots + architecture notes
family architecture

Two domains, one shared spine.

Two sites, one customer identity, separate operational truth.
01 --- HQ

gifted-hq.com

Marketing, coaching commerce, store, partner portals, and operator tools.

  • Brand site + team profiles
  • Store, subscriptions, partner portals
  • Coach products, templates, affiliates
  • Sanity Studio + Stripe product sync
Clerk one production user pool
Stripe checkout, portal, webhooks
Bunny files, video, course media
02 --- ACADEMICS

gifted-academics.com

Certification courses, checkout, access control, progress, and admin.

  • Course catalog + lesson player
  • Quizzes, progress, enrollments
  • Payment plans + section unlocks
  • Admin orders, discounts, SEO tooling
HQ truth Sanity

Editable business content, coach profiles, merch, training templates, affiliates.

Academics truth Postgres

Courses, purchase options, enrollments, quizzes, lesson progress, webhook dedupe.

hiring signal
What this proves

I can turn an existing operator-run business into production software without flattening the business rules.

Risk handled

Payments, refunds, and course access each break differently, and someone signed in on one site has to already be signed in on the other.

Evidence

Both sites are live. One Clerk identity covers them, Stripe webhooks settle every purchase, and Postgres is the only thing that decides who can open a course.

live surfaces

HQ — store, coaching, consults, and contact.

HQ home
Store
Coach page
Consult form
Contact

Academics — catalog, courses, lessons, and quizzes.

Academics home
Course catalog
Public course view
Lesson player
Quiz flow

Two live websites, one business. HQ runs the public brand, coaching, store, partner portals, and operator tools. Academics runs courses, quizzes, progress, payment plans, and admin. Customers still get one account and one billing relationship.

Why it exists

GIFTED was already a real business. The old digital footprint had the usual founder-operator problem: commerce in one place, courses in another, content edits somewhere else, and support stitched together with memory, spreadsheets, and inbox search.

The rebuild had to be an operating system, not a nicer landing page. It had to handle checkout, fulfillment, refunds, payment plans, and course access for live customers, with admin screens good enough that nobody has to sit and watch them.

What made it hard

Live business software punishes optimistic code. Stripe can retry a webhook. A customer can refresh a success page. A payment plan can renew before a support ticket is closed. A refund should revoke access without corrupting order history. A course user may start on Academics and authenticate through HQ.

I built around those failure modes. HQ uses Sanity for editable business content and syncs publish events into Stripe. Academics uses Postgres for access truth. Webhooks settle the transaction; the interface only reports what already happened.

operating loops
The hard part was not publishing pages. It was making money and access settle correctly.

HQ commerce loop

  1. 01
    Publish

    Sanity product, template, coach, affiliate, or store item

  2. 02
    Sync

    Webhook resolves Stripe product and price IDs, then writes them back

  3. 03
    Checkout

    Customer pays through Stripe with Clerk identity attached

  4. 04
    Fulfill

    Webhook sends email, updates metadata, inventory, Shippo, or downloads

Academics access loop

  1. 01
    Author

    Course sections, lessons, videos, documents, quizzes, purchase options

  2. 02
    Sell

    Stripe Checkout carries course, user, option, and attribution metadata

  3. 03
    Enroll

    WebhookEvent dedupe, then Enrollment upsert in Postgres

  4. 04
    Unlock

    Lesson access follows enrollment status and payment-plan progress

decisions

Where the seams had to go.

Two apps, one shared identity

Chose

HQ and Academics are separate Next.js apps on separate domains. They share one Clerk user pool; Academics is the satellite and HQ is the auth home.

Why

Coaching, store, and course access fail in different ways. Splitting the apps keeps those risks separate while customers still use one account.

Cost

Auth setup is fussier: satellite DNS, redirect origins, two Vercel envs, two webhook surfaces, and support copy for cross-domain sign-in.

Stripe events are the ledger, not a side effect

Chose

Stripe webhooks change state. HQ locks checkout with Upstash Redis and stamps metadata. Academics stores processed event IDs before enrollments, payment-plan updates, refunds, or receipts.

Why

Retries are normal in a live business. Payments, refunds, fulfillment, and access have to survive them without crediting the wrong thing twice.

Cost

Every handler needs idempotency, retry behavior, and a recovery path after Stripe has already accepted the charge.

Use the right content store for each surface

Chose

HQ keeps its merchandising and marketing content in Sanity, where it can be edited without a deploy. Academics keeps courses, quizzes, and enrollment state in Postgres, where it can be queried and constrained.

Why

HQ needs editable business content. Academics needs relational guarantees. Forcing both into one CMS would make the course platform worse.

Cost

Operators need to know the owner of each truth: Sanity, Stripe, Postgres, Clerk, or Bunny. Clean boundaries come with training overhead.

the stack

The stack.

Want the walkthrough, or the parts that aren’t written up yet?

jakerosow@gmail.com
Up next
Blog Automation Workflow Automated GitHub Actions publishing workflow