Deployment
Kinetic Email is hosted on Vercel with automatic deployments from the main branch — push to main and Vercel builds and deploys.
Build Pipeline
npx convex deploy \
--cmd 'npm run build:vite && npm run build:prerender && npm run build:sitemap' \
--cmd-url-env-var-name VITE_CONVEX_URL --codegen enable -y
This runs in sequence:
- Convex deploy — Deploys real-time tracking functions (requires
CONVEX_DEPLOY_KEYin Vercel) and injectsVITE_CONVEX_URLfor the frontend build - Vite build — Compiles React app to
dist/ - Pre-render — Generates static HTML for SEO (
scripts/prerender.mjs) - Sitemap — Generates
dist/sitemap.xml(scripts/generate-sitemap.mjs)
Vercel Configuration
The vercel.json file defines:
- Framework: Vite
- API functions: Serverless functions from
api/directory - Rewrites: SPA fallback (
/*→index.html), blog OG images - Headers: Security headers (X-Content-Type-Options, X-Frame-Options), CORS, cache control
- CORS: Restricted to
kinetic.emailand localhost origins
Pre-rendering for SEO
The pre-render script (scripts/prerender.mjs) injects static HTML and meta tags into route-specific index.html files:
dist/index.html → Homepage
dist/learn/index.html → Learning hub
dist/playground/index.html → AI Playground
dist/blog/index.html → Blog listing
...
SEO metadata is configured in src/seo/seoConfig.ts. Auth-gated pages (learn modules 2-6, marketing modules 2-5) have noindex: true.
Running Tests Before Deploy
npm test # Run all tests
npm run build # Verify build succeeds
Always run tests before deploying, especially after modifying:
- API routes
- Auth logic
- Token economy functions
- Email generation pipeline
Smoke-Testing api/ After Deploy
Vitest is not Vercel — always verify api/ changes against the real runtime after deploying:
curl -s -X POST https://kinetic.email/api/generate
401JSON response — healthy: the module loaded and auth rejected the unauthenticated request- Plain-text "A server error has occurred" — the function crashed at module load (
FUNCTION_INVOCATION_FAILED)
The most common module-load crash: api/ runs as ESM, so relative imports must carry the .js extension ('./_auth.js', even for .ts sources). Extensionless imports pass Vitest locally but crash every invocation on Vercel.
Also note: migrations that add columns read by deployed client queries must run in Supabase before the deploy — a select() naming a missing column returns a 400.
Environment Variables
All environment variables must be configured in the Vercel project settings. See Environment Variables for the complete list.