← Vault Index
Source: tools/rapport/rapport-build-guide.md

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:


Tech Stack

LayerTechnologyHost
FrontendReact + ViteVercel
BackendExpress.js + Node.jsRailway
DatabasePostgreSQLRailway
EmailResend—
AuthJWT (access + refresh tokens)—

Pre-Build: Mockup Phase

Before writing any code, we built an interactive HTML mockup to test the UX:

  1. Defined the data model — Contacts, Deals, Tasks, Lead Sources, Pipeline Stages
  2. Created interactive prototype — Single HTML file with React via CDN
  3. Iterated on feedback — Added features like:
  1. 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)

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)

  1. Go to https://railway.app
  2. Click New Project → Empty Project
  3. Press Ctrl + K → type "postgres" → Add PostgreSQL
  4. 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


Phase 4: Frontend Development

Build Order

  1. Pipeline view — Kanban board with drag-and-drop
  2. Deal detail modal — Edit deals, add notes, add tasks, custom links
  3. Contacts page — List with search, add/edit contacts
  4. Contact detail — View deals, tasks, links for a contact
  5. Tasks page — List view (grouped) + Calendar view
  6. Settings — Lead sources + Pipeline stages management
  7. Auth pages — Login, Register, Forgot Password, Reset Password

Key Features Implemented

Design System (Advisory OS)


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

  1. User enters email on forgot-password page
  2. Server generates reset token, stores hash in database
  3. Resend sends email with reset link
  4. User clicks link → reset-password page
  5. 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

  1. Go to https://github.com/new
  2. Create private repo named "rapport"
  3. 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):


Phase 7: Deploy Backend (Railway)

Add GitHub Repo to Railway

  1. In Railway project (where Postgres lives), press Ctrl + K
  2. Select GitHub Repo
  3. Click Configure GitHub App if repo not visible
  4. Select rapport repo

Configure Service

  1. Click rapport service → Settings
  2. Set Root Directory to server

Environment Variables

Add these in the Variables tab:

VariableValue
DATABASE_URL(Add Reference → select from Postgres)
NODE_ENVproduction
JWT_SECRET(random string)
JWTREFRESHSECRET(different random string)
RESENDAPIKEY(from Resend dashboard)
RESENDFROMEMAILonboarding@resend.dev
CLIENT_URLhttps://rapport.advisoryos.ai

Note: Use Raw Variable, not Add Reference, for manual values.

Custom Domain

  1. Settings → Networking → Generate Domain
  2. Add custom domain: rapport-api.advisoryos.ai
  3. Add CNAME in DNS: rapport-api → [railway-url].up.railway.app

Phase 8: Deploy Frontend (Vercel)

Initial Setup

  1. Go to https://vercel.com
  2. Click Add New Project
  3. Select rapport repo
  4. Set Root Directory to client
  5. Framework Preset: Vite

Environment Variable

VariableValue
VITEAPIURLhttps://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

  1. Settings → Domains → Add Existing
  2. Enter: rapport.advisoryos.ai
  3. Add CNAME in DNS: rapport → cname.vercel-dns.com

Phase 9: Resend Email Setup

For Testing

For Production (TODO)

  1. Go to Resend → Domains → Add Domain
  2. Add advisoryos.ai
  3. Add DNS records they provide (TXT, MX)
  4. Once verified, change RESENDFROMEMAIL to noreply@advisoryos.ai

Troubleshooting

ProblemSolution
"Failed to fetch" on frontendCheck CORS config in backend, verify API URL
API returns 404Check route mounting in index.js, verify Root Directory in Railway
Server not accessible externallyBind to 0.0.0.0, not localhost
Password reset email not sendingCheck RESENDAPIKEY, RESENDFROMEMAIL, CLIENT_URL in Railway
Reset link shows 404Add vercel.json with rewrites for client-side routing
Environment variables not workingRedeploy with "Use existing build cache" UNCHECKED
GitHub push failsUse Personal Access Token, not password
Railway variables corruptedDelete and re-add as Raw Variable

Redeployment Checklist

After Code Changes

git add .
git commit -m "Description of changes"
git push origin main

Redeploy Without Cache (Vercel)

  1. Deployments tab
  2. Three dots on latest → Redeploy
  3. Uncheck "Use existing build cache"
  4. Redeploy

Features Summary

Core CRM

Authentication

Phase 2 (Future)


Costs

ServiceCost
Railway~$5/month (database + backend)
VercelFree (hobby tier)
ResendFree (3,000 emails/month)
DomainAlready owned

Guide created: January 28, 2026