Enqueue
Validated, deduped on an idempotency key, and queued into one of four priority queues on BullMQ + Redis.
POST /v1/notifications → 202 ML-powered notification delivery with adaptive channel routing.
The router learns each recipient's delivery preferences per context (which channel works, at what hour, on which day, for what kind of content) instead of following a static preference table. Every send is scored by an XGBoost model over 19 features, including time-of-day and day-of-week patterns, engagement recency, and per-recipient delivery history.
Four stages from API call to delivered notification.
Validated, deduped on an idempotency key, and queued into one of four priority queues on BullMQ + Redis.
POST /v1/notifications → 202 Per candidate channel: time of day and day of week, engagement recency, per-recipient history, channel health, LLM content signals.
19-feature vector Every eligible channel gets an engagement probability; ε-greedy exploration occasionally routes elsewhere so the model keeps learning.
XGBoost · ε = 0.1 Attempts walk the ranked channels, skip open circuit breakers, retry with backoff. If the ML service is slow, static routing takes over.
2s timeout · DLQ What happens when a channel, a provider, or the model itself fails.
Four queues (critical, high, standard, bulk), each with its own retry policy: from 5 attempts at a 1s base delay down to 2 attempts at 30s, all exponential backoff, plus a dead-letter queue for exhausted jobs.
Hard 2-second prediction timeout; any failure falls back to static priority routing.
Per channel: a 60-second rolling window opens the breaker above a 50% failure rate (minimum 10 samples); half-open probes with escalating cooldown recover it while traffic reroutes to the next-best channel.
Row-level security enabled and forced: tenant isolation below the application layer.
SHA-256 stored, constant-time compared, revocable and expiring; raw keys shown once.
Per-tenant atomic Redis counters; X-RateLimit-* headers and proper 429 + Retry-After.
Retried requests replay the original response instead of enqueuing duplicates.
X-RateLimit-* headers on every response and proper
429 + Retry-After semantics on breach.
Adaptive routing needs labeled engagement data, which a new deployment doesn't have.
LLM-driven recipient personas (email_lover, push_fan,
disengaged) decide whether each delivered notification would be engaged with,
and a cold-start model is bootstrapped from synthetic recipient archetypes with realistic
time-of-day behavior.
On that data the full training loop runs for real: automatic retraining every 6 hours on labeled delivery attempts, an AUC-gated promotion step with an overfit guard and a minimum sample floor, and a versioned model registry with per-model metrics and feature importance. All of it is triggerable on demand from the dashboard, before any production traffic exists.
Engagement in the demo environment is simulated, stored explicitly as simulated engagement. The one genuine engagement signal today is the email open-tracking pixel.
New channels plug into a registry behind a single deliver() interface.
| Channel | Status | Detail |
|---|---|---|
| Live | Real SMTP delivery (nodemailer); Mailpit as the dev sink. | |
| WebSocket | Live | Presence-aware real-time delivery over Socket.IO via Redis pub/sub. |
| SMS | Mocked | Demo mock only. No provider integration yet. |
| Webhook | Planned | Reserved in the schema and types; delivery not implemented. |
Express / TypeScript · FastAPI / XGBoost · Postgres 16 · Redis 7 + BullMQ · React 19 · Socket.IO · Turborepo
Every claim on this page is traceable to the codebase.