Founder-Operator Case Study
case-study · gifted

GIFTED HQ + Academics

Two live sites for one business: HQ runs brand, coaching, commerce, and operations; Academics runs courses, quizzes, payments, and access.

Role
Founder + architect + integrator
Status
Live
Started
2026-03
URL
gifted-hq.com ↗
Source
Private · live URL + screenshots + architecture notes
// 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, course access, cross-domain auth, product sync, and support flows all fail in different ways.

Evidence

Two live surfaces, shared Clerk identity, Stripe webhook settlement, Sanity-to-Stripe sync, Postgres access truth, and product screenshots.

// 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

// family architecture
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.

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: product setup, checkout, fulfillment, course access, refunds, payment plans, video, discounts, and admin that could handle live customers without babysitting.

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 are the settlement layer, not a callback afterthought.

// 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

// pragmatic decisions

Three trade-offs worth naming.

The short version: choice, reason, cost.

01

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.

02

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.

03

Use the right content store for each surface

Chose

HQ uses Sanity for merch, coaching products, templates, profiles, affiliates, and marketing content. Academics uses Postgres for courses, lessons, quizzes, enrollments, progress, and payment-plan unlocks.

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.

Up next
Shipit News — AI / coding news aggregator