[PHASE 4] AI Marketing Intelligence Suite
Phase
Phase 4 — AI Intelligence | Depends on: Issues 01, 02, 05, 06. Best after Issues 09, 10 for richer data.
Description
The platform already has a functional AI layer for the Data Modeler (GeminiService, RedisAISessionService, DRAAIInsightReport, background job infrastructure via SchedulerService and QueueService, and a full notification system via NotificationHelperService). This infrastructure is reusable for marketing AI features without the need to build another AI model or integration.
This issue adds five AI-powered marketing intelligence features, surfaced across three locations in the platform:
- Weekly Performance Summary — automatically-generated narrative email + in-app digest, scheduled weekly
- Anomaly Detection — cron job comparing current week's metrics to trailing 4-week average; fires notifications on significant deviations
- Budget Recommendation Engine — Gemini-powered analysis of ROAS by channel, generating reallocation suggestions
- UTM Builder & Generator — interactive tool for creating correctly formatted UTM-tagged URLs; feeds back into the Attribution Engine
- Natural Language Query — plain-English questions answered with chart-ready data or annotated SQL
These features make the platform proactive rather than reactive — instead of requiring a user to open a dashboard to notice a problem, the platform surfaces insights where and when they are needed.
What already exists (do NOT rebuild)
| Layer |
File |
Status |
| Gemini 2.0 Flash integration |
backend/src/services/GeminiService.ts |
✅ Complete |
| Redis AI session management |
backend/src/services/RedisAISessionService.ts |
✅ Complete |
| Scheduler service |
backend/src/services/SchedulerService.ts |
✅ Complete — cron pattern |
| Background job queue |
backend/src/services/QueueService.ts |
✅ Complete |
| Notification system |
backend/src/services/NotificationHelperService.ts |
✅ Complete |
| Email service |
Existing email templates from account/invite system |
✅ Pattern to follow |
UTMParameterService |
backend/src/services/UTMParameterService.ts |
✅ Core UTM logic exists |
DRAAIInsightReport model |
backend/src/models/DRAAIInsightReport.ts |
✅ Add marketing insight types |
| Marketing data access |
MarketingReportProcessor.getMarketingHubSummary() (Issue 06) |
✅ On completion of Issue 06 |
First Principles Rationale
The difference between a BI tool and an intelligence platform is whether the system tells the user something they didn't know, versus waiting for the user to query for it. Weekly summaries convert marketing data into narrative context. Anomaly detection converts threshold crossings into actionable alerts. Budget recommendations convert ROAS differentials into specific spending decisions. UTM Builder lowers the barrier to accurate tracking data entry. NL Query removes the need to know SQL. Together these five features are the reason a CMO would describe this as an "intelligence platform" rather than a "reporting tool."
Acceptance Criteria
Feature 1: Weekly Performance Summary
Feature 2: Anomaly Detection
Feature 3: Budget Recommendation Engine
Feature 4: UTM Builder & Generator
Feature 5: Natural Language Query
Technical Implementation Plan
1. New Database Table: dra_marketing_anomalies
Migration [timestamp]-CreateMarketingAnomaliesTable.ts:
CREATE TABLE dra_marketing_anomalies (
id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL REFERENCES dra_projects(id) ON DELETE CASCADE,
data_source_id INTEGER REFERENCES dra_data_sources(id) ON DELETE SET NULL,
channel_type VARCHAR(50) NOT NULL,
metric VARCHAR(50) NOT NULL, -- 'spend' | 'impressions' | 'cpl' | 'roas' | 'conversions'
detected_at TIMESTAMP DEFAULT NOW(),
current_value NUMERIC(15,4) NOT NULL,
baseline_value NUMERIC(15,4) NOT NULL, -- 4-week average
deviation_pct NUMERIC(8,2) NOT NULL,
direction VARCHAR(10) NOT NULL, -- 'spike' | 'drop'
is_resolved BOOLEAN DEFAULT FALSE,
resolved_at TIMESTAMP,
severity VARCHAR(20) NOT NULL -- 'warning' (20-40%) | 'critical' (>40%)
);
2. MarketingAIService (backend/src/services/MarketingAIService.ts)
export class MarketingAIService {
private static instance: MarketingAIService;
public static getInstance(): MarketingAIService { ... }
// Feature 1: Weekly Summary
async generateWeeklySummary(projectId: number): Promise<string>
// Calls GeminiService.sendMessage() with prior week's IMarketingHubSummary as context
// Returns Gemini's narrative text
// Feature 2: Anomaly Detection
async detectAnomalies(projectId: number): Promise<IMarketingAnomaly[]>
// Compares current week vs 4-week trailing average across all channels
// Inserts anomaly records for deviations > threshold
// Feature 3: Budget Recommendations
async generateBudgetRecommendations(
projectId: number,
campaignId?: number
): Promise<IBudgetRecommendation[]>
// Sends channel ROAS + spend data to Gemini
// Returns 3 structured recommendations with channel, action, rationale
// Feature 5: NL Query
async processNaturalLanguageQuery(
projectId: number,
userId: number,
sessionId: string,
question: string
): Promise<INLQueryResponse>
// Session managed via RedisAISessionService
// Returns type: 'prose' | 'sql_result' | 'chart_config'
}
Gemini system prompt for Budget Recommendations:
You are a marketing budget optimisation assistant.
Analyse the following channel performance data and provide exactly 3 specific budget reallocation recommendations.
Format each as: {"channel": "...", "action": "increase|decrease|maintain", "pct_change": 20, "rationale": "..."}
Only recommend changes where the data clearly supports the decision.
Current project data: [JSON of IMarketingHubSummary]
Gemini system prompt for NL Query:
You are a marketing analytics assistant with access to the following data sources for this project:
[List of connected sources with classification]
[Schema summary: table names, key columns]
The user will ask marketing performance questions. You can respond with:
1. A direct prose answer
2. A PostgreSQL query (wrap in ```sql ... ```) — I will execute it and show results
3. A chart config (JSON with type, x, y, series) — the frontend will render it
Always cite which data source you used. If the data is insufficient, say so clearly.
3. Scheduled Jobs
WeeklySummaryJob (backend/src/jobs/WeeklySummaryJob.ts):
Follows the ScheduledDeletionJob pattern:
export class WeeklySummaryJob {
public static schedule(): void {
// Every Monday at 8:00 AM UTC
cron.schedule('0 8 * * 1', async () => {
const projects = await projectRepo.find({ where: { /* has marketing data */ } });
for (const project of projects) {
await QueueService.getInstance().enqueue({
type: 'weekly_summary',
projectId: project.id
});
}
});
}
}
AnomalyDetectionJob (backend/src/jobs/AnomalyDetectionJob.ts):
// Daily at 6:00 AM UTC
cron.schedule('0 6 * * *', async () => {
const anomalies = await marketingAIService.detectAnomalies(projectId);
for (const anomaly of anomalies) {
await NotificationHelperService.getInstance().sendNotification({
userId: projectOwnerId,
type: 'marketing_anomaly',
title: `${anomaly.direction === 'drop' ? '⚠️' : '📈'} ${anomaly.channel_type} ${anomaly.metric} ${anomaly.direction}`,
message: `${Math.abs(anomaly.deviation_pct)}% ${anomaly.direction} vs 4-week average`,
link: `/marketing-projects/${projectId}/marketing`
});
}
});
Add 'marketing_anomaly' to dra_notifications_type_enum via migration.
4. Backend Routes (backend/src/routes/marketingAI.ts)
POST /marketing-ai/weekly-summary/:projectId — manual trigger (for testing)
GET /marketing-ai/anomalies/:projectId — list open anomalies
POST /marketing-ai/budget-recommendations — body: { projectId, campaignId? }
POST /marketing-ai/nl-query — body: { projectId, question, sessionId }
GET /marketing-ai/nl-query/session/:sessionId — get session history
All routes: validateJWT + aiOperationsLimiter for compute-intensive endpoints.
5. Frontend Surface Points
Project Overview (/marketing-projects/[id]):
- "Weekly Performance Digest" card showing the most recent
weekly_summary insight report
- "Active Alerts" card listing unresolved anomalies with severity badges
Campaign Detail → AI Analysis tab (/marketing-projects/[id]/campaigns/[cid]):
- "Get Budget Recommendations" button
- Recommendation side panel (3 structured suggestions)
- "Apply" button updates campaign channel budget allocation
Marketing Hub (/marketing-projects/[id]/marketing):
- "Ask your data..." NL Query chat panel (collapsible, right side of page)
- Chat history shown in the panel for the current session
Data Connectivity (/marketing-projects/[id]/data-sources):
- UTM Builder tool accessible from Data Connectivity sidebar sub-item
6. UTM Builder Page (frontend/pages/marketing-projects/[projectid]/data-sources/utm-builder.vue)
<template>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Left: Input form -->
<div>
<h2>UTM Builder</h2>
<form>
<input v-model="destinationUrl" placeholder="https://yoursite.com/landing-page" />
<input v-model="source" placeholder="Campaign Source (e.g. google)" />
<input v-model="medium" placeholder="Campaign Medium (e.g. cpc)" />
<input v-model="name" placeholder="Campaign Name (e.g. spring-2026)" />
<input v-model="term" placeholder="Campaign Term (optional)" />
<input v-model="content" placeholder="Campaign Content (optional)" />
</form>
</div>
<!-- Right: Live preview + saved presets -->
<div>
<div class="bg-gray-50 p-4 rounded font-mono text-sm break-all">
{{ generatedUrl }}
</div>
<button @click="copyUrl">
<font-awesome-icon :icon="['fas', 'copy']" /> Copy URL
</button>
<button @click="savePreset">
<font-awesome-icon :icon="['fas', 'bookmark']" /> Save as Preset
</button>
<div v-if="savedPresets.length">
<h3>Saved Presets</h3>
<button v-for="preset in savedPresets" @click="applyPreset(preset)">
{{ preset.name }}
</button>
</div>
</div>
</div>
</template>
New Notification Type
Add 'marketing_anomaly' to dra_notifications_type_enum enum via a new migration following the AddNewNotificationTypes pattern.
Email Preferences
Add marketing_summaries boolean column to dra_email_preferences:
ALTER TABLE dra_email_preferences ADD COLUMN marketing_summaries BOOLEAN DEFAULT TRUE;
Files to Create
| File |
Purpose |
backend/src/services/MarketingAIService.ts |
Core AI service |
backend/src/jobs/WeeklySummaryJob.ts |
Weekly cron job |
backend/src/jobs/AnomalyDetectionJob.ts |
Daily anomaly detection cron |
backend/src/routes/marketingAI.ts |
Marketing AI routes |
backend/src/migrations/[timestamp]-CreateMarketingAnomaliesTable.ts |
DB migration |
backend/src/migrations/[timestamp]-AddMarketingAnomalyNotificationType.ts |
Extend notifications enum |
backend/src/migrations/[timestamp]-AddMarketingSummariesEmailPreference.ts |
Email preferences column |
frontend/pages/marketing-projects/[projectid]/data-sources/utm-builder.vue |
UTM Builder page |
frontend/components/budget-recommendations-panel.vue |
Recommendations side panel |
frontend/components/nl-query-chat.vue |
Natural language query chat |
frontend/components/anomaly-alerts-card.vue |
Summary of open anomalies |
frontend/components/weekly-digest-card.vue |
Weekly summary digest card |
Files to Modify
| File |
Change |
backend/src/server.ts |
Register marketingAI routes; start scheduled jobs |
backend/src/datasources/PostgresDSMigrations.ts |
Add anomalies table entity |
frontend/pages/marketing-projects/[projectid]/index.vue |
Add Weekly Digest + Anomaly Alerts cards |
frontend/pages/marketing-projects/[projectid]/marketing/index.vue |
Add NL Query chat panel |
frontend/pages/marketing-projects/[projectid]/campaigns/[campaignid]/index.vue |
Fill AI Analysis tab with Budget Recommendations |
frontend/components/marketing-project-sidebar.vue |
Add red anomaly badge to Marketing Hub sidebar item |
Dependencies
- Requires: Issues 01, 02 (Campaign entity for budget recommendations), 06 (MarketingReportProcessor — data source for all AI features)
- Enhanced by: Issues 09-11 (richer data = better AI summaries and anomaly detection)
GeminiService, RedisAISessionService, SchedulerService, QueueService, NotificationHelperService are all available and require no changes
Testing Requirements
[PHASE 4] AI Marketing Intelligence Suite
Phase
Phase 4 — AI Intelligence | Depends on: Issues 01, 02, 05, 06. Best after Issues 09, 10 for richer data.
Description
The platform already has a functional AI layer for the Data Modeler (
GeminiService,RedisAISessionService,DRAAIInsightReport, background job infrastructure viaSchedulerServiceandQueueService, and a full notification system viaNotificationHelperService). This infrastructure is reusable for marketing AI features without the need to build another AI model or integration.This issue adds five AI-powered marketing intelligence features, surfaced across three locations in the platform:
These features make the platform proactive rather than reactive — instead of requiring a user to open a dashboard to notice a problem, the platform surfaces insights where and when they are needed.
What already exists (do NOT rebuild)
backend/src/services/GeminiService.tsbackend/src/services/RedisAISessionService.tsbackend/src/services/SchedulerService.tsbackend/src/services/QueueService.tsbackend/src/services/NotificationHelperService.tsUTMParameterServicebackend/src/services/UTMParameterService.tsDRAAIInsightReportmodelbackend/src/models/DRAAIInsightReport.tsMarketingReportProcessor.getMarketingHubSummary()(Issue 06)First Principles Rationale
The difference between a BI tool and an intelligence platform is whether the system tells the user something they didn't know, versus waiting for the user to query for it. Weekly summaries convert marketing data into narrative context. Anomaly detection converts threshold crossings into actionable alerts. Budget recommendations convert ROAS differentials into specific spending decisions. UTM Builder lowers the barrier to accurate tracking data entry. NL Query removes the need to know SQL. Together these five features are the reason a CMO would describe this as an "intelligence platform" rather than a "reporting tool."
Acceptance Criteria
Feature 1: Weekly Performance Summary
SchedulerServiceDRAAIInsightReportwithreport_type = 'weekly_summary'email_preferences.marketing_summaries = true(new preference column)/marketing-projects/[id])Feature 2: Anomaly Detection
dra_marketing_anomaliestableanomaly_alert_cardwidget on dashboards (Issue 08)Feature 3: Budget Recommendation Engine
dra_campaign_channelsbudget allocation (not the total campaign budget — it redistributes the existing budget)aiOperationsLimiter(20 calls/hour)Feature 4: UTM Builder & Generator
/marketing-projects/[id]/data-sources/utm-builderimport.meta.client)localStorageas presets (max 10 saved)/campaigns/[cid]/utm-builder)Feature 5: Natural Language Query
GeminiServicewith a marketing-specific system prompt containing the current project's data schema contextRedisAISessionService) for 24 hoursdra_ai_insight_reports(new report type:natural_language_query)aiOperationsLimiterTechnical Implementation Plan
1. New Database Table:
dra_marketing_anomaliesMigration
[timestamp]-CreateMarketingAnomaliesTable.ts:2.
MarketingAIService(backend/src/services/MarketingAIService.ts)Gemini system prompt for Budget Recommendations:
Gemini system prompt for NL Query:
3. Scheduled Jobs
WeeklySummaryJob(backend/src/jobs/WeeklySummaryJob.ts):Follows the
ScheduledDeletionJobpattern:AnomalyDetectionJob(backend/src/jobs/AnomalyDetectionJob.ts):Add
'marketing_anomaly'todra_notifications_type_enumvia migration.4. Backend Routes (
backend/src/routes/marketingAI.ts)All routes:
validateJWT+aiOperationsLimiterfor compute-intensive endpoints.5. Frontend Surface Points
Project Overview (
/marketing-projects/[id]):weekly_summaryinsight reportCampaign Detail → AI Analysis tab (
/marketing-projects/[id]/campaigns/[cid]):Marketing Hub (
/marketing-projects/[id]/marketing):Data Connectivity (
/marketing-projects/[id]/data-sources):6. UTM Builder Page (
frontend/pages/marketing-projects/[projectid]/data-sources/utm-builder.vue)New Notification Type
Add
'marketing_anomaly'todra_notifications_type_enumenum via a new migration following theAddNewNotificationTypespattern.Email Preferences
Add
marketing_summariesboolean column todra_email_preferences:Files to Create
backend/src/services/MarketingAIService.tsbackend/src/jobs/WeeklySummaryJob.tsbackend/src/jobs/AnomalyDetectionJob.tsbackend/src/routes/marketingAI.tsbackend/src/migrations/[timestamp]-CreateMarketingAnomaliesTable.tsbackend/src/migrations/[timestamp]-AddMarketingAnomalyNotificationType.tsbackend/src/migrations/[timestamp]-AddMarketingSummariesEmailPreference.tsfrontend/pages/marketing-projects/[projectid]/data-sources/utm-builder.vuefrontend/components/budget-recommendations-panel.vuefrontend/components/nl-query-chat.vuefrontend/components/anomaly-alerts-card.vuefrontend/components/weekly-digest-card.vueFiles to Modify
backend/src/server.tsbackend/src/datasources/PostgresDSMigrations.tsfrontend/pages/marketing-projects/[projectid]/index.vuefrontend/pages/marketing-projects/[projectid]/marketing/index.vuefrontend/pages/marketing-projects/[projectid]/campaigns/[campaignid]/index.vuefrontend/components/marketing-project-sidebar.vueDependencies
GeminiService,RedisAISessionService,SchedulerService,QueueService,NotificationHelperServiceare all available and require no changesTesting Requirements
detectAnomalies()correctly identifies a 25% CPL increase asseverity = 'warning'and a 45% drop asseverity = 'critical'detectAnomalies()does not create duplicate anomaly records if the same deviation persists across multiple daily runs (checkis_resolved = falseexists for the same metric+channel before inserting)generateWeeklySummary()does not throw when a project has no data sources (returns "No data available for this period" message)generateBudgetRecommendations()response is rate-limited toaiOperationsLimiter(20/hour)copyUrlguarded withimport.meta.client(no SSR crash)savePresetpersists tolocalStorageand loads correctly on page revisitemail_preferences.marketing_summaries = falsenpm run validate:ssrpasses