backend — Core API
The backend repo (package name nodvel) is the platform's core API server. It
is the largest service and the integration hub: clients call it, and it
orchestrates the microservices and external CRMs.
- Stack: Express 4 + Mongoose/MongoDB, Socket.IO (+ Redis adapter), built on the Nodevel framework.
- Runtime: Node 22 (container), FFmpeg installed, 6 GB heap.
- Deploy: Docker → ECR → EKS, 4 API replicas + 1 scheduler pod.
Responsibilities
- Auth & RBAC — registration, sessions, JWT, passport OAuth (Google/Facebook/
Apple), teams, branches. Authorization is
user_type+ the per-userpermissions[]array — the legacy roles/permissions layer (PermissionService,RoleController,PermissionController,populate('roles')in the bearer strategy) was removed 2026-07-20. - Tenancy & billing — companies, agencies, subscriptions, packages, add-ons, invoices (Stripe + Xero).
- Visitor identity — the identity graph:
Person/PersonProfileresolution, IP signals, enrichment. - CRM sync — through the
crmport to GoHighLevel, HubSpot, Salesforce, Jobber (+ Slack). - Realtime — the main Socket.IO server for dashboards, call/chat signalling, agent presence.
- Scheduler — ~38 cron jobs (subscriptions, reporting, CRM sync, Knox metrics).
- Orchestration — hands work to ms-ai, ms-sessions, ms-communication.
Boot flow
index.js → otel.js (Sentry/OTel) → Application.run() (Nodevel sequence) →
app/hooks.js:
boot()— middleware (file upload, trust proxy, CORS, rate limiting), then loads services (Redis, Auth, Mongo, Logger, Mail, EmailQueue, CrmSync, thecrmport, provider services gated bySERVICE_*flags — Salesforce, Jobber, PlatformCrm) and the identity subsystem chain (IdentityGraphRenderer → IdentityGraphConfig → SubscriptionGate → IdentityGraph → Verifier → PersonResolver → PersonWatch), plus Knox auto-engagement and CRM enrichment. The scheduler is the last statement ofboot()— it constructs every job and starts ticking in its constructor, so anything it loads before is invisible to job constructors (see Boot order). Globals declared inhooks.jsmust matcheslint.config.mjs— the same commit keeps them in step.server()— starts Socket.IO, mounts Bull Board (/admin/queues), Sentry error handler.
Directory map
| Path | Contents |
|---|---|
app/configs/ | app/service/database/redis/AWS/Azure/CRM config |
app/controllers/ | 80+ controllers (Admin*, Agency*, AI*, Call*, Chat*, Company*, Payment*, Reporting*, …) |
app/models/ | ~70 Mongoose models (see Data Stores) |
app/routes/ | 80+ route files; api.js is the root |
app/services/ | ~45 service classes (GoHighLevelService, HubspotService, StripeService, IdentityGraphService, PersonResolverService, SocketService, SchedulerService, …) |
app/jobs/ | ~38 cron jobs |
app/lib/ | cross-service clients (msAiHandoff.js, msAiKnoxClient.js, msSessionsRecordingClient.js), ghlResilience.js, ipSignals.js |
core/ | the Nodevel framework |
eks/prod/ | K8s manifests (backend + scheduler deployments, service) |
public/ | served visitor scripts (widget.js, knock-socket.js, Twilio/ElevenLabs clients) |
Key route groups
| Area | Routes |
|---|---|
| Auth | authRoutes.js — login, signup, OAuth, refresh |
| Agency | agencyRoutes.js — multi-client mgmt, staff, packages |
| Company | CompanyRoutes.js — settings, website, FAQ, integrations |
| Chat | chatRoutes.js — messages, chatbot training |
| Calls | callRoutes.js, internalAiCallRoutes.js, aiInboundAgentRoutes.js, aiOutboundCallRoutes.js |
| Sessions | userSessionRoutes.js — visitor analytics |
| Identity | identityGraphRoutes.js, adminIdentityRoutes.js |
| CRM | goHighLevelRoutes.js, hubspotRoutes.js — webhooks + sync |
| Payments | subscriptionRoutes.js, stripeRoutes.js |
| Wallet | internalTelephonyRoutes.js (ms-ai → backend: /internal/telephony/wallet|debit|notify), GET /subscription/wallet-transactions |
| Playground | playgroundRoutes.js — public no-login sandbox + agency/admin management |
| Reporting | reportRoutes.js, statsRoutes.js, statsV2Routes.js |
| Storage | adminStorageRoutes.js — super-admin storage stats + recording cleanup proxy |
| Platform API | platformApiRoutes.js — EzyMigrate integration (three trust zones). See Platform API |
| Lead API | on userSessionRoutes.js behind leadApiAuth: /api/v1/lead/visitors, /api/v1/visitors/count (now key-required — breaking), /api/v1/lead/:user_session_id/transcript |
| SMS agents | aiSmsAgentRoutes.js — /ai-sms-agents CRUD/clone/rotate-secret/messages + public POST /ai-sms/webhook (per-agent secret) |
| Certification | certificationRoutes.js (/certification/*), adminCertificationRoutes.js (/admin/certification/*). See Certification |
| Nox activity | noxActivityRoutes.js — /nox/activity + /export (CSV) + /report |
| Push test | adminPushTestRoutes.js — super-admin push tester (/admin/push-test/*) |
| Engage | POST /user-session/:_id/engage + /engage-status — on-demand live-visitor auto-trigger |
| CRM port | schedulingRoutes.js (/scheduling/*), salesforceRoutes.js, jobberRoutes.js; migrationRoutes.js gains backfill-crm-links + crm-smoke. See CRM Integration |
| Nox config | noxConfigRoutes.js — catalog/read/apply/revert for settings-by-chat, permission-mirrored. See Nox Assistant |
| Nox tasks | NoxActionUserController — create/edit/approve/cancel every ScheduledNoxAction kind; knoxBriefUserRoutes.js GET /knox/brief (mobile) |
| Webhooks | app/configs/webhookEvents.js catalogue + WebhookQueueService (BullMQ retries) — custom webhooks and Zapier share one dispatch. See Webhooks |
| Partners | PartnerApplicationController (POST /certification/apply, PARTNER_API_KEY), app/lib/partnerInvites.js, partnerApplicants.js, certificationSettings.js |
Recent models worth knowing: SmsAgentMessageModel (per-send SMS-agent log;
status: 'sent' only on a Twilio message SID), ScreenedCallModel
(read-only here — ms-communication owns the collection and all writes;
index definitions must stay byte-identical across both services or Mongo
throws IndexOptionsConflict at boot), ScheduledNoxActionModel (now
kind: 'ai_call' | 'sms'), and user.push_preferences (see
Settings — Notifications).
Scheduler & cron
Cron runs on a dedicated scheduler pod (SERVICE_CRON=true) so jobs fire
exactly once. SchedulerService registers node-cron jobs wrapped by
CronMonitor (Redis-backed history at /admin/cron). Representative jobs:
| Cadence | Jobs (examples) |
|---|---|
| 1 min | ActiveSessionJob, UserOnlineStatusJob, OutboundCallGhlSyncJob, PostCallWebhookFallbackJob, BalanceAutoRechargeJob (wallet top-up), PlatformCallCompletedWatcherJob (platform-webhook call-completed events), GhlCallNotePostJob |
| 2 min | SimproInvoiceChaseJob, TapiPollJob |
| 5 min | FactExtractionTriggerJob, AiCallGhlTranscriptSyncJob, MsAiHandoffSweeperJob, GhlCallRecordingAttachJob, ScheduledNoxActionJob (fires due scheduled calls/SMS, atomic claim) |
| 10 min | DailyBriefEmailJob, SubscriptionExpireJob, SubscriptionSuspensionJob, FunnelSweepJob |
| 15 min | RecordingBackfillSweeperJob (rescue sweep — backfills failed ms-sessions handoffs) |
| Hourly | AgentKUsageEmitJob (provider usage → ms-ai cost-tracking), AgentKDailyPushJob (daily GHL push), AgentKSequenceJob (Mode B sends), AgentKLinkedInRenewalJob (idempotent per period), FunnelDailyStatsJob |
| Daily | KnoxBaselineMetricsJob (02:00), KnoxGhlSyncJob (03:00), FunnelDormancyJob (04:00), NoxFunnelReconcileJob (04:30), FunnelBackfillJob (05:00) |
Cron runs in the schedular deployment, not the API pods — it has its own
configmap (Apps/PROD/schedular/configmap.yaml); provider API keys must exist in
both configmaps or cron feeders silently break.
Retired this window: ReportingEmailJob is unscheduled (replaced by the Nox
daily brief) and GhlTipsEmailJob is stopped (both commented out in
SchedulerService).
External integrations
GoHighLevel (GoHighLevelService.js, ~61 KB — the biggest integration), HubSpot,
Slack, Twilio, ElevenLabs, Firebase (FCM), AWS (S3/CloudFront/ACM), Azure CDN,
Stripe, Xero, Simpro, Tapi (IMAP work-order emails), Snitcher/Apollo/Clearbit
(enrichment; HappierLeads removed 2026-09-09), Salesforce, Jobber, Calendly, Dualhook (WhatsApp), Rb2b. Each lives in
app/services/<Name>Service.js with config under app/configs/. See
CRM Integration for the sync mechanics.
Realtime
SocketService creates the Socket.IO server with a Redis adapter (in-memory
fallback for single-pod dev). Session→socket mappings live in Redis
(session_sockets:, 24h TTL); heartbeat writes are throttled via
heartbeat_status: (5m TTL). Events drive live dashboards, call status, typing
indicators, and agent availability.
Config & deployment
- Key env:
MS_AI_URL/MS_SESSION_URL/MS_COMMUNICATION_URL,MS_AI_SECRET_TOKEN,MS_COMM_API_KEY(backend → ms-communication),CRON_ENABLED/SERVICE_CRON,FILE_STORAGE,DB_*, plus per-integration credentials. Observability viaBUGSINK_DSN(Sentry) and Winston. - EKS:
backend-deployment.yaml(4 replicas, 8–12 Gi mem), separateschedular-deployment.yaml(1 replica, cron leader),backend-service.yaml(ClusterIP). Config viabackend-cmConfigMap. Namespacekk-ns-prod. - CI:
buildspec.ymlbuilds the image, pushes to ECR, andkubectl rollout restarts the deployments. See CI/CD.