# Growth Engine Codebase Audit

**Date:** 2026-02-07
**Auditor:** CC Local (Claude Agent)
**Scope:** Full `src/` directory — routes, services, jobs, middleware, config, utils

---

## EXECUTIVE SUMMARY

The Growth Engine is a Node.js/Express CRM backend with ~45 route files, ~40 service files, ~20 job files, Supabase (Postgres) database, Twilio SMS/Voice, OpenAI, Deepgram, VAPI, Slack, and Notion integrations. It serves a React SPA admin frontend.

**Overall:** The codebase is functional and reasonably well-structured. Auth is solid (Supabase JWT + email whitelist). However, there are several security issues, dead code, and missing validation patterns.

---

## 1. CRITICAL ISSUES

### CRIT-1: VAPI Webhook Validation is Effectively Disabled (CRITICAL)
**File:** `src/middleware/webhookValidation.js:25-36`
The VAPI webhook validation returns `true` (allows) when `VAPI_PUBLIC_KEY` is not set AND `REQUIRE_VAPI_SIGNATURE` is not `'true'`. Since `REQUIRE_VAPI_SIGNATURE` is NOT in the `.env` file and `NODE_ENV` is likely not set to `'production'`, **any POST to `/api/vapi/webhook` will be accepted without signature verification**. An attacker could forge VAPI webhook payloads to trigger AI voice actions.

### CRIT-2: Hardcoded Email Whitelist in Source Code (CRITICAL)
**File:** `src/middleware/auth.js:9-25`
The `ALLOWED_EMAILS` array is hardcoded with real email addresses in source code (committed to git). This means:
- Adding/removing users requires a code deploy
- Personal email addresses are in version control
- Revoked users (like `ytingmae@gmail.com`) are still visible in comments

### CRIT-3: WebSocket Endpoints Have No Authentication (CRITICAL)
**File:** `src/index.js:304-318`
Both WebSocket endpoints (`/api/transcribe/ws` and `/api/twilio-media`) have **zero authentication**. Anyone who knows the URL can:
- Connect to the Deepgram transcription proxy and burn API credits
- Connect to the Twilio media stream endpoint

### CRIT-4: Debug Endpoint Exposes Raw Platform API Data (CRITICAL)
**File:** `src/routes/sync.js:112-179`
`GET /api/sync/platform/debug` returns raw user data, jobs, contracts, and stats from the RateRight Platform API. This is behind `requireAuth` but:
- Returns full PII (emails, names, phone numbers) from the platform
- Should be disabled in production or restricted to admin-only

### CRIT-5: Error Messages Leak Internal Details (CRITICAL)
**Multiple files** return `error.message` directly in responses:
- `src/routes/jobs.js:45` — Internal job endpoint returns raw error messages
- `src/routes/jobs.js:143` — Audit history returns `error.message`
- `src/routes/jobs.js:173` — Latest audit returns `error.message`
- `src/routes/jobs.js:229` — Rep performance returns `error.message`
- `src/index.js:156` — Notifications subscribe returns `err.message`
This can leak stack traces, database error details, and internal paths to attackers.

---

## 2. HIGH ISSUES

### HIGH-1: Intelligence & Frontend Tracking Routes Have No Auth (HIGH)
**File:** `src/index.js:258-259`
```js
app.use('/api/intelligence', trackingLimiter, intelligenceRoutes); // No auth
app.use('/api/frontend', trackingLimiter, frontendTrackingRoutes); // No auth
```
These are only rate-limited (60/min by IP), not authenticated. An attacker could:
- Flood fake usage tracking data
- Submit fake web vitals / error data
- Pollute analytics with junk

### HIGH-2: Battleground V2 Routes Mounted on Same Path as V1 (HIGH)
**File:** `src/index.js:250-251`
```js
app.use('/api/battleground', requireAuth, battlegroundRoutes);
app.use('/api/battleground', requireAuth, battlegroundV2Routes);
```
Both V1 and V2 are mounted at `/api/battleground`. If they define overlapping route paths, Express will match the first one, making V2 routes unreachable. If they don't overlap, this works but is confusing.

### HIGH-3: Internal Leads Endpoint Uses X-Internal-Key But Key Isn't Defined in .env (HIGH)
**File:** `src/routes/leads.js` (internalLeadsRouter) + `src/index.js:228`
The internal leads router is mounted at `/api/leads/internal` with `writeLimiter` only. If it uses the same `INTERNAL_JOB_KEY` pattern as `jobs.js`, the key IS in `.env`. But if the leads router has its own auth check, it may have a different bypass pattern.

### HIGH-4: No Input Validation Middleware on Most Routes (HIGH)
**File:** `src/middleware/validation.js` exists but is not imported in `src/index.js`
The validation middleware file exists but is never used in the main app. Most routes accept arbitrary JSON bodies without schema validation. This means:
- No type checking on request bodies
- No length limits on string fields (beyond the 1MB body limit)
- No sanitization of user input before database operations

### HIGH-5: Supabase Admin Client Used Without RLS (HIGH)
**File:** `src/config/database.js:17-23`
Multiple services use `supabaseAdmin` (bypasses Row Level Security) when `supabase` (anon key, respects RLS) would be more appropriate. Jobs legitimately need admin access, but services called from authenticated routes should use the anon client with the user's context.

### HIGH-6: SMS Batch Has No Recipient Limit Validation (HIGH)
**File:** `src/services/twilio.js:121-141`
`sendBatchSMS()` accepts an array of messages with no limit on array size. While there's a rate limiter (10 batch ops/hour), a single batch could contain thousands of messages. The 100ms delay between messages isn't enough protection.

---

## 3. MEDIUM ISSUES

### MED-1: `voice.js.patch` File Left in Routes Directory (MEDIUM)
**File:** `src/routes/voice.js.patch`
A patch file is committed to the repo. This is dead code/artifact and should be removed.

### MED-2: Notifications Routes Disabled But Code Remains (MEDIUM)
**File:** `src/index.js:63, 263`
```js
// const notificationsRoutes = require('./routes/notifications'); // Temporarily disabled
```
The entire notifications route file exists but is commented out. Inline notification endpoints were added directly in `src/index.js:126-212` as a replacement. Dead code.

### MED-3: Redis Cache Service May Not Be Used (MEDIUM)
**File:** `src/services/cache.js`
The cache service initializes Redis connection on module load. If Redis isn't running (common on VPS), it falls back to in-memory cache. But there's no evidence the cache middleware is applied to any routes in `src/index.js`. The `cacheService` is imported but only used for the import statement.

### MED-4: Console.log Statements Used Instead of Logger (MEDIUM)
**Multiple files** use `console.log` / `console.error` directly instead of the structured logger at `src/utils/logger.js`. This means:
- No log levels in production
- No structured JSON logging
- Debug output (like DeepgramProxy audio chunks) polluting production logs

### MED-5: Deepgram API Key Logged to Console (MEDIUM)
**File:** `src/index.js:334-335`
```js
console.log('[DeepgramProxy] Full Deepgram URL:', deepgramUrl);
console.log('[DeepgramProxy] API key present:', !!apiKey, 'length:', apiKey?.length || 0);
```
While the key value isn't directly logged, the URL may contain query params with sensitive config, and logging API key length is unnecessary information disclosure.

### MED-6: CORS Allows Railway Deployment URL (MEDIUM)
**File:** `src/index.js:99-104`
The CORS origin list includes `https://rateright-growth-production.up.railway.app` which is the deployment URL. If the app has moved to VPS, this origin may be stale and should be audited.

### MED-7: No CSRF Protection (MEDIUM)
The app uses cookie-less JWT auth (Bearer tokens), which is inherently CSRF-resistant. However, the webhook endpoints accept POST requests with no CSRF tokens — this is correct for webhooks but worth documenting.

### MED-8: Job Locking Race Condition (MEDIUM)
**File:** `src/services/jobLocking.js:57-59`
When an expired lock is found, it's deleted and then `acquireJobLock` is called recursively. Between delete and re-insert, another instance could also delete the same expired lock, leading to potential infinite recursion or race conditions.

---

## 4. LOW ISSUES

### LOW-1: `contentSecurityPolicy: false` in Helmet (LOW)
**File:** `src/index.js:94`
CSP is disabled entirely. This allows XSS attacks if the admin UI has any injection vectors.

### LOW-2: Some Rate Limiters Defined But Not Used (LOW)
**File:** `src/middleware/rateLimiter.js`
`expensiveAiLimiter`, `smsBatchLimiter`, `deleteLimiter`, `importLimiter`, `authLimiter`, `strictLimiter`, `trackingBatchLimiter` are exported but not all are used in `src/index.js`.

### LOW-3: Package Dependencies Not Audited (LOW)
No `npm audit` output available. Redis packages (`ioredis`, `cache-manager`, `cache-manager-redis-store`) are imported but may not be installed or used.

### LOW-4: No Request Size Limits on WebSocket (LOW)
**File:** `src/index.js:399-418`
The WebSocket handler forwards arbitrary-sized audio chunks to Deepgram without size validation. A malicious client could send very large payloads.

### LOW-5: challengesService.js vs challengesServiceV2.js (LOW)
Both V1 and V2 challenge services exist. V1 may be dead code if V2 has fully replaced it.

---

## 5. DATABASE TABLES REFERENCED

All queries go through Supabase (Postgres). Tables found across the codebase:

| Table | Used In |
|-------|---------|
| `leads` | routes/leads, routes/dashboard, routes/callList, jobs/platformSync, services/scoring, etc. |
| `communications` | routes/calls, routes/sms, services/callAnalytics |
| `sms_messages` | routes/sms, jobs/scheduledSms |
| `call_quality_scores` | routes/callScoring, routes/jobs, services/aiCallScoring |
| `rep_weekly_snapshots` | routes/jobs, jobs/weeklyRepReport |
| `quality_audits` | routes/jobs, jobs/dailyQualityAudit, jobs/frequentAudit |
| `ai_suggestions` | services/selfImprovement, routes/prompts |
| `prompt_versions` | services/promptManagement |
| `sms_templates` | services/templates, routes/scripts |
| `sequences` | routes/sequences, jobs/sequenceProcessor |
| `sequence_enrollments` | routes/sequences, jobs/sequenceProcessor |
| `sequence_steps` | routes/sequences |
| `callbacks` | routes/calls, jobs/callbackReminder |
| `platform_activity` | jobs/platformSync |
| `usage_events` | routes/intelligence, jobs/dailyMetricsAggregation |
| `daily_metrics` | jobs/dailyMetricsAggregation, jobs/dailySummary |
| `activation_states` | routes/activation, services/activationAI |
| `achievements` | routes/achievements, services/badgesService |
| `user_xp` | routes/xp, services/xpService |
| `xp_history` | routes/xp, services/xpService |
| `coaching_alerts` | routes/coaching, services/callAnalytics |
| `objection_rebuttals` | routes/objections |
| `deal_intelligence` | routes/dealIntelligence, services/dealIntelligence |
| `playbook_entries` | routes/playbook |
| `voicemails` | routes/voicemail |
| `leaderboard_snapshots` | services/leaderboardService |
| `web_vitals` | routes/frontendTracking |
| `frontend_errors` | routes/frontendTracking |
| `push_subscriptions` | src/index.js, services/pushNotifications |
| `notification_preferences` | src/index.js |
| `job_locks` | services/jobLocking |
| `tags` | routes/tags |
| `lead_tags` | routes/tags |
| `competitors` | routes/competitors |
| `outbound_webhooks` | routes/outboundWebhooks, services/webhookDelivery |
| `webhook_deliveries` | services/webhookDelivery |
| `enrichment_config` | services/enrichment |
| `enrichment_cache` | services/enrichment |
| `enrichment_usage` | services/enrichment |
| `friction_patterns` | jobs/frictionDetection |
| `excellence_reports` | jobs/weeklyExcellenceReport |
| `impact_measurements` | jobs/impactMeasurement |
| `duel_challenges` | services/duelsService |
| `challenges` | services/challengesService, services/challengesServiceV2 |
| `users` | routes/users, routes/team |

**SQL Injection Risk: LOW** — All queries use the Supabase JS client which parameterizes queries. No raw SQL detected.

---

## 6. AUTHENTICATION ANALYSIS

### Auth Architecture
1. **Supabase JWT** — Primary auth via `requireAuth` middleware. Validates Bearer token against Supabase, then checks email whitelist.
2. **API Key** — `requireApiKeyOrAuth` accepts either JWT OR `X-API-Key` header matching `CLAWDBOT_API_KEY` env var. Used by Clawdbot automation.
3. **Internal Key** — `X-Internal-Key` header matching `INTERNAL_JOB_KEY` for job triggers.
4. **Webhook Validation** — Twilio signature validation (works), VAPI signature validation (effectively disabled).

### Auth Coverage

| Auth Level | Routes |
|-----------|--------|
| **No Auth** | `/health`, `/api/health`, `/api/webhooks` (SMS), `/api/voice` (Twilio webhooks), `/api/vapi` (VAPI webhooks), `/api/intelligence`, `/api/frontend`, `/api/notifications/vapid-public-key`, WebSocket endpoints |
| **API Key OR Auth** | `/api/leads`, `/api/sms`, `/api/calls`, `/api/call-list`, `/api/dashboard`, `/api/ai`, `/api/sequences`, `/api/team`, `/api/notion`, `/api/objections` |
| **Auth Only** | `/api/jobs`, `/api/voice` (token), `/api/bug-report`, `/api/sync`, `/api/activation`, `/api/scripts`, `/api/achievements`, `/api/xp`, `/api/coaching`, `/api/analytics`, `/api/ai-alerts`, `/api/prompts`, `/api/battleground`, `/api/dossier`, `/api/manager`, `/api/playbook`, `/api/voicemail`, `/api/dev`, `/api/users`, `/api/deals`, `/api/call-analytics`, `/api/call-scoring`, `/api/tags`, `/api/competitors`, `/api/import`, `/api/export`, `/api/webhooks` (outbound), `/api/enrich` |
| **Internal Key** | `/api/jobs/internal/:jobName`, `/api/leads/internal` |

### Potential Bypasses
- None found in JWT validation logic. The `requireAuth` function properly validates tokens via Supabase admin client and checks email whitelist.
- API key comparison uses simple `===` (timing-safe comparison not used, but this is a low-risk issue for API keys of this nature).

---

## 7. TWILIO ANALYSIS

### SMS
- **Service:** `src/services/twilio.js` — Clean implementation with `sendSMS()`, `sendBatchSMS()`, global SMS disable flag (`DISABLE_ALL_SMS`)
- **Webhook validation:** Uses `twilio.validateRequest()` properly
- **Issue:** Batch SMS has no array size limit

### Voice
- **Token generation:** `generateVoiceToken()` creates Twilio AccessTokens properly
- **TwiML generation:** `src/services/twimlWithStream.js` generates TwiML for calls
- **Media streams:** `src/services/twilioMedia.js` handles real-time call audio via WebSocket
- **Issue:** WebSocket endpoint has no auth (CRIT-3)

### Webhook Routes
- **Inbound SMS:** `/api/webhooks` — Twilio signature validated
- **Voice webhooks:** `/api/voice/twiml`, `/api/voice/inbound`, `/api/voice/call-status`, etc. — Twilio signature validated
- **Recording/Transcription:** `/api/voice/recording-status`, `/api/voice/transcribe-recording`

---

## 8. VAPI ANALYSIS

### Webhook Handling
- **Endpoint:** `POST /api/vapi/webhook`
- **Validation:** EFFECTIVELY DISABLED (see CRIT-1)
- **Processing:** Receives call events (call started, ended, transcript, etc.) and processes them through task parser/executor

### Task System
- `src/services/taskParser.js` — Parses VAPI call transcripts for actionable tasks
- `src/services/taskExecutor.js` — Executes parsed tasks (create lead, schedule callback, send SMS, etc.)
- **Risk:** If VAPI webhook validation is bypassed, an attacker could inject fake call transcripts that trigger automated actions

---

## 9. ENVIRONMENT VARIABLES

### Defined in .env:
```
SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY
NOTION_API_KEY, NOTION_BUSINESS_STATE_DB, NOTION_TASKS_DB, NOTION_KNOWLEDGE_DB, NOTION_TEAM_DB, NOTION_METRICS_DB
DISABLE_ALL_SMS, SMS_PAUSE_REASON
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_PHONE_NUMBER, TWILIO_API_KEY_SID, TWILIO_API_KEY_SECRET, TWILIO_TWIML_APP_SID
VAPI_API_KEY, VAPI_PUBLIC_KEY, VAPI_ASSISTANT_ID, VAPI_PHONE_NUMBER_ID
OPENAI_API_KEY
APP_URL, INTERNAL_JOB_KEY
CLAWDBOT_API_KEY
APOLLO_API_KEY
```

### Referenced in code but NOT in .env:
| Variable | Used In | Status |
|----------|---------|--------|
| `PORT` | src/index.js | Defaults to 3000, OK |
| `NODE_ENV` | webhookValidation.js | Not set — may affect webhook validation behavior |
| `ALLOWED_ORIGINS` | src/index.js | Not set — falls back to hardcoded list |
| `REQUIRE_VAPI_SIGNATURE` | webhookValidation.js | NOT SET — causes VAPI validation bypass |
| `SKIP_WEBHOOK_VALIDATION` | webhookValidation.js | Dev-only, OK |
| `REDIS_URL` | services/cache.js | Not set — falls back to localhost |
| `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_DB` | services/cache.js | Not set |
| `VAPID_PUBLIC_KEY`, `VAPID_PRIVATE_KEY` | src/index.js, services/pushNotifications | Not set — push notifications disabled |
| `SLACK_WEBHOOK_URL` | services/slack.js | Not in .env (may be set in Railway or elsewhere) |
| `DEEPGRAM_API_KEY` | services/deepgram.js | Not in .env |
| `PERPLEXITY_API_KEY` | services/perplexity.js | Not in .env |
| `RATERIGHT_API_URL` | jobs/platformSync.js | Defaults to rateright.com.au/api |
| `RATERIGHT_ADMIN_API_KEY` | jobs/platformSync.js, services/platformSync.js | Not in .env |

---

## 10. DEAD CODE

| Item | Location | Type |
|------|----------|------|
| `voice.js.patch` | src/routes/ | Patch artifact, should be deleted |
| Notifications route file | src/routes/notifications.js | Commented out in index.js, replaced by inline |
| `challengesService.js` (V1) | src/services/ | May be superseded by V2 |
| `battleground.js` (V1) | src/routes/ | May be superseded by V2 |
| Unused rate limiters | src/middleware/rateLimiter.js | `expensiveAiLimiter`, `authLimiter`, `strictLimiter`, etc. exported but unused |
| `cacheService` import | src/index.js:10 | Imported but cache middleware never applied to routes |
| Revoked user comment | src/middleware/auth.js:23 | `ytingmae@gmail.com` still visible in code comments |
| `validation.js` middleware | src/middleware/validation.js | Exists but never imported/used in index.js |

---

## 11. ALL ENDPOINTS

### Public (No Auth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/health` | Health check |
| GET | `/api/health` | Health check (alias) |
| POST | `/api/webhooks/sms` | Twilio inbound SMS webhook |
| POST | `/api/webhooks/sms-status` | Twilio SMS delivery status |
| POST | `/api/voice/twiml` | Twilio TwiML generation |
| POST | `/api/voice/inbound` | Twilio inbound call |
| POST | `/api/voice/inbound-status` | Twilio inbound call status |
| POST | `/api/voice/call-status` | Twilio call status updates |
| POST | `/api/voice/voicemail` | Twilio voicemail recording |
| POST | `/api/voice/voicemail-complete` | Twilio voicemail complete |
| POST | `/api/voice/recording-status` | Twilio recording status |
| POST | `/api/voice/transcribe-recording` | Twilio recording transcription |
| POST | `/api/voice/backfill-transcripts` | Backfill call transcripts |
| POST | `/api/vapi/webhook` | VAPI event webhook |
| GET | `/api/notifications/vapid-public-key` | VAPID key for push notifications |
| POST | `/api/intelligence/track` | Usage event tracking |
| POST | `/api/intelligence/track-batch` | Batch usage tracking |
| GET | `/api/intelligence/events` | Get usage events |
| POST | `/api/frontend/web-vitals` | Web vitals reporting |
| POST | `/api/frontend/error` | Frontend error reporting |
| GET | `/api/frontend/vitals` | Get web vitals data |
| GET | `/api/frontend/errors` | Get frontend errors |
| WS | `/api/transcribe/ws` | Deepgram transcription proxy |
| WS | `/api/twilio-media` | Twilio media stream |

### Internal Key Auth
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/jobs/internal/:jobName` | Trigger job by name (X-Internal-Key) |
| Various | `/api/leads/internal/*` | Internal lead management (Clawdbot) |

### API Key OR JWT Auth
| Method | Path | Purpose |
|--------|------|---------|
| GET/POST/PUT/PATCH/DELETE | `/api/leads/*` | Lead CRUD |
| GET/POST | `/api/sms/*` | SMS sending and management |
| GET/POST | `/api/calls/*` | Call management |
| GET | `/api/call-list/*` | Prioritized call list |
| GET | `/api/dashboard/*` | Dashboard data |
| GET/POST | `/api/ai/*` | AI features (analysis, suggestions) |
| GET/POST/PUT/DELETE | `/api/sequences/*` | SMS sequence management |
| GET | `/api/team/*` | Team performance data |
| GET/POST | `/api/notion/*` | Notion sync |
| GET/POST/PUT/DELETE | `/api/objections/*` | Sales rebuttals CRUD |

### JWT Auth Only
| Method | Path | Purpose |
|--------|------|---------|
| GET/POST | `/api/jobs/*` | Job management and history |
| GET | `/api/voice/token` | Twilio Voice access token |
| POST | `/api/bug-report` | Submit bug reports |
| GET/POST | `/api/sync/*` | Platform sync |
| GET/POST | `/api/activation/*` | Activation funnel |
| GET/POST/PUT | `/api/scripts/*` | SMS scripts CRUD |
| GET | `/api/achievements/*` | Gamification achievements |
| GET/POST | `/api/xp/*` | XP system |
| GET/POST | `/api/coaching/*` | Coaching alerts |
| GET | `/api/analytics/*` | Analytics data |
| GET | `/api/ai-alerts/*` | AI-generated alerts |
| GET/POST/PUT | `/api/prompts/*` | Prompt management |
| GET/POST | `/api/battleground/*` | Gamification battleground (V1 + V2) |
| GET | `/api/dossier/:leadId` | Lead dossier (comprehensive profile) |
| GET | `/api/manager/*` | Manager analytics dashboard |
| GET/POST/PUT | `/api/playbook/*` | Sales playbook |
| GET/POST | `/api/voicemail/*` | Voicemail management |
| GET/POST | `/api/dev/*` | Developer utilities |
| GET/PUT | `/api/users/*` | User management |
| GET | `/api/deals/*` | Deal intelligence |
| POST | `/api/notifications/subscribe` | Push notification subscribe |
| GET | `/api/notifications/status` | Push notification status |
| GET/PUT | `/api/notifications/preferences` | Notification preferences |
| POST | `/api/notifications/test` | Test push notification |
| GET | `/api/call-analytics/*` | Call quality metrics |
| GET/POST | `/api/call-scoring/*` | AI call scoring |
| GET/POST/PUT/DELETE | `/api/tags/*` | Trade tag management |
| GET/POST/PUT/DELETE | `/api/competitors/*` | Competitor tracking |
| POST | `/api/import/*` | Bulk data import |
| GET | `/api/export/*` | Data export |
| GET/POST/PUT/DELETE | `/api/webhooks/*` (outbound) | Outbound webhook management |
| GET/POST | `/api/enrich/*` | Apollo lead enrichment |

---

## 12. INTEGRATION POINTS — What the $50 App Could Call

These are the endpoints/webhooks an external integration app could use to sync data with the Growth Engine:

### Best Integration Points (API Key auth via X-API-Key header):

1. **`GET /api/leads`** — Fetch all leads with filtering
2. **`POST /api/leads`** — Create new leads
3. **`PATCH /api/leads/:id`** — Update lead status/data
4. **`GET /api/dashboard/stats`** — Get dashboard statistics
5. **`GET /api/dashboard/consolidated`** — Get all dashboard data in one call
6. **`GET /api/team/performance`** — Get team performance metrics
7. **`GET /api/call-list/ranked`** — Get AI-ranked call list
8. **`POST /api/sms/send`** — Send SMS to a lead
9. **`GET /api/sequences`** — List SMS sequences
10. **`POST /api/sequences/enroll`** — Enroll lead in sequence
11. **`GET /api/notion/sync`** — Sync with Notion databases
12. **`GET /api/objections`** — Get sales rebuttals library

### Internal Endpoints (for system-level sync):
13. **`POST /api/jobs/internal/:jobName`** — Trigger any background job (X-Internal-Key)
14. **`/api/leads/internal/*`** — Direct lead management without user context

### Webhook Receivers (for real-time events):
15. **`POST /api/webhooks/sms`** — Receive SMS delivery events
16. **`POST /api/vapi/webhook`** — Receive AI voice call events

### Recommended New Integration Points:
- **Outbound Webhooks** (`/api/webhooks`) — Configure the Growth Engine to POST events (lead created, status changed, SMS received) to the $50 app's webhook URL
- **Platform Sync** (`POST /api/sync/platform`) — Trigger sync of users from RateRight platform

### Auth for $50 App:
Use `X-API-Key` header with `CLAWDBOT_API_KEY` value. This gives access to all `requireApiKeyOrAuth` routes without needing a Supabase user account.

---

## 13. RECOMMENDATIONS (Priority Order)

1. **[CRITICAL] Fix VAPI webhook validation** — Set `REQUIRE_VAPI_SIGNATURE=true` in .env and ensure `VAPI_PUBLIC_KEY` is configured, OR set `NODE_ENV=production`
2. **[CRITICAL] Add auth to WebSocket endpoints** — At minimum, require a token query param on `/api/transcribe/ws`
3. **[CRITICAL] Stop returning error.message in responses** — Use generic error messages in all catch blocks
4. **[HIGH] Move ALLOWED_EMAILS to database or env var** — Remove hardcoded emails from source code
5. **[HIGH] Add input validation middleware** — Use the existing `src/middleware/validation.js` or add Joi/Zod schemas
6. **[HIGH] Add auth to tracking endpoints** — Or at minimum add stricter rate limiting and input validation
7. **[MEDIUM] Clean up dead code** — Remove `voice.js.patch`, unused rate limiters, V1 services if V2 is active
8. **[MEDIUM] Set NODE_ENV=production** — This affects multiple security behaviors
9. **[MEDIUM] Use structured logger consistently** — Replace console.log with logger throughout
10. **[LOW] Add WebSocket message size limits** — Prevent memory exhaustion attacks

---

*Audit generated by CC Local on 2026-02-07*
