Twilio-based phone matching: connect pre-matched pairs for blind phone dates (e.g. Valentine’s Day).
- TypeScript + Express — webhook server
- PostgreSQL + Kysely — participants, matches, conferences, call logs
- Twilio — voice & conference
- Sentry (optional) — error reporting
- pnpm — package manager
- Node.js 18+ (engines: Node 24.x, pnpm 10.x)
- Twilio account + phone number
- PostgreSQL (local or Docker); optional: ngrok for local webhooks
pnpm install
cp .env.example .envEdit .env:
TWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN,TWILIO_PHONE_NUMBERDATABASE_URL(e.g.postgresql://loveline:loveline@localhost:5432/loveline)POSTGRES_USER,POSTGRES_PASSWORD(matchDATABASE_URL)WEBHOOK_BASE_URLin production
Postgres not running? Script will try Docker Compose:
pnpm run db:ensure- Local: Create
data/participants.json(see structure below) or setSEED_FILEto its path. - Production: Set
SEED_DATAto a JSON string (never commit real data).
Each item: phoneNumber (E.164), name, match: { name, phoneNumber }. Pairs must reference each other.
pnpm run db:seed(db:seed is skipped if participants already exist. Use pnpm run db:reset to wipe and re-seed.)
pnpm run devBuild: pnpm run build → pnpm start
- A CALL COMES IN:
POST→{WEBHOOK_BASE_URL}/voice/incoming - STATUS CALLBACK URL:
{WEBHOOK_BASE_URL}/voice/conference-status
Local: use ngrok (ngrok http 3000) and set Twilio URLs to the ngrok HTTPS host.
POST /voice/incoming— incoming call → lookup, welcome, conference dialPOST /voice/wait-music— hold music while waiting for matchPOST /voice/conference-status— conference events (join/leave, exit notification)GET /health— health check
- Caller dials → Twilio →
/voice/incoming - Lookup by phone → if found: welcome + join conference; if not: reject
- First caller hears hold music; when match joins, both are connected
- When one leaves, the other gets exit message and call ends
| Script | Purpose |
|---|---|
pnpm run dev |
db:ensure + ngrok + dev server |
pnpm run db:ensure |
Create DB / start Postgres (Docker) |
pnpm run db:seed |
Seed from SEED_FILE or SEED_DATA |
pnpm run db:seed:prod |
Seed in production (after build) |
pnpm run db:reset |
Reset DB and re-run migrations |
JSON logs → logs/calls.log (timestamps, events, participant/match, errors).
Set env (e.g. Railway/DigitalOcean): TWILIO_*, DATABASE_URL, WEBHOOK_BASE_URL, optional SENTRY_DSN. Point Twilio webhooks to production URL. For seed, set SEED_DATA in host secrets and run pnpm run db:seed:prod after deploy if needed.
♡ ✿ love line ✿ ♡ —— .✧ before your date ✧. ——