Technical reference documentation for platform architecture, algorithms, and API endpoints.
Technology Stack
Backend
Express.js 4.x + Node.js
Database
PostgreSQL (Neon Serverless)
Hosting
Render (Auto-deploy from GitHub)
Storage
Cloudflare R2 (S3-compatible)
Auth
JWT + bcrypt password hashing
Scheduling
node-cron (in-process)
System Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ Client (Browser) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ Dashboard │ │ Forecasting │ │ Scheduling │ │ Settings │ │
│ │ HTML/JS │ │ HTML/JS │ │ HTML/JS │ │ HTML/JS │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └─────┬──────┘ │
└─────────┼────────────────┼────────────────┼───────────────┼─────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ Express.js API Server │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Routes: /api/auth, /api/forecast, /api/schedule, /api/settings ││
│ └─────────────────────────────────────────────────────────────────┘│
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────────────┐│
│ │ preprocess.js │ │ forecasting.js │ │ lib/wfm-core.js ││
│ │ Data Pipeline │ │ ML Models │ │ Erlang C/A Formulas ││
│ └────────────────┘ └────────────────┘ └────────────────────────┘│
└──────────────────────────────┬──────────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Neon PostgreSQL │ │ Cloudflare R2 │ │ Aircall API │
│ • call_data │ │ • Logo uploads │ │ • OAuth 2.0 │
│ • forecasts │ │ • Video uploads │ │ • Call import │
│ • business_units │ │ • Attachments │ │ │
│ • employees │ │ │ │ │
│ • schedules │ │ │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Preprocessing Algorithms
IQR Outlier Detection
// Quartile calculation
Q1 = sorted[n × 0.25]
Q3 = sorted[n × 0.75]
IQR = Q3 - Q1
// Outlier bounds (default threshold = 1.5)
Lower Bound = Q1 - (threshold × IQR)
Upper Bound = Q3 + (threshold × IQR)
// Severity classification
Moderate: value outside bounds
Severe: value outside bounds by > 1 IQR
Z-Score Detection (Alternative)
Z = (value - mean) / stdDev
Outlier if |Z| > threshold (default: 3.0)
Severe if |Z| > threshold × 1.5
Additional Detection Rules
- 10x Spike: value > mean × 10 → severe outlier
- Near-Zero: value < 5 when mean > 50 → possible outage
- DST Awareness: Outliers during DST transitions downgraded to moderate
Forecasting Models
| Model |
Use Case |
Data Requirements |
Croston's |
Intermittent/sparse demand (2-3 calls/day) |
Sporadic, low volume |
Holt-Winters |
Small stable BUs, limited historical data |
14+ days |
SARIMA |
Medium volume, seasonal patterns |
4+ weeks, interpretable |
Prophet-style |
Trend + seasonality + holiday effects |
8+ weeks ideal |
Gradient Boosting |
Feature-based ML regression |
Large datasets |
Ensemble |
Volatile BUs, blends multiple models |
High variance data |
Long-Range Prophet |
12-18 month budget forecasting |
1+ year history |
Model Selection Tree (IntelligentAutoSelector)
if (avgDailyVolume < 5) → Croston's
else if (dataPoints < 14) → Holt-Winters
else if (coefficientOfVariation > 0.6) → Ensemble
else if (hasStrongSeasonality) → SARIMA
else → Prophet-style
Erlang Staffing Formulas
Traffic Intensity (Erlangs)
A = (callsPerInterval × AHT) / intervalDuration
// Where AHT and intervalDuration are in seconds
Erlang C (No Abandonment)
// Probability of waiting
Pc = (A^N/N!) × (N/(N-A)) / [Σ(A^k/k!) + (A^N/N!) × (N/(N-A))]
// Service Level
SL = 1 - Pc × e^(-(N-A) × targetTime / AHT)
Erlang A (With Abandonment)
// Average patience time θ = time before customer hangs up
avgWaitTime = Pc × AHT / (N - A)
probAbandonGivenWait = 1 - e^(-avgWaitTime / θ)
probAbandon = Pc × probAbandonGivenWait
Database Schema (Key Tables)
call_data
id SERIAL PK | date DATE | hour INTEGER (0-23) | call_count INTEGER
forecasts
id SERIAL PK | date DATE | hour INTEGER | predicted_calls INTEGER | lower_bound | upper_bound
business_units
id SERIAL PK | name VARCHAR | channel_type VARCHAR | country_code VARCHAR(3) | timezone VARCHAR
processed_data
id | dataset_id UUID | date | interval (0-47) | call_count | day_of_week | is_weekend | is_holiday | holiday_name | lag_1d | lag_7d | lag_14d | rolling_avg_7d | rolling_avg_30d | is_outlier | outlier_reason
employees
id SERIAL PK | bu_id FK | team_id FK | name VARCHAR | email | hire_date | fte_percentage | shift_preference | skills JSONB
API Reference
| Method |
Endpoint |
Description |
| GET |
/api/forecast |
Get forecast data for BU |
| POST |
/api/forecast/generate |
Generate new forecast |
| POST |
/api/forecast/publish |
Publish draft as plan-of-record |
| GET |
/api/schedule |
Get schedule for date range |
| POST |
/api/schedule/generate |
Generate optimized schedule |
| GET |
/api/business-units |
List all business units |
| GET |
/api/employees |
List employees with skills |
| POST |
/api/auth/login |
Authenticate, returns JWT |
| GET |
/api/settings |
Get company settings |
| GET |
/api/admin/export |
Export full database (admin) |
Trend Damping & Mean Reversion
Raw ML models can overreact to recent changes. Trend damping smooths predictions to reduce volatility while preserving genuine shifts.
Damping Decay Formula
trend_influence = 0.85 × e^(-0.12 × months_ahead)
mean_reversion = 1 - trend_influence
Short-Term (1-13 weeks)
- Rapid growth (>15% wow): 70% trend, 30% mean
- Stable pattern: 90% trend, 10% mean
Long-Term (3-18 months)
- 0-3mo: 85% trend, 15% mean
- 6-12mo: 50% trend, 50% mean
- 12-18mo: 30% trend, 70% mean