Here is the complete structured report:

---

# Growth Engine Routes Audit Report

**Audit Date:** 2026-02-07
**Codebase:** `/home/ccuser/rateright-growth/src/routes/`
**Total Route Files:** 46 (.js) + 1 (.patch)
**Total Endpoints:** ~260+

---

## 1. COMPLETE ENDPOINT INVENTORY

### health.js (PUBLIC -- No Auth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/health/` | System health status with DB, OpenAI checks |
| GET | `/health/ping` | Simple ping for load balancers |

### webhooks.js (PUBLIC -- webhookLimiter only, no auth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/webhooks/twilio/inbound` | Receive inbound SMS from Twilio |
| GET | `/api/webhooks/twilio/inbound` | Test/info endpoint for Twilio SMS webhook |
| GET | `/api/webhooks/twilio/test-lookup` | Test phone number lookup in leads DB |
| POST | `/api/webhooks/twilio/status` | Receive SMS delivery status from Twilio |
| POST | `/api/webhooks/website-signup` | Receive website form signups (X-Webhook-Key auth) |
| GET | `/api/webhooks/website-signup` | Info endpoint for website signup webhook |

### voice.js -- voiceWebhooks router (PUBLIC -- webhookLimiter + validateTwilioWebhookMiddleware)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/voice/twiml` | Test webhook accessibility |
| POST | `/api/voice/twiml` | TwiML instructions for outbound calls |
| POST | `/api/voice/inbound` | Handle inbound voice calls |
| POST | `/api/voice/inbound-status` | Inbound call status updates |
| POST | `/api/voice/voicemail` | Voicemail recording handler |
| POST | `/api/voice/voicemail-complete` | Voicemail recording complete |
| POST | `/api/voice/call-status` | Call status callbacks |
| POST | `/api/voice/recording-status` | Recording status + triggers transcription |
| POST | `/api/voice/transcribe-recording` | Transcribe a recording |
| POST | `/api/voice/backfill-transcripts` | Backfill transcripts (uses X-API-Key inline) |

### voice.js -- router (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/voice/token` | Generate Twilio Voice access token |
| GET | `/api/voice/lookup-caller` | Look up caller info by phone |
| GET | `/api/voice/status` | Voice configuration status |

### vapi.js (PUBLIC -- webhookLimiter + validateVapiWebhookMiddleware)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/vapi/webhook` | Receive VAPI end-of-call webhooks |
| GET | `/api/vapi/status` | VAPI configuration status |
| GET | `/api/vapi/tasks` | List recent VAPI tasks |
| POST | `/api/vapi/tasks/:id/retry` | Retry a failed VAPI task |

### intelligence.js (PUBLIC -- trackingLimiter only, no auth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/intelligence/track` | Track a usage event |
| POST | `/api/intelligence/track-batch` | Track multiple events (max 50) |
| GET | `/api/intelligence/suggestions` | Get improvement suggestions |
| PATCH | `/api/intelligence/suggestions/:id/status` | Update suggestion status |
| POST | `/api/intelligence/suggestions/:id/measure-impact` | Measure suggestion impact |
| GET | `/api/intelligence/friction` | Get friction patterns |
| GET | `/api/intelligence/weekly-report` | Get weekly intelligence report |
| GET | `/api/intelligence/metrics` | Get aggregated metrics |
| GET | `/api/intelligence/page-stats` | Get page-level stats |
| GET | `/api/intelligence/dashboard` | Intelligence dashboard data |

### frontendTracking.js (PUBLIC -- trackingLimiter only, no auth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/frontend/vitals` | Store web vital metrics |
| POST | `/api/frontend/error` | Store frontend errors |
| GET | `/api/frontend/vitals/summary` | Get web vitals summary |

### Inline Notification routes (index.js -- mixed auth)
| Method | Path | Auth | Purpose |
|--------|------|------|---------|
| GET | `/api/notifications/vapid-public-key` | None | Get VAPID public key |
| POST | `/api/notifications/subscribe` | requireAuth | Register push subscription |
| GET | `/api/notifications/status` | requireAuth | Get subscription status |
| GET | `/api/notifications/preferences` | requireAuth | Get notification preferences |
| PUT | `/api/notifications/preferences` | requireAuth | Update notification preferences |
| POST | `/api/notifications/test` | requireAuth | Test notification (placeholder) |

### jobs.js -- internalJobTrigger (X-Internal-Key auth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/jobs/internal/:jobName` | Trigger job with internal API key |

### jobs.js -- router (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/jobs/:jobName` | Manually trigger a job |
| GET | `/api/jobs/` | List available jobs |
| GET | `/api/jobs/audit-history` | Get audit history for trends |
| GET | `/api/jobs/audit-latest` | Get most recent audit result |
| GET | `/api/jobs/rep-performance` | Get weekly rep performance report |
| GET | `/api/jobs/call-scores/:userId` | Get call quality scores for a rep |

### leads.js -- internalLeadsRouter (X-Internal-Key auth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/leads/internal/hot` | Fetch hot leads for Clawdbot sync |

### leads.js -- router (requireApiKeyOrAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/leads/` | List leads (with 60s cache) |
| GET | `/api/leads/search` | Search leads |
| GET | `/api/leads/assignments` | Get lead assignments |
| GET | `/api/leads/:id` | Get single lead |
| POST | `/api/leads/` | Create a lead |
| PATCH | `/api/leads/:id` | Update a lead |
| POST | `/api/leads/import-sheet` | Import leads from sheet (importLimiter) |
| POST | `/api/leads/:id/archive` | Archive a lead |
| POST | `/api/leads/:id/claim` | Claim a lead |
| POST | `/api/leads/:id/release` | Release a lead |
| POST | `/api/leads/:id/notes` | Add note to lead |
| GET | `/api/leads/:id/notes` | Get lead notes |
| PATCH | `/api/leads/:id/intel` | Update lead intel |
| GET | `/api/leads/:id/intel` | Get lead intel |
| DELETE | `/api/leads/:id` | Delete a lead (deleteLimiter) |

### calls.js (requireApiKeyOrAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/calls/log` | Log a call outcome |
| GET | `/api/calls/outcomes` | Get valid call outcomes |
| GET | `/api/calls/history/:leadId` | Get call history for a lead |
| GET | `/api/calls/recording/:communicationId` | Get recording for a communication |

### callList.js (requireApiKeyOrAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/call-list/` | Smart ranked call list |
| GET | `/api/call-list/stats` | Call list statistics |
| GET | `/api/call-list/activity` | Call activity data |

### sms.js (requireApiKeyOrAuth + per-endpoint rate limiting)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/sms/templates` | List SMS templates |
| GET | `/api/sms/templates/:id` | Get single template |
| POST | `/api/sms/templates` | Create SMS template |
| PUT | `/api/sms/templates/:id` | Update SMS template |
| DELETE | `/api/sms/templates/:id` | Delete SMS template |
| POST | `/api/sms/templates/:id/use` | Record template use |
| POST | `/api/sms/send` | Send SMS (smsLimiter) |
| POST | `/api/sms/send-batch` | Send batch SMS (smsLimiter) |
| GET | `/api/sms/scheduled` | List scheduled SMS |
| DELETE | `/api/sms/scheduled/:id` | Cancel scheduled SMS |
| POST | `/api/sms/batch` | Batch SMS send (smsBatchLimiter) |
| POST | `/api/sms/request-contact` | Request contact info from worker |
| GET | `/api/sms/conversations` | List SMS conversations |
| GET | `/api/sms/conversation/:leadId` | Get conversation for a lead |
| PATCH | `/api/sms/conversation/:leadId/read` | Mark conversation as read |
| PATCH | `/api/sms/conversation/:leadId/handle` | Assign handler to conversation |
| GET | `/api/sms/conversations/prioritized` | Get prioritized conversations |
| POST | `/api/sms/ai-suggest` | Get AI reply suggestion |
| GET | `/api/sms/stats` | SMS statistics |
| POST | `/api/sms/preview` | Preview SMS with variables |
| POST | `/api/sms/flag/:id` | Flag a message |
| GET | `/api/sms/response-times` | Response time analytics |

### ai.js (requireApiKeyOrAuth + aiLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/ai/status` | AI service status (OpenAI, Deepgram) |
| GET | `/api/ai/intel-brief/:leadId` | Generate AI intelligence brief |
| POST | `/api/ai/post-call-summary` | Generate post-call summary |
| POST | `/api/ai/draft-message` | AI message drafting (expensiveAiLimiter) |
| POST | `/api/ai/draft-similar` | Draft similar message |
| POST | `/api/ai/transcribe` | Transcribe audio file (expensiveAiLimiter) |
| POST | `/api/ai/extract-lead-corrections` | Extract corrections from transcript |
| POST | `/api/ai/voice-command` | Process voice command |
| POST | `/api/ai/explain-message` | Explain a message |
| POST | `/api/ai/objection-response` | Generate objection response |
| GET | `/api/ai/conversion-probability/:leadId` | Get conversion probability |
| POST | `/api/ai/weekly-insights` | Generate weekly insights (strictLimiter) |
| POST | `/api/ai/cleanup-notes` | Clean up notes |
| GET | `/api/ai/insights` | Get recent AI insights |
| GET | `/api/ai/deepgram-credentials` | Check Deepgram configured status |
| POST | `/api/ai/copilot-session` | Save copilot session data |
| POST | `/api/ai/suggestion-feedback` | Submit suggestion feedback |
| GET | `/api/ai/effective-responses/:objectionType` | Get effective responses |
| GET | `/api/ai/similar-wins/:leadId` | Get similar successful deals |
| GET | `/api/ai/live-intel/:leadId` | Live intelligence for a lead |
| GET | `/api/ai/company-research/:companyName` | Research company (expensiveAiLimiter) |
| GET | `/api/ai/person-research/:leadId` | Research person (expensiveAiLimiter) |
| POST | `/api/ai/update-intel` | Update lead intel from AI |
| POST | `/api/ai/extract-contractor` | Extract contractor from transcript |
| POST | `/api/ai/parse-intel-voice` | Parse intel from voice input |

### dashboard.js (requireApiKeyOrAuth + dashboardLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/dashboard/stats` | Dashboard statistics |
| GET | `/api/dashboard/wisdom` | Sales wisdom/tips |
| POST | `/api/dashboard/wisdom/rate` | Rate wisdom |
| GET | `/api/dashboard/strategy` | AI strategy recommendations |
| GET | `/api/dashboard/slipping` | Slipping leads |
| GET | `/api/dashboard/priority-action` | Priority action items |
| GET | `/api/dashboard/all` | All dashboard data combined |

### activation.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/activation/leads-at-stage` | Get leads at a funnel stage |
| GET | `/api/activation/funnel` | Activation funnel metrics |
| GET | `/api/activation/stuck` | Stuck leads analysis |
| GET | `/api/activation/revenue-at-risk` | Revenue at risk calculation |
| GET | `/api/activation/metrics` | Activation metrics |
| GET | `/api/activation/stage-transitions` | Stage transition analytics |
| POST | `/api/activation/bulk-nudge` | Send bulk nudges |
| POST | `/api/activation/add-to-call-list` | Add leads to call list |
| GET | `/api/activation/analyze` | Analyze funnel |
| POST | `/api/activation/nudge` | Send single nudge |
| GET | `/api/activation/action-script` | Get action script for stage |
| GET | `/api/activation/dropoff-analysis` | Dropoff analysis |
| GET | `/api/activation/journey/:leadId` | Lead journey timeline |
| GET | `/api/activation/diagnose/:leadId` | Diagnose lead issues |
| GET | `/api/activation/similar-wins/:leadId` | Find similar converted leads |
| GET | `/api/activation/nudge-templates` | List nudge templates |
| POST | `/api/activation/nudge-templates` | Create nudge template |
| PATCH | `/api/activation/nudge-templates/:id` | Update nudge template |
| DELETE | `/api/activation/nudge-templates/:id` | Delete nudge template |

### scripts.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/scripts/guidelines` | Get script guidelines |
| PUT | `/api/scripts/guidelines` | Update guidelines |
| GET | `/api/scripts/guidelines/history` | Guidelines change history |
| POST | `/api/scripts/compliance/check` | Check script compliance |
| POST | `/api/scripts/compliance/auto-fix` | Auto-fix compliance issues |
| POST | `/api/scripts/compliance/validate/:id` | Validate specific script |
| POST | `/api/scripts/compliance/batch-validate` | Batch validate scripts |
| GET | `/api/scripts/compliance/queue` | Compliance review queue |
| GET | `/api/scripts/compliance/stats` | Compliance statistics |
| POST | `/api/scripts/compliance/queue/:id/approve` | Approve script |
| POST | `/api/scripts/compliance/queue/:id/reject` | Reject script |
| POST | `/api/scripts/compliance/queue/:id/edit` | Edit queued script |
| GET | `/api/scripts/` | List scripts |
| GET | `/api/scripts/:id` | Get single script |
| POST | `/api/scripts/` | Create script |
| PATCH | `/api/scripts/:id` | Update script |
| DELETE | `/api/scripts/:id` | Delete script |
| POST | `/api/scripts/:id/use` | Record script use |
| PATCH | `/api/scripts/sessions/:sessionId` | Update script session |
| GET | `/api/scripts/for-lead/:leadId` | Get scripts for a lead |
| GET | `/api/scripts/ai-recommend/:leadId` | AI script recommendation |
| GET | `/api/scripts/analytics/overview` | Script analytics overview |
| GET | `/api/scripts/objections/list` | List objections |
| GET | `/api/scripts/objections/:id` | Get single objection |
| POST | `/api/scripts/objections` | Create objection |
| POST | `/api/scripts/objections/:id/response` | Add response to objection |

### achievements.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/achievements/` | List all achievements |
| GET | `/api/achievements/user` | Get user achievements |
| POST | `/api/achievements/check` | Check achievement progress |

### xp.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/xp/user` | Get user XP data |
| POST | `/api/xp/award` | Award XP |
| GET | `/api/xp/history` | Get XP history |
| GET | `/api/xp/leaderboard` | XP leaderboard |

### coaching.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/coaching/scores` | Get coaching scores |
| POST | `/api/coaching/score` | Submit coaching score |
| GET | `/api/coaching/tips` | Get coaching tips |
| GET | `/api/coaching/weekly` | Weekly coaching report |
| GET | `/api/coaching/patterns` | Coaching patterns |

### analytics.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/analytics/trends` | Get analytics trends |
| GET | `/api/analytics/patterns` | Get patterns |
| GET | `/api/analytics/funnel` | Funnel analytics |
| GET | `/api/analytics/best-times` | Best times to contact |
| GET | `/api/analytics/sources` | Lead source analytics |
| POST | `/api/analytics/patterns` | Generate pattern analysis |
| POST | `/api/analytics/vitals` | Store web vitals |
| POST | `/api/analytics/error` | Store frontend errors |
| GET | `/api/analytics/vitals/summary` | Get vitals summary |

### aiAlerts.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/ai-alerts/` | List AI alerts |
| POST | `/api/ai-alerts/` | Create AI alert |
| PATCH | `/api/ai-alerts/:id/read` | Mark alert as read |
| PATCH | `/api/ai-alerts/:id/dismiss` | Dismiss alert |
| POST | `/api/ai-alerts/read-all` | Mark all alerts read |
| POST | `/api/ai-alerts/generate` | Generate AI alerts |

### prompts.js (requireAuth + smsLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/prompts/` | List prompts |
| GET | `/api/prompts/:id` | Get single prompt |
| POST | `/api/prompts/` | Create prompt |
| PUT | `/api/prompts/:id` | Update prompt |
| DELETE | `/api/prompts/:id` | Delete prompt |
| POST | `/api/prompts/generate` | Generate prompt |
| POST | `/api/prompts/:id/preview` | Preview prompt |
| POST | `/api/prompts/:id/send` | Send via prompt |
| POST | `/api/prompts/:id/bulk-send` | Bulk send via prompt |
| POST | `/api/prompts/recipients` | Get recipients for prompt |
| GET | `/api/prompts/history` | Prompt send history |
| GET | `/api/prompts/stats` | Prompt statistics |

### battleground.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/battleground/leaderboard` | Get leaderboard |
| GET | `/api/battleground/leaderboard/:userId` | Get user leaderboard data |
| POST | `/api/battleground/leaderboard/snapshot` | Save leaderboard snapshot |
| GET | `/api/battleground/hall-of-fame` | Get hall of fame |
| POST | `/api/battleground/hall-of-fame/check` | Check hall of fame eligibility |
| GET | `/api/battleground/challenges` | Get challenges |
| POST | `/api/battleground/challenges/progress` | Update challenge progress |
| POST | `/api/battleground/challenges/generate` | Generate challenges |
| GET | `/api/battleground/pulse` | Get team pulse |
| POST | `/api/battleground/pulse` | Create pulse event |
| GET | `/api/battleground/badges/:userId` | Get user badges |
| GET | `/api/battleground/badges` | Get all badges |
| POST | `/api/battleground/badges/award-weekly` | Award weekly badges |
| POST | `/api/battleground/streak/update` | Update streak |

### battlegroundV2.js (requireAuth -- mounted at same path /api/battleground)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/battleground/duels/types` | Get duel types |
| POST | `/api/battleground/duels/challenge` | Challenge to a duel |
| POST | `/api/battleground/duels/:id/accept` | Accept duel |
| POST | `/api/battleground/duels/:id/decline` | Decline duel |
| GET | `/api/battleground/duels/active` | Get active duels |
| GET | `/api/battleground/duels/history` | Get duel history |
| GET | `/api/battleground/duels/:id/scores` | Get duel scores |
| GET | `/api/battleground/duels/odds` | Get duel odds |
| GET | `/api/battleground/challenges/config` | Get challenge config |
| POST | `/api/battleground/challenges` | Create challenge |
| GET | `/api/battleground/challenges` | List challenges |
| GET | `/api/battleground/challenges/:id` | Get single challenge |
| POST | `/api/battleground/challenges/:id/join` | Join challenge |
| DELETE | `/api/battleground/challenges/:id` | Delete challenge |
| GET | `/api/battleground/celebrations` | Get celebrations |
| GET | `/api/battleground/team` | Get team data |

### dossier.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/dossier/:leadId` | Get lead dossier |
| PATCH | `/api/dossier/:leadId` | Update lead dossier |
| POST | `/api/dossier/:leadId/add-fact` | Add fact to dossier |
| POST | `/api/dossier/:leadId/add-call-summary` | Add call summary |
| POST | `/api/dossier/:leadId/add-objection` | Add objection |

### manager.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/manager/dashboard` | Manager dashboard |
| GET | `/api/manager/rep/:userId` | Get rep details |
| GET | `/api/manager/analytics` | Manager analytics |

### playbook.js (requireAuth + smsLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/playbook/today` | Today's playbook |
| POST | `/api/playbook/send` | Send playbook message |
| GET | `/api/playbook/bulk-preview` | Preview bulk send |
| POST | `/api/playbook/bulk-send` | Bulk send messages |
| GET | `/api/playbook/sections` | List playbook sections |
| GET | `/api/playbook/sections/:category` | Get sections by category |
| POST | `/api/playbook/sections` | Create section |
| PUT | `/api/playbook/sections/:id` | Update section |
| DELETE | `/api/playbook/sections/:id` | Delete section |
| POST | `/api/playbook/sections/:id/record-use` | Record section use |
| GET | `/api/playbook/recommend/:leadId` | AI section recommendation |

### voicemail.js (requireAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/voicemail/recordings` | List voicemail recordings |
| POST | `/api/voicemail/recordings` | Upload voicemail recording |
| DELETE | `/api/voicemail/recordings/:id` | Delete recording |
| PATCH | `/api/voicemail/recordings/:id/default` | Set default recording |
| POST | `/api/voicemail/drop` | Drop voicemail to lead |
| GET | `/api/voicemail/drops/stats` | Voicemail drop stats |

### dev.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/dev/bug` | Report a bug |
| POST | `/api/dev/feature` | Request a feature |
| GET | `/api/dev/queue` | View dev queue |
| PATCH | `/api/dev/queue/:id/status` | Update queue item status |
| GET | `/api/dev/queue/:id` | Get queue item details |

### sequences.js (requireApiKeyOrAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/sequences/` | List sequences |
| GET | `/api/sequences/check-enrollments` | Check enrollments for lead |
| GET | `/api/sequences/:id` | Get single sequence |
| POST | `/api/sequences/:id/enroll` | Enroll lead in sequence |
| POST | `/api/sequences/:id/unenroll` | Unenroll lead |
| GET | `/api/sequences/enrollments/active` | Get active enrollments |
| GET | `/api/sequences/enrollments/lead/:leadId` | Get enrollments for lead |
| POST | `/api/sequences/enrollments/:id/pause` | Pause enrollment |
| POST | `/api/sequences/enrollments/:id/resume` | Resume enrollment |
| POST | `/api/sequences/enrollments/:id/skip` | Skip step |
| POST | `/api/sequences/enrollments/:id/send-now` | Send next step now |
| POST | `/api/sequences/lead/:leadId/opt-out` | Opt out lead |
| GET | `/api/sequences/lead/:leadId/opt-out` | Check opt-out status |
| POST | `/api/sequences/:id/bulk-enroll` | Bulk enroll leads |

### users.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/users/` | List users |
| GET | `/api/users/me` | Get current user |
| POST | `/api/users/sync` | Sync user profile |
| GET | `/api/users/check/:email` | Check if email exists |
| GET | `/api/users/map` | Get user ID/name map |

### dealIntelligence.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/deals/intelligence/objections` | Get objection analytics |
| GET | `/api/deals/intelligence/responses/:objectionType` | Get responses by type |
| GET | `/api/deals/intelligence/velocity` | Deal velocity metrics |
| GET | `/api/deals/intelligence/stall/:leadId` | Stall analysis for lead |
| GET | `/api/deals/intelligence/at-risk` | At-risk deals |
| GET | `/api/deals/outcomes` | Get deal outcomes |
| POST | `/api/deals/outcomes` | Record deal outcome |
| GET | `/api/deals/outcomes/reasons` | Get outcome reasons |
| GET | `/api/deals/outcomes/reason-options` | Get reason option list |
| POST | `/api/deals/stages` | Record stage transition |

### callAnalytics.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/call-analytics/analyze` | Analyze call quality |
| GET | `/api/call-analytics/:communicationId` | Get analysis for call |
| GET | `/api/call-analytics/lead/:leadId` | Get call analytics for lead |
| GET | `/api/call-analytics/trends` | Call analytics trends |
| GET | `/api/call-analytics/leaderboard` | Call analytics leaderboard |
| GET | `/api/call-analytics/coaching/alerts` | Coaching alerts |
| POST | `/api/call-analytics/coaching/alerts/:id/review` | Review coaching alert |

### callScoring.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/call-scoring/score` | Score a call |
| GET | `/api/call-scoring/:communicationId` | Get score for call |
| GET | `/api/call-scoring/user/:userId` | Get scores for user |
| GET | `/api/call-scoring/user/:userId/stats` | Get user scoring stats |
| GET | `/api/call-scoring/best` | Get best scored calls |
| GET | `/api/call-scoring/needs-coaching` | Get calls needing coaching |
| GET | `/api/call-scoring/leaderboard` | Call scoring leaderboard |

### team.js (requireApiKeyOrAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/team/performance` | Team performance metrics |
| GET | `/api/team/members` | List team members |
| GET | `/api/team/:userId/stats` | Get member stats |
| GET | `/api/team/roles` | List roles |
| GET | `/api/team/roles/:id` | Get single role |
| POST | `/api/team/roles` | Create role |
| PUT | `/api/team/roles/:id` | Update role |
| DELETE | `/api/team/roles/:id` | Delete role |
| PUT | `/api/team/members/:userId/role` | Assign role to member |
| GET | `/api/team/permissions` | Get user permissions |
| GET | `/api/team/permissions/check` | Check specific permission |

### notion.js (requireApiKeyOrAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/notion/snapshot` | Get business state snapshot |
| GET | `/api/notion/weekly-summary` | Get weekly summary |
| GET | `/api/notion/knowledge-export` | Export knowledge base |
| POST | `/api/notion/sync-business-state` | Sync business state to Notion |
| POST | `/api/notion/sync-weekly-metrics` | Sync weekly metrics to Notion |
| GET | `/api/notion/status` | Notion integration status |

### objections.js (requireApiKeyOrAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/objections/` | List objections |
| POST | `/api/objections/` | Create objection |
| PUT | `/api/objections/:id` | Update objection |
| DELETE | `/api/objections/:id` | Delete objection |

### tags.js (requireAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/tags/trades` | List trade tags |
| POST | `/api/tags/trades` | Create trade tag |
| PUT | `/api/tags/trades/:id` | Update trade tag |
| DELETE | `/api/tags/trades/:id` | Delete trade tag |
| GET | `/api/tags/trades/:id/leads` | Get leads for trade tag |
| POST | `/api/tags/leads/:leadId/tags` | Add tags to lead |
| DELETE | `/api/tags/leads/:leadId/tags/:tagId` | Remove tag from lead |
| GET | `/api/tags/leads/:leadId/tags` | Get tags for lead |

### competitors.js (requireAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/competitors/` | List competitors |
| GET | `/api/competitors/:id` | Get single competitor |
| POST | `/api/competitors/` | Create competitor |
| PUT | `/api/competitors/:id` | Update competitor |
| DELETE | `/api/competitors/:id` | Delete competitor |
| GET | `/api/competitors/:id/mentions` | Get competitor mentions |
| POST | `/api/competitors/mentions` | Log competitor mention |
| GET | `/api/competitors/stats/overview` | Competitor stats overview |

### import.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/import/leads` | Import leads (has own rate limiting) |
| GET | `/api/import/:id` | Get import status |
| GET | `/api/import/:id/errors` | Get import errors |
| GET | `/api/import/` | List imports |

### export.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/export/leads` | Export leads |
| POST | `/api/export/communications` | Export communications |
| POST | `/api/export/analytics` | Export analytics |
| GET | `/api/export/:id` | Get export file |
| GET | `/api/export/` | List exports |

### outboundWebhooks.js (requireAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/webhooks/events` | List available events |
| GET | `/api/webhooks/endpoints` | List webhook endpoints |
| POST | `/api/webhooks/endpoints` | Create webhook endpoint |
| PUT | `/api/webhooks/endpoints/:id` | Update webhook endpoint |
| DELETE | `/api/webhooks/endpoints/:id` | Delete webhook endpoint |
| POST | `/api/webhooks/endpoints/:id/test` | Test webhook delivery |
| GET | `/api/webhooks/endpoints/:id/deliveries` | Get delivery history |
| POST | `/api/webhooks/deliveries/:id/retry` | Retry failed delivery |

### enrichment.js (requireAuth + writeLimiter)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/enrich/usage` | Get enrichment usage |
| GET | `/api/enrich/config` | Get enrichment config |
| PUT | `/api/enrich/config` | Update enrichment config |
| GET | `/api/enrich/:leadId` | Get enrichment for lead |
| POST | `/api/enrich/:leadId` | Enrich a lead |
| POST | `/api/enrich/bulk` | Bulk enrich leads |
| GET | `/api/enrich/sources` | Get enrichment sources |

### bugReport.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/bug-report/` | Submit a bug report |

### sync.js (requireAuth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/sync/platform/status` | Platform sync status |
| GET | `/api/sync/platform/stats` | Platform sync stats |
| POST | `/api/sync/platform` | Trigger platform sync |
| POST | `/api/sync/platform/recent` | Sync recent activity |
| GET | `/api/sync/platform/debug` | Debug sync info |

### notifications.js (DISABLED -- file exists but not mounted)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/vapid-public-key` | Get VAPID public key (public) |
| POST | `/subscribe` | Subscribe to push (requireAuth) |
| DELETE | `/unsubscribe` | Unsubscribe (requireAuth) |
| GET | `/preferences` | Get prefs (requireAuth) |
| PUT | `/preferences` | Update prefs (requireAuth) |
| POST | `/test` | Test notification (requireAuth) |
| GET | `/status` | Get status (requireAuth) |

### WebSocket Endpoints (index.js)
| Protocol | Path | Purpose |
|----------|------|---------|
| WSS | `/api/transcribe/ws` | Deepgram transcription proxy |
| WSS | `/api/twilio-media` | Twilio media stream |

---

## 2. SECURITY ISSUES

### CRITICAL

**SEC-01: VAPI Webhook Signature Validation Is Effectively Disabled**
- **File:** `/home/ccuser/rateright-growth/src/middleware/webhookValidation.js` lines 25-35, 59-69
- When `VAPI_PUBLIC_KEY` is not set, webhooks are ALLOWED unless `NODE_ENV=production` AND `REQUIRE_VAPI_SIGNATURE=true`. Both env vars must be set. If either is missing, any attacker can POST fake VAPI webhook payloads to `/api/vapi/webhook` and inject arbitrary tasks into `vapi_tasks` table, which are then auto-executed via `executeTask()`.
- The TODO on line 12 states: *"TODO: Verify actual VAPI webhook signature format from official documentation"* -- the validation uses three guessed HMAC patterns, none of which may match VAPI's actual format.

**SEC-02: `default-user` Fallback Allows Unauthenticated Actions**
- **Files:** `achievements.js:247,301`, `aiAlerts.js:27,76,119,143,166,189`, `analytics.js:15`, `battleground.js:122,134,161`, `calls.js:244`, `coaching.js:59,115,205,277`, `dashboard.js:1668`, `xp.js:53,278`
- Pattern: `const userId = req.user?.id || 'default-user'`
- Even though these routes are behind `requireAuth` middleware, if `req.user` is somehow null/undefined (middleware bug, race condition), all operations proceed as `default-user`. This is a defense-in-depth failure -- XP, achievements, and alerts could be awarded/created under a fake identity. The XP award endpoint at `xp.js:120` correctly checks `if (!userId)` and returns 401, but other routes do not.

**SEC-03: Intelligence & Frontend Tracking Routes Have No Authentication**
- **Files:** `intelligence.js`, `frontendTracking.js`
- **Mount:** `app.use('/api/intelligence', trackingLimiter, intelligenceRoutes)` and `app.use('/api/frontend', trackingLimiter, frontendTrackingRoutes)`
- These routes use `supabaseAdmin` (bypasses RLS) to write directly to `usage_events`, `web_vitals`, and `frontend_errors` tables. An attacker can flood these tables with garbage data, pollute analytics, and exhaust database storage. Only rate limiting provides protection.
- The `/api/intelligence/suggestions`, `/api/intelligence/friction`, `/api/intelligence/dashboard` read endpoints also expose internal business intelligence to anyone.

**SEC-04: Webhook Test-Lookup Endpoint Exposes Lead Data Without Auth**
- **File:** `webhooks.js:456-513` (`GET /api/webhooks/twilio/test-lookup`)
- Returns `lead.id`, `first_name`, `last_name`, `phone`, `company` for any phone number provided via `?phone=` query parameter. Mounted under webhookLimiter only, no authentication. Anyone can enumerate lead data by querying phone numbers.

### HIGH

**SEC-05: Error Messages Leak Internal Details**
- **Affected files:** Nearly ALL route files (40+ files)
- Pattern: `res.status(500).json({ error: error.message })` or `res.status(500).json({ error: err.message })`
- Supabase, Twilio, OpenAI, and Node.js error messages are returned directly to clients. These can reveal database table names, column names, API configuration details, and internal architecture. Highest counts: `activation.js` (20), `voice.js` (17), `battleground.js` (14), `battlegroundV2.js` (14), `intelligence.js` (12).

**SEC-06: VAPI Webhook Returns Error Details to External Caller**
- **File:** `vapi.js:36` -- `res.status(200).json({ received: true, error: err.message })`
- On error, the webhook returns the internal error message to VAPI's servers. Error details should never be sent to external services.

**SEC-07: Vapi `/status` and `/tasks` Endpoints Bypass Validation Middleware**
- **File:** `webhookValidation.js:238` -- VAPI middleware only validates `/webhook` path
- The `/api/vapi/status`, `/api/vapi/tasks`, and `/api/vapi/tasks/:id/retry` endpoints are on the same router mounted with `webhookLimiter` but NO auth middleware. Anyone with the URL can view all VAPI tasks, see configuration status (which env vars are set), and retry tasks.

**SEC-08: Outbound Webhooks Route Mounting Conflict**
- In `index.js:123`, `webhookRoutes` (inbound Twilio/website) is mounted at `/api/webhooks` with `webhookLimiter` only (no auth).
- In `index.js:273`, `outboundWebhooksRoutes` is mounted at the SAME path `/api/webhooks` but with `requireAuth + writeLimiter`.
- Express merges these, but the first mount (no auth) takes priority for matching. The GET `/api/webhooks/events` and GET `/api/webhooks/endpoints` from `outboundWebhooks.js` could potentially be accessible without auth depending on Express route resolution order.

**SEC-09: WebSocket Endpoints Have No Authentication**
- **File:** `index.js:304-317`
- Both `/api/transcribe/ws` (Deepgram proxy) and `/api/twilio-media` accept WebSocket upgrades with no authentication check. Any client can connect and use the Deepgram transcription proxy (consuming API credits) or the Twilio media stream endpoint.

### MEDIUM

**SEC-10: `parseInt()` Used on Unsanitized Query Parameters Without Bounds**
- Across many files, `parseInt(limit)`, `parseInt(days)`, `parseInt(hours)` are used directly from `req.query` without upper bound capping. Users can request `?limit=999999` to retrieve excessive data or `?days=99999` for expensive date range queries.
- Examples: `analytics.js:19,35`, `aiAlerts.js:45`, `activation.js:54`, `callAnalytics.js:108,127`, `intelligence.js:155`, `frontendTracking.js:99`.

**SEC-11: Environment Variable Configuration Exposure**
- `health.js:21` -- Reveals whether OpenAI is configured.
- `voice.js:898-902` -- Reveals which Twilio credentials are set (AccountSid, ApiKey, ApiSecret, TwimlApp, PhoneNumber).
- `vapi.js:131-135` -- Reveals which VAPI credentials are set.
- `ai.js:32-33` -- Reveals OpenAI and Deepgram configuration status.
- While these don't expose actual keys, they provide reconnaissance data.

**SEC-12: `voicemail.js:278` -- Missing User ID**
- `user_id: null, // TODO: Get from auth` -- Voicemail drops are created without tracking which user performed the action. This is behind auth but breaks audit trails.

---

## 3. DEAD CODE

**DEAD-01: `notifications.js` Route File Is Not Mounted**
- The entire file (`/home/ccuser/rateright-growth/src/routes/notifications.js`, 232 lines, 7 endpoints) is never loaded. In `index.js:63`: `// const notificationsRoutes = require('./routes/notifications'); // Temporarily disabled`. The functionality is partially replicated inline in `index.js:126-212` with simpler implementations.

**DEAD-02: `voice.js.patch` File**
- `/home/ccuser/rateright-growth/src/routes/voice.js.patch` (154 lines) is a documentation/patch file describing changes that have already been applied to `voice.js` and `calls.js`. It should be removed from the routes directory.

**DEAD-03: Commented-Out Push Notification Code in `webhooks.js`**
- Lines 414-420: Commented-out push notification for high-intent SMS responses.
```javascript
// Push notification temporarily disabled
// try {
//   const { notifyHighIntentSms } = require('../services/pushNotifications');
//   await notifyHighIntentSms(lead, Body, buyingSignal);
// } catch (pushError) {
//   console.error('[Webhook] Push notification error:', pushError.message);
// }
```

**DEAD-04: Removed Debug Endpoint Comment in `voice.js`**
- Line 906: `// Debug endpoint removed for security - exposed Twilio credentials` -- This comment is leftover artifact with no associated code.

**DEAD-05: Duplicate Analytics/Vitals/Error Routes**
- `analytics.js` has `/api/analytics/vitals`, `/api/analytics/error`, `/api/analytics/vitals/summary` (lines 515, 555, 598).
- `frontendTracking.js` has `/api/frontend/vitals`, `/api/frontend/error`, `/api/frontend/vitals/summary` (lines 17, 56, 96).
- These are functionally duplicate, likely different tables but same purpose. The analytics versions require auth while frontendTracking versions do not.

---

## 4. ERROR HANDLING ISSUES

**ERR-01: Widespread Internal Error Message Leakage**
- As noted in SEC-05, nearly every route file returns `error.message` or `err.message` in 500 responses. This affects approximately 150+ catch blocks across all files.
- Recommended pattern: `res.status(500).json({ error: 'Internal server error' })` with `console.error()` for logging.

**ERR-02: Inconsistent Error Response Formats**
- Some routes return `{ error: 'message' }` (e.g., `calls.js`, `leads.js`)
- Some return `{ success: false, error: 'message' }` (e.g., `achievements.js`, `analytics.js`, `battleground.js`)
- Some return plain text: `res.status(500).send('Internal server error')` (e.g., `webhooks.js`)
- No standard error response format across the codebase.

**ERR-03: Silent Failures in Tracking Routes**
- `intelligence.js:68-69` and `intelligence.js:80-81`: On database errors, returns `{ success: true, tracked: false }` without logging enough context. The caller thinks the request succeeded.
- `frontendTracking.js:38-41`: Returns `{ success: true, note: 'Table not created yet' }` when the database table doesn't exist -- masking a deployment/migration issue.

**ERR-04: VAPI Webhook Always Returns 200 Even On Error**
- `vapi.js:35-36`: `res.status(200).json({ received: true, error: err.message })` -- Returns 200 on error to "prevent retries." While this is a common webhook pattern, it means failures are invisible to the calling service. The error message exposure is unnecessary.

**ERR-05: Missing Database Connection Checks**
- Many routes check `if (!supabase)` at the start (e.g., `calls.js:20`), but many others proceed directly without checking, relying on try/catch for null reference errors. This creates inconsistent behavior -- some routes return 503, others return 500 with cryptic errors.

**ERR-06: `setImmediate()` with `async` Swallows Errors**
- `calls.js:288,308` -- `setImmediate(async () => { ... })` -- If the async function rejects and the internal `.catch()` fails, the error is completely lost. The pattern is used for fire-and-forget scoring and dossier updates.

---

## 5. TWILIO/VAPI WEBHOOK VALIDATION ISSUES

**TWV-01: VAPI Signature Validation Uses Guessed Patterns**
- **File:** `webhookValidation.js:11-13` -- TODO comment admits the implementation is guesswork: *"TODO: Verify actual VAPI webhook signature format from official documentation"*
- Three HMAC patterns are tried (timestamp.body, body only, sha256= prefix), but none may match VAPI's actual implementation. This means validation will always fail if VAPI does send signatures, and the code falls through to the "allow without validation" path.

**TWV-02: Twilio Webhook Validation Only Covers Listed Paths**
- **File:** `webhookValidation.js:187-196` -- Only these paths are validated: `/twiml`, `/inbound`, `/inbound-status`, `/voicemail`, `/voicemail-complete`, `/call-status`, `/recording-status`, `/transcribe-recording`, `/backfill-transcripts`.
- Any new webhook endpoint added to `voice.js` will NOT be validated unless manually added to this list. This is a maintenance hazard.

**TWV-03: Webhook URL Construction May Be Wrong**
- `webhooks.js:35`: `const webhookUrl = \`\${process.env.APP_URL || 'https://rateright-growth-production.up.railway.app'}/api/webhooks/twilio/inbound\``
- Twilio validates signatures against the exact URL it was configured with. If `APP_URL` doesn't match the URL configured in Twilio console (e.g., trailing slash difference, http vs https), validation will always fail silently. This is also duplicated in `webhooks.js:523` for the status endpoint.

**TWV-04: `rawBody` Fallback May Cause Validation Failures**
- `webhookValidation.js:171`: `const rawBody = req.rawBody || JSON.stringify(req.body)`
- If `rawBodyMiddleware` doesn't fire (middleware ordering issue), `JSON.stringify(req.body)` is used as fallback. The re-serialized JSON may differ from the original raw body (property ordering, whitespace), causing HMAC validation to fail even for legitimate webhooks.

**TWV-05: GET Requests Skip All Validation**
- `webhookValidation.js:182-184` and `242-244` -- Both Twilio and VAPI middleware skip validation for GET requests. The voice webhook router exposes `GET /twiml` which returns webhook configuration info.

---

## 6. AUTH MIDDLEWARE COVERAGE

### Routes WITHOUT Authentication (Public)
| Mount Path | File | Auth Level | Notes |
|-----------|------|------------|-------|
| `/health` | health.js | None | Intentionally public |
| `/api/health` | health.js | None | Alias |
| `/api/webhooks` (inbound) | webhooks.js | webhookLimiter only | Twilio signature validated inline |
| `/api/voice` (webhooks) | voice.js (voiceWebhooks) | webhookLimiter + Twilio signature | Twilio callbacks |
| `/api/vapi` | vapi.js | webhookLimiter + VAPI signature | VAPI callbacks (signature broken) |
| `/api/intelligence` | intelligence.js | trackingLimiter only | Usage tracking -- NO AUTH |
| `/api/frontend` | frontendTracking.js | trackingLimiter only | Web vitals -- NO AUTH |
| `/api/notifications/vapid-public-key` | index.js inline | None | Public key endpoint |

### Routes WITH Authentication
| Mount Path | File | Auth Type | Rate Limiter |
|-----------|------|-----------|-------------|
| `/api/leads` | leads.js | requireApiKeyOrAuth | writeLimiter |
| `/api/sms` | sms.js | requireApiKeyOrAuth | per-endpoint |
| `/api/calls` | calls.js | requireApiKeyOrAuth | writeLimiter |
| `/api/call-list` | callList.js | requireApiKeyOrAuth | None |
| `/api/ai` | ai.js | requireApiKeyOrAuth | aiLimiter |
| `/api/dashboard` | dashboard.js | requireApiKeyOrAuth | dashboardLimiter |
| `/api/sequences` | sequences.js | requireApiKeyOrAuth | writeLimiter |
| `/api/team` | team.js | requireApiKeyOrAuth | None |
| `/api/notion` | notion.js | requireApiKeyOrAuth | None |
| `/api/objections` | objections.js | requireApiKeyOrAuth | writeLimiter |
| `/api/jobs` | jobs.js | requireAuth | None |
| `/api/voice` (protected) | voice.js (router) | requireAuth | None |
| `/api/bug-report` | bugReport.js | requireAuth | None |
| `/api/sync` | sync.js | requireAuth | None |
| `/api/activation` | activation.js | requireAuth | None |
| `/api/scripts` | scripts.js | requireAuth | None |
| `/api/achievements` | achievements.js | requireAuth | None |
| `/api/xp` | xp.js | requireAuth | None |
| `/api/coaching` | coaching.js | requireAuth | None |
| `/api/analytics` | analytics.js | requireAuth | None |
| `/api/ai-alerts` | aiAlerts.js | requireAuth | None |
| `/api/prompts` | prompts.js | requireAuth | smsLimiter |
| `/api/battleground` | battleground.js + battlegroundV2.js | requireAuth | None |
| `/api/dossier` | dossier.js | requireAuth | None |
| `/api/manager` | manager.js | requireAuth | None |
| `/api/playbook` | playbook.js | requireAuth | smsLimiter |
| `/api/voicemail` | voicemail.js | requireAuth | writeLimiter |
| `/api/dev` | dev.js | requireAuth | None |
| `/api/users` | users.js | requireAuth | None |
| `/api/deals` | dealIntelligence.js | requireAuth | None |
| `/api/call-analytics` | callAnalytics.js | requireAuth | None |
| `/api/call-scoring` | callScoring.js | requireAuth | None |
| `/api/tags` | tags.js | requireAuth | writeLimiter |
| `/api/competitors` | competitors.js | requireAuth | writeLimiter |
| `/api/import` | import.js | requireAuth | Own limiter |
| `/api/export` | export.js | requireAuth | None |
| `/api/webhooks` (outbound) | outboundWebhooks.js | requireAuth | writeLimiter |
| `/api/enrich` | enrichment.js | requireAuth | writeLimiter |

### Internal Routes (X-Internal-Key header)
| Mount Path | File | Auth Type |
|-----------|------|-----------|
| `/api/jobs/internal` | jobs.js (internalRouter) | X-Internal-Key header |
| `/api/leads/internal` | leads.js (internalLeadsRouter) | X-Internal-Key header |

### Inline Notification Routes (index.js)
| Mount Path | Auth Type |
|-----------|-----------|
| `GET /api/notifications/vapid-public-key` | None |
| `POST /api/notifications/subscribe` | requireAuth |
| `GET /api/notifications/status` | requireAuth |
| `GET /api/notifications/preferences` | requireAuth |
| `PUT /api/notifications/preferences` | requireAuth |
| `POST /api/notifications/test` | requireAuth |

---

## Summary of Findings

| Category | Count |
|----------|-------|
| Total Endpoints | ~260 |
| Route Files | 46 + 1 patch |
| Critical Security Issues | 4 |
| High Security Issues | 5 |
| Medium Security Issues | 3 |
| Dead Code Items | 5 |
| Error Handling Issues | 6 |
| Twilio/VAPI Validation Issues | 5 |
| Routes without any auth | 5 route files (~30 endpoints) |
| Routes with `default-user` fallback | 8 route files |

**Top 3 Priorities:**
1. Fix VAPI webhook validation (SEC-01) -- attacker can inject tasks that auto-execute
2. Remove public exposure of lead data via test-lookup endpoint (SEC-04)
3. Add authentication to WebSocket endpoints (SEC-09) and VAPI non-webhook endpoints (SEC-07)