Documentation
Self-host on Vercel
Run the same Next.js app as production: UI, /api routes, OAuth, and webhooks on one Vercel project with Neon Postgres.
Deploy guide
This page mirrors DEPLOY.md in the repository. Update the file in git to keep production docs in sync.
Deploy on Vercel
Production runs as a single Next.js app on Vercel. Pages (/, /demo, /app) and API routes (/api/*) execute in the same deployment — there is no separate Railway backend and no /api proxy.
Do not set BACKEND_URL on Vercel unless you are deliberately proxying some routes to another host (legacy). Leaving it unset is correct for heyrepairo.in-style deploys.
Order of operations
- Create a Neon database (or Vercel → Storage → Postgres) and copy the connection string.
- Import the repo on Vercel → add environment variables below.
- Add
DATABASE_URL(and preferDATABASE_URL_UNPOOLEDfor Neon migrations) on Vercel before the first production deploy —vercel.jsonrunsnpm run db:migratebeforenext build. - Create a GitHub OAuth App with callback on your Vercel domain.
- Deploy → smoke test
/api/health,/demo,/app→ Continue with GitHub.
If OAuth fails with “Failed query” or “relation users does not exist”, the schema was never applied — redeploy with DATABASE_URL set, or run npm run db:migrate locally against the same Neon URL.
Vercel environment variables
Project → Settings → Environment Variables (Production, Preview, and Development as needed).
Required (hosted /app + Quick Repair)
| Variable | Value |
|---|---|
APP_URL | Public site URL, no trailing slash — e.g. https://www.heyrepairo.in |
DATABASE_URL | Neon connection string (or use POSTGRES_URL from Vercel Postgres) |
GITHUB_CLIENT_ID | GitHub OAuth App |
GITHUB_CLIENT_SECRET | GitHub OAuth App |
SESSION_SECRET | 32+ random characters (openssl rand -base64 32) |
Optional but recommended:
| Variable | Value |
|---|---|
TOKEN_ENCRYPTION_KEY | 32+ chars — encrypts stored GitHub tokens at rest |
NEXT_PUBLIC_GITHUB_APP_SLUG | Slug from https://github.com/apps/<slug> — Install App CTA |
APP_ID, PRIVATE_KEY, WEBHOOK_SECRET | GitHub App — webhooks at {APP_URL}/api/github/webhooks |
Optional
| Variable | Purpose |
|---|---|
SARVAM_API_KEY | Otto chat widget (/api/chat) |
SARVAM_MODEL, SARVAM_BASE_URL, SARVAM_REASONING_EFFORT | Otto tuning |
STRIPE_SECRET_KEY, STRIPE_PRICE_PRO, STRIPE_WEBHOOK_SECRET | Pro billing |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Checkout UI |
CRON_SECRET | Protect /api/cron/poll-vendors (Vercel Cron in vercel.json also runs daily) |
ANTHROPIC_API_KEY | Not used by the website — CLI --agent-resolve only |
AUTH_SECRET, AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET | Google waitlist sign-in (if enabled) |
NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION | Google Search Console HTML tag verification (optional) |
After changing secrets, redeploy Vercel so serverless functions pick them up.
SEO & AI discovery (no secrets)
- Set
APP_URLto your canonical domain (includingwwwif that is primary) — drives sitemap,llms.txt, Open Graph, and JSON-LD. - Public summaries:
/llms.txt,/llms-full.txt,/ai.txt(also at/.well-known/llms.txt). - Submit
https://YOUR-DOMAIN/sitemap.xmlin Google Search Console.
GitHub OAuth App
- https://github.com/settings/developers → OAuth Apps → New (or edit existing)
- Homepage URL:
https://YOUR-VERCEL-DOMAIN - Authorization callback URL:
https://YOUR-VERCEL-DOMAIN/api/auth/callback - Copy Client ID and generate Client secret → set on Vercel (
GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET), not Railway. - Set
APP_URLon Vercel to the same domain (includingwwwif that is canonical). - Redeploy.
If login fails with redirect_uri_mismatch, either register the exact callback above on the OAuth App, or set EXPLICIT_REDIRECT_URI=true and GITHUB_CALLBACK_URL to that same URL.
Stripe billing (optional, required for Pro)
- Stripe → Product “Repairo Pro” → $29/mo price → copy
price_… - On Vercel:
STRIPE_SECRET_KEY,STRIPE_PRICE_PRO,STRIPE_WEBHOOK_SECRET - Webhook endpoint:
https://YOUR-VERCEL-DOMAIN/api/webhooks/stripe
Events:checkout.session.completed,customer.subscription.updated,customer.subscription.deleted,invoice.payment_failed - Redeploy →
/api/healthshould report"stripe": truewhen configured.
Vendor OpenAPI polling (cron)
vercel.json schedules /api/cron/poll-vendors daily. Set CRON_SECRET on Vercel and ensure the cron route checks it (or use GitHub Actions — see .github/workflows/vendor-poll-cron.yml with CRON_TARGET_URL = your Vercel URL).
Manual trigger:
Smoke test
https://YOUR-VERCEL-DOMAIN/api/health→{ "ok": true, ... }/app→ Continue with GitHub → authorize- Connect a repo → run repair → Open pull request
/demo— fixture flow without OAuth- Install GitHub App from the site CTA (separate from OAuth)
Custom domain
- Add apex/www on Vercel
- Update
APP_URLto the canonical domain - Update GitHub OAuth homepage + callback URLs
- Redeploy
Troubleshooting
| Issue | Fix |
|---|---|
“OAuth not configured” on /app | Set GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, SESSION_SECRET on Vercel and redeploy |
| OAuth lands then fails | Callback URL must match OAuth App registration; APP_URL must match the URL users open |
| DB errors / empty workspace | DATABASE_URL missing or migrations not applied (npm run db:migrate) |
BACKEND_URL set | Unset it on Vercel unless you still proxy to another API host |
| Stripe checkout OK but plan stays free | Webhook URL + STRIPE_WEBHOOK_SECRET on Vercel |
| Otto chat unavailable | SARVAM_API_KEY on Vercel (optional product surface) |
Optional: Docker / Railway (self-host)
Dockerfile + railway.toml remain for running a standalone Node image (e.g. local Docker, OUTPUT_STANDALONE=1). That path is not the current heyrepairo.in architecture. For the public product, use Vercel + Neon only.
LinkedIn / launch copy
Use your Vercel URL in posts. See LAUNCH.md if present.