Rapport Build Guide
From Spec to Deployment
Overview
Rapport is a lightweight CRM for Advisory OS, built to track high-value client relationships through a custom sales pipeline: Lead → Mini-Site → Proposal → Agreement.
Build Time: ~6 hours across multiple sessions Live URLs:
- App: https://rapport.advisoryos.ai
- API: https://rapport-api.advisoryos.ai
Tech Stack
| Layer | Technology | Host |
|---|---|---|
| Frontend | React + Vite | Vercel |
| Backend | Express.js + Node.js | Railway |
| Database | PostgreSQL | Railway |
| Resend | — | |
| Auth | JWT (access + refresh tokens) | — |
Pre-Build: Mockup Phase
Before writing any code, we built an interactive HTML mockup to test the UX:
- Defined the data model — Contacts, Deals, Tasks, Lead Sources, Pipeline Stages
- Created interactive prototype — Single HTML file with React via CDN
- Iterated on feedback — Added features like:
- Editable fields on deals and contacts
- Task creation from multiple entry points
- Calendar view with recurring task support
- Stage reordering in settings
- Finalized spec — rapport-spec.md with all features documented
Key insight: The mockup caught several UX issues before we wrote backend code.
Phase 1: Environment Setup
Prerequisites (from Cadence)
- Linux enabled on Chromebook
- Node.js installed
- Claude Code installed and authenticated
- GitHub, Railway, Vercel accounts ready
Create Project Folder
mkdir ~/rapport
cd ~/rapport
claude
Create Spec File
Pasted the full rapport-spec.md into Claude Code to create the project specification.
Phase 2: Database Setup (Railway)
- Go to https://railway.app
- Click New Project → Empty Project
- Press Ctrl + K → type "postgres" → Add PostgreSQL
- Get the DATABASEPUBLICURL from the Variables tab (not the internal URL)
Critical: Use the public URL (contains proxy.rlwy.net) for local development, not the internal Railway URL.
Phase 3: Backend Development
Initial Setup
Told Claude Code:
I have a PostgreSQL database ready on Railway. Here's my DATABASE_URL: [pasted URL]
Please set up the monorepo structure with /client and /server folders. Start with the backend - create an Express server that connects to this PostgreSQL database using the data models in rapport-spec.md.
Server Binding Fix
The server needed to bind to 0.0.0.0 (not localhost) for Railway deployment:
app.listen(PORT, '0.0.0.0', () => {...})
API Routes Created
/api/contacts— CRUD for contacts/api/contact-links— Multiple links per contact/api/deals— CRUD for deals/api/deal-notes— Activity log/api/deal-links— Custom links (proposal, mini-site, etc.)/api/tasks— Tasks with recurrence/api/lead-sources— Configurable lead sources/api/pipeline-stages— Configurable stages with probabilities/api/auth— Register, login, refresh, forgot-password, reset-password/health— Health check endpoint
Phase 4: Frontend Development
Build Order
- Pipeline view — Kanban board with drag-and-drop
- Deal detail modal — Edit deals, add notes, add tasks, custom links
- Contacts page — List with search, add/edit contacts
- Contact detail — View deals, tasks, links for a contact
- Tasks page — List view (grouped) + Calendar view
- Settings — Lead sources + Pipeline stages management
- Auth pages — Login, Register, Forgot Password, Reset Password
Key Features Implemented
- Drag-and-drop — Moves deals between pipeline stages, auto-updates probability
- Contact search — By name, email, location, lead source
- Custom links — Editable name + URL on both contacts and deals
- Recurring tasks — Weekly, biweekly, monthly with calendar display
- Stage management — Add, edit, delete, reorder stages
- Recurring task deletion — Options: this instance, this + future, all
Design System (Advisory OS)
- Colors: Charcoal (#1a1a1a), Cream (#f9f7f4), Gold (#b79d64)
- Typography: Cormorant Garamond (headings), Inter (body)
- Gold scrollbar, gold left-border on cards
- Desktop only (no mobile optimization)
Phase 5: Authentication
Told Claude Code:
Add authentication to Rapport:
1. User registration with email and password
2. Login screen as the default page if not logged in
3. JWT tokens for authentication (access + refresh tokens)
4. Forgot password flow - sends reset email using Resend
5. All API routes should require authentication
6. Store user passwords securely (hashed with bcrypt)
Auth Middleware
All /api/* routes (except auth routes) require valid JWT token.
Password Reset Flow
- User enters email on forgot-password page
- Server generates reset token, stores hash in database
- Resend sends email with reset link
- User clicks link → reset-password page
- User enters new password → token validated → password updated
Phase 6: Git & GitHub
Initialize Repository
git init
git add .
git commit -m "Initial commit"
Create GitHub Repo
- Go to https://github.com/new
- Create private repo named "rapport"
- Don't initialize with README
Push to GitHub
git remote add origin https://github.com/USERNAME/rapport.git
git push -u origin main
When prompted, use Personal Access Token (not password):
- Create at https://github.com/settings/tokens/new
- Check repo scope only
- Copy immediately (only shown once)
Phase 7: Deploy Backend (Railway)
Add GitHub Repo to Railway
- In Railway project (where Postgres lives), press Ctrl + K
- Select GitHub Repo
- Click Configure GitHub App if repo not visible
- Select rapport repo
Configure Service
- Click rapport service → Settings
- Set Root Directory to
server
Environment Variables
Add these in the Variables tab:
| Variable | Value |
|---|---|
| DATABASE_URL | (Add Reference → select from Postgres) |
| NODE_ENV | production |
| JWT_SECRET | (random string) |
| JWTREFRESHSECRET | (different random string) |
| RESENDAPIKEY | (from Resend dashboard) |
| RESENDFROMEMAIL | onboarding@resend.dev |
| CLIENT_URL | https://rapport.advisoryos.ai |
Note: Use Raw Variable, not Add Reference, for manual values.
Custom Domain
- Settings → Networking → Generate Domain
- Add custom domain:
rapport-api.advisoryos.ai - Add CNAME in DNS:
rapport-api→[railway-url].up.railway.app
Phase 8: Deploy Frontend (Vercel)
Initial Setup
- Go to https://vercel.com
- Click Add New Project
- Select rapport repo
- Set Root Directory to
client - Framework Preset: Vite
Environment Variable
| Variable | Value |
|---|---|
| VITEAPIURL | https://rapport-api.advisoryos.ai/api |
Note: Vite environment variables must start with VITE_ to be exposed to the client.
API URL Configuration
Due to build caching issues, we hardcoded the production URL:
// client/src/api/index.js
const API_BASE = window.location.hostname === 'localhost'
? 'http://localhost:3001/api'
: 'https://rapport-api.advisoryos.ai/api'
Client-Side Routing Fix
Created client/vercel.json for React Router to handle all routes:
{
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
Custom Domain
- Settings → Domains → Add Existing
- Enter:
rapport.advisoryos.ai - Add CNAME in DNS:
rapport→cname.vercel-dns.com
Phase 9: Resend Email Setup
For Testing
- Use
onboarding@resend.devas sender - Only delivers to your verified Resend email
For Production (TODO)
- Go to Resend → Domains → Add Domain
- Add
advisoryos.ai - Add DNS records they provide (TXT, MX)
- Once verified, change
RESENDFROMEMAILtonoreply@advisoryos.ai
Troubleshooting
| Problem | Solution |
|---|---|
| "Failed to fetch" on frontend | Check CORS config in backend, verify API URL |
| API returns 404 | Check route mounting in index.js, verify Root Directory in Railway |
| Server not accessible externally | Bind to 0.0.0.0, not localhost |
| Password reset email not sending | Check RESENDAPIKEY, RESENDFROMEMAIL, CLIENT_URL in Railway |
| Reset link shows 404 | Add vercel.json with rewrites for client-side routing |
| Environment variables not working | Redeploy with "Use existing build cache" UNCHECKED |
| GitHub push fails | Use Personal Access Token, not password |
| Railway variables corrupted | Delete and re-add as Raw Variable |
Redeployment Checklist
After Code Changes
git add .
git commit -m "Description of changes"
git push origin main
- Railway: Auto-redeploys on push
- Vercel: Auto-redeploys, but for env var changes: Redeploy without cache
Redeploy Without Cache (Vercel)
- Deployments tab
- Three dots on latest → Redeploy
- Uncheck "Use existing build cache"
- Redeploy
Features Summary
Core CRM
- ✅ Pipeline view (Kanban with drag-and-drop)
- ✅ Contacts (list, search, detail, custom links)
- ✅ Deals (notes, custom links, probability)
- ✅ Tasks (list view, calendar view, recurring)
- ✅ Settings (lead sources, pipeline stages)
Authentication
- ✅ Register / Login / Logout
- ✅ JWT with refresh tokens
- ✅ Forgot password with email reset
Phase 2 (Future)
- ⬜ Offers/Products library (preset deal templates)
- ⬜ Email integration (Gmail API)
Costs
| Service | Cost |
|---|---|
| Railway | ~$5/month (database + backend) |
| Vercel | Free (hobby tier) |
| Resend | Free (3,000 emails/month) |
| Domain | Already owned |
Guide created: January 28, 2026