apps/growth is a small internal SPA (Vite + React + Clerk, plain Tailwind — no Catalyst) for the PolicyCodex team’s own sales operations: website leads, demo-call bookings, and the state of the Resend nurture sequences. It is not customer-facing and holds no tenant data — leads and email sends are internal sales-ops data.

C2 slice

Access model — the important part

  • The tables growth reads (website_leads, email_*) are service-role-only with no RLS policies. The SPA has no direct Supabase path to them; growth-api is the one door.
  • growth-api verifies the Clerk session token in-function (the platform gateway cannot validate Clerk JWTs; verify_jwt = false in config.toml), then requires the caller to be a domain_owner of the internal org identified by the GROWTH_INTERNAL_CLERK_ORG_ID secret.
  • Client-side, SignedIn only means “has a Clerk session” — an unauthorized signed-in user gets a 403 from every fetch. This is deliberate and verified: sign-in cannot be app-restricted on the shared Clerk instance; the function is the boundary.

Surfaces

Deployment: git-connected Cloudflare Pages project policycodex-growth, same pattern as the other three SPAs — git push origin main is the deploy, nothing else. Local dev: pnpm dev:growth (port 5175). The email_* tables are filled by Resend — for the full inventory of templates, automations and their triggering events, see Resend environment (as-built).