Campaign Manager - Product Guide
Complete product overview - what Campaign Manager is, how campaigns are built, templates, AI features, microsites, and the Customer App
1. Overview
Campaign Manager (Velocity Engine) is a platform for building, managing, and executing marketing campaigns at scale. It combines a structured campaign pipeline - where each campaign is broken into phases, steps, and computation variables - with powerful AI/LLM content generation across multiple providers (OpenAI, Anthropic Claude, Google Gemini, Perplexity, AWS Bedrock).
Users design campaign workflows, provide inputs, and the system computes outputs using templates, scripts, and AI prompts, ultimately producing ready-to-publish assets that can be deployed to company microsites.
Two Applications, Two Audiences
| Application | Audience | Experience |
|---|---|---|
| Admin Console | Campaign designers, admins, operators | Full management: variable grid, storage, templates, security, AI model config, audit trails |
| Customer App | Marketing professionals, end users | Guided 5-step wizard: fill in forms, the system generates campaign content via AI |
Architecture Overview
graph TB
subgraph Users
Admin["Admin Console
(full management)"]
Customer["Customer App
(5-step wizard)"]
end
subgraph Backend["Campaign Manager Backend"]
API["REST API
55+ controllers, 200+ endpoints"]
Compute["Computation Engine
DAG executor, multi-threaded"]
WS["WebSocket
real-time progress"]
Script["Scripting Engine
JavaScript functions"]
end
subgraph AI["AI / LLM Providers"]
OpenAI["OpenAI (GPT-4, DALL-E)"]
Claude["Anthropic Claude"]
Gemini["Google Gemini"]
Perplexity["Perplexity"]
Bedrock["AWS Bedrock"]
end
subgraph Infra["Infrastructure"]
DB[(PostgreSQL)]
S3["S3 File Storage"]
CF["CloudFront CDN"]
R53["Route53 DNS"]
Agents["External AI Agents
(A2A proxy)"]
end
Admin -->|REST + WebSocket| API
Customer -->|REST + WebSocket| API
API --> Compute
Compute --> AI
Compute --> Script
API --> WS
API --> DB
API --> S3
S3 --> CF
CF --> R53
API -->|/a2a proxy| Agents
2. How a Campaign Is Built
A campaign in Campaign Manager is not a flat document - it is a structured computation pipeline. Understanding the hierarchy is key to understanding why the system works the way it does.
What Is a Campaign?
A campaign is a project container that holds everything needed to produce marketing deliverables: strategy inputs, AI-generated content, formatted documents, and publishable assets. The system structures this work into a hierarchy so that different pieces can be worked on independently, computed selectively, and exported or imported at any level.
Why Phases Exist
Think of phases as chapters in a book. Each phase groups related work into a logical stage of the campaign process. For example, a campaign might have three phases: "Research", "Content Creation", and "Review". Phases let you:
- Organize work into meaningful stages that reflect the real-world campaign process
- Compute an entire phase at once without touching other phases
- Export or import a single phase independently
- Assign different teams to different phases
Why Steps Exist
Think of steps as pages in a chapter. Each step is an individual unit of work within a phase. A step holds the actual data (variables) and can produce a document. Steps allow you to:
- Break work into manageable pieces that can be computed independently
- Attach a document template to a step so it renders a formatted deliverable
- Clone, move, or reorder steps within a phase
- Control which steps are visible to different users
Why Variables (CC Items) Exist
Each step has variables (called CC Items - Computation Context Items) that hold values. Some variables are entered manually by users, some are computed by AI prompts, and some are calculated by JavaScript functions. Variables can depend on each other, forming a computation graph. This is what makes the system powerful: you define inputs and the system produces outputs by walking the dependency chain automatically.
Variable types include:
- String / JSON / XML - static values entered by users or other systems
- Prompt - text generated by an AI model from a prompt template
- JSON Prompt - structured JSON output from an AI model with schema validation
- Image Prompt - images generated by AI (DALL-E)
- Session Prompt - multi-turn AI conversations with memory
- Function - a JavaScript function that computes a value from other variables
- Asset - a deployable file rendered from a document template
The Campaign Hierarchy
flowchart TD
Campaign["Campaign
'Summer Product Launch'"]
Campaign --> P1["Phase 1
'Research'"]
Campaign --> P2["Phase 2
'Content Creation'"]
Campaign --> P3["Phase 3
'Review & Publish'"]
P1 --> S1["Step: Market Analysis"]
P1 --> S2["Step: Competitor Review"]
P2 --> S3["Step: Blog Posts"]
P2 --> S4["Step: Social Media"]
P2 --> S5["Step: Landing Page"]
P3 --> S6["Step: Final Review"]
P3 --> S7["Step: Asset Export"]
S3 --> V1["Var: audience_profile
(AI prompt)"]
S3 --> V2["Var: blog_headline
(AI prompt, depends on V1)"]
S3 --> V3["Var: blog_body
(AI prompt, depends on V1+V2)"]
S3 --> V4["Var: word_count
(function)"]
S3 --> V5["Var: blog_pdf
(asset - renders document)"]
style Campaign fill:#EEEEFD,stroke:#623CEA
style P1 fill:#F4FBFF,stroke:#22B5FF
style P2 fill:#F4FBFF,stroke:#22B5FF
style P3 fill:#F4FBFF,stroke:#22B5FF
The full hierarchy is: Campaign -> Phase -> Step -> Variable -> (produces) Asset
Why This Hierarchy Matters
| Benefit | How the hierarchy enables it |
|---|---|
| Parallel work | Variables without dependencies on each other run simultaneously across up to 30 threads per campaign |
| Selective recomputation | Recompute a single variable, a step, a phase, or the entire campaign - your choice |
| Export / Import at any level | Export the entire campaign as ZIP, or export just one phase or one step independently |
| Independent editing | Override any variable's AI output with a manual value without affecting other variables |
| Template reuse | The same structure can be published as a template and cloned into new campaigns |
| Dependency tracking | The computation graph knows exactly which variables depend on which, enabling smart downstream reset |
Computation Graph
When you trigger a computation, the system builds a dependency graph (DAG) of all variables, determines the optimal execution order via topological sort, and runs independent variables in parallel. AI-powered variables each get their own thread. The result is that a campaign with 100 variables might compute in minutes rather than hours, because many variables run simultaneously.
Campaign Lifecycle: 5 Phases
Every campaign goes through the same five phases, whether in the Admin Console or Customer App:
flowchart LR
Create["1. CREATE
Clone from template
or start from scratch"] --> Configure["2. CONFIGURE
Set env vars, phases,
steps, variables"]
Configure --> Compute["3. COMPUTE
Execute the DAG,
AI generates content"]
Compute --> Iterate["4. ITERATE
Review, override,
re-compute"]
Iterate --> Compute
Iterate --> Publish["5. PUBLISH
Deploy to microsite,
export assets"]
Configure --> Iterate
| Phase | What happens | Key concept |
|---|---|---|
| 1. Create | Start a new campaign (empty, from template clone, or via import). The system creates the Campaign record and optionally deep-clones all phases, steps, and variables from a published template. | Starting from a template saves time - the structure is pre-built |
| 2. Configure | Set up the campaign structure: add phases and steps, define variables (what inputs and AI prompts to use), configure environment variables (prompt templates, functions, shared config), assign document templates to steps, select the default AI model. | This is design time - no AI calls yet |
| 3. Compute | The computation engine takes over: builds a dependency graph, resolves environment variables, then executes every variable in the correct order. AI prompts call LLM providers, functions run JavaScript, assets render document templates. Progress streams in real-time via WebSocket. | This is where AI content is actually generated |
| 4. Iterate | Review the computed results. Override any AI output with a manual value. Edit upstream variables and selectively recompute downstream dependents. Use the AI Chat interface to refine specific values conversationally. Patch AI results with human edits. | You don't accept the first AI output - you refine it |
| 5. Publish | Deploy generated assets to the company microsite (S3 + CloudFront). Export the campaign as ZIP or push to Git. Assets go through an approval chain (Review -> Approve Content -> Approve Publication -> Publish). | The final deliverables go live |
Iterate is where most time is spent
The Compute-Iterate loop is the core workflow. Users typically compute, review results, override some values, recompute affected downstream variables, and repeat until the output quality is satisfactory. The system is designed to make this loop fast - you can recompute a single variable in seconds, not the whole campaign.
3. Templates & Blueprints
Campaign Types
Every campaign in the system has one of three types:
| Type | Description | Who sees it |
|---|---|---|
| Campaign | A working campaign created from a template or from scratch | Users with campaign access in the company |
| Draft Template | A campaign being prepared as a reusable template (not yet available to others) | Only admins/creators |
| Published Template | A finalized template that others can use to start new campaigns | All users who can create campaigns |
The Template Concept: Design Once, Start Many
The core idea behind templates is simple: a campaign designer builds a campaign structure once (phases, steps, variables, prompts, functions, document templates), publishes it as a template, and then any number of users can start new campaigns from it. Each new campaign gets its own independent copy of everything.
stateDiagram-v2
[*] --> DraftTemplate : create as template
[*] --> Campaign : create or start from template
DraftTemplate --> PublishedTemplate : publish template
PublishedTemplate --> DraftTemplate : unpublish
PublishedTemplate --> Campaign : "Start Campaign" (clone + activate)
Campaign --> Campaign : compute, edit, manage
Campaign --> Archived : archive
Archived --> Campaign : unarchive
note right of PublishedTemplate
Publishing resets all env items
and computation items
end note
note right of Campaign
"Start Campaign" deep-clones
the entire template structure
end note
"Start Campaign" = Deep Clone
When a user starts a campaign from a published template, the system creates a complete deep clone: all phases, steps, variables, environment items, i18n labels, and assets are copied. The new campaign gets its own independent copy of everything. What gets copied:
- All phases and steps (structure and configuration)
- All CC Items (variables) with their types, dependencies, and settings
- All environment variables (prompt templates, functions, JS libraries)
- All i18n labels (translations)
- All assets (file references)
- The MCT reference is saved as a read-only link back to the source template
Publishing a template resets all data
When you publish a campaign as a template, all environment variables and computation items are reset to their initial state. This is intentional - templates are structures, not data. Any computed results in the template will be cleared. Do not publish if you need to preserve results.
Template changes do NOT propagate to live campaigns
Once a campaign is started from a template, it is fully independent. If you update the template later, existing campaigns are not affected. To propagate changes, you must use the Migration feature.
Template -> Campaign Relationship
flowchart TD
Template["Published Template
(MCT)"] -->|"Start Campaign
(deep clone)"| C1["Campaign A"]
Template -->|"Start Campaign"| C2["Campaign B"]
Template -->|"Start Campaign"| C3["Campaign C"]
Template -->|"Update template"| Template
Template -.->|"NO auto-update"| C1
Template -.->|"NO auto-update"| C2
Template -.->|"NO auto-update"| C3
C1 -->|"mctId link
(read-only reference)"| Template
style Template fill:#EEEEFD,stroke:#623CEA
style C1 fill:#F4FBFF,stroke:#22B5FF
style C2 fill:#F4FBFF,stroke:#22B5FF
style C3 fill:#F4FBFF,stroke:#22B5FF
- The
mctIdfield on a campaign is a read-only reference to its source template. It is for tracking lineage only. - To propagate template changes to a live campaign, you must re-clone or migrate.
- The migration feature can selectively copy data from a source campaign to a target using a field mapping file.
MCT (Market Campaign Template) - Git Integration
MCT is the system's approach to reusable campaign blueprints with version control. The workflow:
- A campaign designer builds a campaign structure (phases, steps, variables, prompts)
- They publish it as a Published Template - this is the MCT
- Users "Start" new campaigns from this MCT - each gets an independent deep clone
- The MCT can be versioned via Git integration - push/pull to a repository
- When the MCT is updated, existing campaigns are NOT affected (one-way clone)
- To propagate changes, use the Migration feature with field mapping
Git Push / Pull / Branch
flowchart LR
MCT["Published Template
(MCT in DB)"] -->|"Git Push"| Repo["Git Repository
(directory structure)"]
Repo -->|"Git Pull"| MCT
MCT -->|"Start Campaign"| Live["Live Campaign
(independent clone)"]
Repo -->|"Branch"| V2["New version branch"]
V2 -->|"Merge + Pull"| MCT
Git stores the MCT as a directory tree: /phase_name/step_name/ with JSON files for each entity. On pull, the system does smart merging: detects renames, creates new items, updates existing ones.
- Push to Git - exports campaign structure as directory tree with JSON files
- Pull from Git - imports and smart-merges: detects renamed phases by checking both name and sequence, creates new items, updates existing ones
- New Branch - creates a Git branch for the campaign template
Smart Merge on Git Pull
When pulling from Git, the system does not just overwrite. It finds phases by name first, then by sequence number. If a phase was renamed, it detects this and renames (moves) rather than creating a duplicate. Steps are matched the same way. This supports incremental template updates without data loss.
Migration: Propagating Template Updates to Live Campaigns
Since template changes do not auto-propagate, the Migration feature provides a controlled way to update existing campaigns:
- Analyze - the system compares source MCT with target campaign, matches by name, reports unmatched items
- Create mapping - a tab-delimited mapping file defines which source field maps to which target
- Choose what to copy: state, result, overrides, patches (each independently)
- Validate - preview mode shows what would change without persisting
- Execute - data transferred according to mapping
Variables tagged with the migration_carryover class keep their target value instead of being overwritten - useful for per-campaign customizations.
Template Matching via Classes
Document templates and steps use a class tagging system for matching. A document template is available for a step only if its classes overlap with the step's classes (or it falls back to default). This allows different steps to automatically use different document templates based on their tags.
Document Template Rendering
Document templates are the mechanism that turns raw computed data into formatted deliverables (HTML pages, PDF documents). The system supports three rendering engines:
| Engine | Input | Output | Best for |
|---|---|---|---|
| XSLT (XSL) | XML data + XSLT stylesheet | HTML or PDF (via Apache FOP) | Complex layouts, data-driven documents, PDF generation |
| FreeMarker (FTL) | Variable map + FreeMarker template | HTML | Dynamic content with conditionals, loops, macros |
| Static (STATIC) | Key-value pairs + HTML with ${key} placeholders | HTML | Simple documents with minimal dynamic content |
How rendering works
flowchart TD
Trigger["Rendering triggered
(computation or preview)"] --> Load["Load template from DB"]
Load --> Context["Build rendering context:
computed values, i18n, campaign metadata"]
Context --> Engine{"Which engine?"}
Engine -->|XSL| XSLT["XSLT transformation"]
Engine -->|FTL| FM["FreeMarker processing"]
Engine -->|STATIC| Static["Placeholder replacement"]
XSLT --> Media{"Output format?"}
Media -->|HTML| HTML["HTML output"]
Media -->|PDF| FOP["Apache FOP -> PDF"]
FM --> HTML
Static --> HTML
HTML --> Store["Store as asset"]
FOP --> Store
Two rendering paths
The system has two fundamentally different ways to build the rendering context:
| Path | When used | Data source | Trade-off |
|---|---|---|---|
| Graph-based | During computation (Asset CC Item executing) | In-memory graph nodes (live computation data) | Always current, but only available during computation |
| Database-based | Previews and standalone rendering (REST API) | Database queries for CC Items | May be slightly stale if computation is in progress |
Preview and actual rendering can show different results
If a computation is in progress, the graph-based path has the latest values (in memory) while the database-based path (used for previews) reads from DB. Some values may not be persisted yet. The preview will show stale data until the computation completes and values are written to the database.
HTML import and Figma conversion
Designers can upload HTML designs (including Figma exports) as ZIP packages. The system automatically:
- Extracts HTML and assets (images, CSS, fonts) from the ZIP
- Rewrites resource paths to cloud storage URIs
- Converts the HTML to an XSLT stylesheet (making it data-driven)
- Extracts referenced variable keys for template matching
This means designers can work in their preferred tools and the system converts their output into reusable, data-driven templates.
PDF generation: two approaches
FOP-based PDF - XSLT produces XSL-FO markup, processed by Apache FOP. Supports complex page layouts, embedded fonts, tables. Best for structured reports.
HTML-to-PDF - an external service converts the HTML output to PDF. Preserves CSS styling. Best for visually rich designs. Custom page sizes supported (A4 default, configurable in mm or inches).
4. Customer App - The 5-Step Wizard
The Customer App provides a guided experience for creating campaigns through 5 sequential steps:
flowchart TD
Home["Home Page
(campaign cards)"] --> New["Step 1: Fundamentals"]
New --> S2["Step 2: Target Audience"]
S2 --> S3["Step 3: Content Strategy"]
S3 --> S4["Step 4: Content Execution"]
S4 --> S5["Step 5: Content Assets"]
S5 --> Activate["Activate Campaign
(triggers heavy AI computation)"]
Activate --> WS["Real-time WebSocket updates
(progress bar)"]
WS --> Generated["View Campaign
(Generated state)"]
Generated --> Edit45["Edit Steps 4-5
(field handlers for incremental AI updates)"]
Generated --> Assets["View External/Internal Assets"]
Generated --> Docs["View Campaign Foundation"]
The 5-Step Flow
| Step | What the user provides | Key validations | Computation? |
|---|---|---|---|
| 1. Fundamentals | Campaign name, GTM Foundation (template), desired outcomes, timeframe, funnel stage | Name required + unique. GTM Foundation required. | No |
| 2. Audience | Target personas (multi-select), segment, additional info | At least 1 persona. Segment required. | No |
| 3. Strategy | Campaign objective, point of view, content themes, messaging pillars | All fields required. | No |
| 4. Execution | Strategy description, content formats (multi-select), distribution channels, stage questions | At least 1 format + channel. | No |
| 5. Assets | External content assets table (add/edit manually) | At least 1 asset required before activation. | Yes! Heavy AI computation triggered by "Activate" |
What happens at "Activate Campaign"
This triggers a full campaign computation. The system builds a dependency graph of all variables, calls AI models to generate content, and produces campaign assets. Progress is streamed in real-time via WebSocket - the user sees a progress bar showing how many items are done vs. total. The state changes from "5/5" to "Generated" when complete.
Field Handlers (Incremental AI Updates)
After a campaign is generated (state = "Generated"), users can edit fields in Steps 4-5. When they change a field and click "Apply", a field handler fires:
- The updated field value is sent to the backend
- The backend identifies which downstream variables are affected
- Only those affected variables are recomputed (not the whole campaign)
- The step data auto-refreshes when computation completes
Only one field can be edited at a time
While a field handler is running, other fields are locked. The system prevents editing multiple fields simultaneously to avoid race conditions. Wait for the current field handler to complete before editing another field.
AI Assist
The Customer App includes an embedded AI chat feature called "AI Assist". When available on a step, users can open a chat panel, converse with the AI about their campaign content, and then push approved AI suggestions directly into campaign variables. AI Assist requires a chat-enabled AI model and step-level aiAssistParams configuration.
Dynamic Form System
The Customer App does NOT have hardcoded forms. Every form field comes from the backend as a schema:
- Backend defines each step's fields: type, validation, dependencies, handler
- Frontend's FormEngine renders them dynamically
- Each field can have an
applyHandlerthat triggers backend computation when the user clicks "Apply" - Field dependencies are declared: if field A changes, dependent field B is cleared and repopulated
This means the entire Customer App workflow can be changed by modifying backend templates without touching frontend code.
All supported field types (click to expand)
| Type | Renders as |
|---|---|
| input | Text/number input with placeholder |
| textarea | Multiline text area |
| select | Single dropdown |
| multiSelect | Multi-select with Apply/Cancel buttons |
| singleSelectEdit | Single select with inline edit |
| singleSelectCalendar | Date picker dropdown |
| checkbox | Boolean toggle |
| checkboxGroup | Multiple checkboxes (funnel stages) |
| richText | WYSIWYG editor with formatting |
| json / jsonWithSelect | JSON editor with validation |
| table | Asset table with add/edit/delete |
| textBlock / html | Display-only content |
| divider | Visual separator |
| fieldGroup | Container with row/grid layout |
Application State Machine
applicationState transitions:
"1 / 5" -> "2 / 5" -> "3 / 5" -> "4 / 5" -> "5 / 5" -> "Generated"
| | | | | |
(Step 1) (Step 2) (Step 3) (Step 4) (Step 5 + (Read-only.
Next btn Next btn Next btn Next btn Activate) Steps 4-5 editable
via field handlers)
After "Generated", steps 1-3 become read-only. Steps 4-5 remain editable with field handlers that trigger incremental recomputation (not full campaign recomputation).
External vs Internal Assets
| Aspect | External Assets | Internal Assets |
|---|---|---|
| What they are | Content pieces for external distribution (blogs, whitepapers, social posts) | Internal campaign documents (briefs, strategies) |
| Created by | User adds manually + AI generates during activation | System generates from step templates |
| Editable? | Yes (add, edit, delete, regenerate) | No (read-only, system-generated) |
| Storage | JSON field in CC item | Campaign steps + document templates |
| Grouped by | Asset type / phase | Step / phase |
| View | Table with columns: Type, Title, Persona, Core Idea | DocView embed (rendered document) |
GTM Foundation vs Campaign Foundation
| Aspect | GTM Foundation | Campaign Foundation |
|---|---|---|
| Scope | Organization-level go-to-market strategy | Campaign-specific discovery and planning |
| Identified by | Phase class = gtm-foundation | Phases listed in campaign foundation data |
| Content | Market strategy templates and frameworks | Campaign briefs, audience analysis, positioning |
| Relationship to campaign | Provides the template structure | Provides the context for content generation |
| When used | Selected in Step 1 of wizard | Referenced during AI content generation |
5. AI / LLM Features
Full Technical Detail
5.1 Supported Providers
| Provider | Models | Capabilities | Notes |
|---|---|---|---|
| OpenAI | GPT-4, GPT-4o, GPT-3.5, DALL-E 3 | Text, images, JSON schema, realtime WebSocket conversations | Primary provider. Native JSON schema. Retries on 502 with 5s delay. Realtime API: gpt-4o-realtime-preview. |
| Anthropic | Claude 3 Opus/Sonnet/Haiku, Claude 3.5 | Text, JSON schema (Claude 3+) | Auto-corrects model names (underscores -> dashes). |
| Google Gemini | Gemini Pro, Ultra | Text, JSON schema | Generative AI API. Generation config support. |
| Google Vertex AI | Various | Text | Requires GCP project ID + service account. |
| Perplexity | Various | Search-augmented text | Best for research-oriented prompts. |
| AWS Bedrock | Various (Claude, Llama, etc.) | Text | AWS-hosted with standard retry mode. |
5.2 Model Configuration
| Parameter | What it controls | Typical values |
|---|---|---|
| Model Name | Specific model identifier | gpt-4, claude-3-sonnet-20240229 |
| Max Tokens | Maximum output length | 1024 - 4096 |
| Temperature | Randomness (0 = deterministic, 2 = very creative) | 0.0 - 2.0 |
| Top P | Nucleus sampling diversity | 0.0 - 1.0 |
| RPM | Rate limit (requests/minute) | 10 - 1000 |
| Read Timeout | How long to wait for response | 30 - 120 seconds |
| Retries on Error | Retry count on failure | 1 - 5 (default: 3) |
| System Default | Use when no model specified | Boolean |
| Use in Chat | Available in chat interface | Boolean |
Priority chain: Variable-specific model -> Campaign default model -> System default model.
5.3 Prompt Types
Text Prompts
A prompt template stored as an environment variable with placeholders:
Write a marketing headline for {product_name} targeting {audience}.
The tone should be {tone}. Maximum {max_words} words.
Placeholders are replaced with values from other variables. The completed prompt is sent to the AI model.
JSON Structured Output
Like text prompts but the AI must return valid JSON matching a defined schema. The system validates using JSON Schema draft-07. If invalid, retries with error feedback included.
Schema-Aware vs. Generic Providers
OpenAI and Google natively enforce JSON schemas in their API. For Anthropic, Perplexity, and Bedrock, the schema is embedded in the prompt text as instructions, and validation happens after the response.
Image Generation
DALL-E and other image models. Style options configurable. The generated image URL becomes the variable's value.
Session Prompts (Multi-turn Conversation)
Creates a persistent session with conversation history. Each message includes full history - token usage grows with each turn. Supports custom "bot instructions" (system prompt) per session.
5.4 Chat Interface
sequenceDiagram
participant User
participant Chat as Chat Interface
participant System as Campaign Manager
participant AI as AI Provider
User->>Chat: Opens chat for a step
Chat->>System: Load history and settings
User->>Chat: Types message
Chat->>System: Send message (async)
System->>System: Build context (prompt + schema + history)
System->>AI: Send formatted prompt
AI-->>System: AI response
alt JSON response invalid
System->>AI: Retry with error feedback
AI-->>System: Corrected response
end
System-->>Chat: Display response
User->>Chat: Likes a suggested value
Chat->>System: Save as Override (APPLIED)
System->>System: Override CC Item value
Two modes: Document Chat (auto-populated with template keys) and Step Keys Chat (manual key selection).
Chat overrides are the bridge between conversation and campaign data: you chat with AI, refine output, then push approved values into campaign variables (status: Ready -> Applied or Canceled).
5.5 Rate Limiting & Key Pool
- Token bucket rate limiter per model (RPM). Calls block-wait for tokens.
- Key rotation - multiple API keys per provider, auto-failover when a key hits quota.
- Auto-recovery - disabled keys checked every 60 seconds and re-enabled. Passivation timeout: 15s for OpenAI.
Warning: Blocking during high load
If many AI prompts run simultaneously with a low RPM, they queue up waiting for rate limit tokens. Large campaigns with many AI prompts take proportionally longer. Increase RPM or add more API keys for higher throughput.
5.6 Monitoring Metrics
| Metric | What it tracks |
|---|---|
llm.duration | AI call latency (SLOs: 2s, 5s, 10s, 20s, 30s, 60s; percentiles: p50/75/95/99) |
llm.calls | Total calls by status (success/error), model, provider |
llm.tokens.prompt/completion | Token usage distribution |
llm.request.retry | Retried requests |
chat.llm.* | Chat-specific latency and token metrics |
5.7 AI in Scripts
JavaScript functions can call AI models programmatically:
llm.request(campaignId, promptKey, arg1, arg2, ...)- single AI callllm.requestWithRetry(campaignId, promptKey, retries, arg1, ...)- with custom retryllm.request(campaignId, promptKey, jsonSchemaKey, arg1, ...)- structured JSON output
Argument count is validated against prompt template placeholders.
5.8 Analysis Agents (A2A Integration)
Campaign Manager integrates with an external Analysis Agents service - a set of specialized AI agents that analyze campaign data and provide insights. The agents run as a separate Python/FastAPI service and communicate via the A2A (Agent-to-Agent) protocol - Google's JSON-RPC 2.0 based standard.
Available Agents
| Agent | Purpose |
|---|---|
| execution_inspector | Analyzes campaign execution results - checks provenance, validates computation outputs, identifies issues |
| campaign_planner | Helps plan campaign structure - suggests phases, steps, and variable configurations based on campaign goals |
| writing_orchestrator | Orchestrates content generation - coordinates multiple writing tasks, ensures consistency across campaign materials |
How it works
- Campaign Manager proxies requests to agents via
/a2a/{agent}/rpc(JSON-RPC) and/a2a/{agent}/stream(Server-Sent Events) - Agents run in isolated AWS ECS containers and have read-only access to the CM database via VPC Peering
- Authentication uses the same cookie-based mechanism as the Asset Viewer - shared session on
.velocityengine.codomain - Users access the Agent Dev Kit UI via a button in the CM menu, routed through auth-redirect for seamless SSO
Agents can also be called from scripts
JavaScript function variables can call agents programmatically via the agentsUtils wrapper object, enabling automated agent-driven workflows within the computation graph.
6. Microsites & Publishing
Business Purpose
A microsite is a company-branded website for publishing campaign deliverables. Think of it as each company's content distribution hub:
- URL:
https://acme.velocityengine.co/(custom subdomain per company) - One company = one microsite (1:1)
- Multiple campaigns publish to the same microsite under different URL paths
- Backed by S3 (storage) + CloudFront (CDN, HTTPS) + Route53 (DNS)
URL Construction
https://{subdomain}.velocityengine.co/{siteContext}/{campaign.folder}/{phase.folder}/{step.folder}/{asset.file}
Example:
https://acme.velocityengine.co/content/c42/launch-phase/hero-content/assets/brochure.pdf
Each level (campaign, phase, step) has its own configurable folder name. Folders support placeholders like ${campaign.id}, ${phase.nameUrl}.
Multiple Campaigns, One Microsite
flowchart TD
MS["acme.velocityengine.co
(company microsite)"]
MS --> C1["/c42/
Campaign: Summer Sale"]
MS --> C2["/c99/
Campaign: Product Launch"]
MS --> C3["/c150/
Campaign: Brand Refresh"]
C1 --> P1["/c42/research/..."]
C1 --> P2["/c42/content/..."]
C2 --> P3["/c99/phase1/..."]
style MS fill:#EEEEFD,stroke:#623CEA
Path isolation ensures campaigns cannot overwrite each other's files. Each campaign has a unique folder (typically using campaign ID).
Microsite Infrastructure
sequenceDiagram
participant Admin as Admin
participant CM as Campaign Manager
participant S3pub as Public S3 Storage
participant S3ms as Microsite S3 Bucket
participant CF as CloudFront CDN
participant DNS as Route53 DNS
Note over Admin,DNS: One-time microsite setup
Admin->>CM: Configure microsite (subdomain, HTTPS)
CM->>S3ms: Create S3 bucket (subdomain.velocityengine.io)
CM->>DNS: Create DNS record
opt HTTPS enabled
CM->>CF: Create CloudFront distribution + SSL cert
CM->>DNS: Create CNAME to CloudFront
end
Note over Admin,DNS: Per-asset deployment
Admin->>CM: Deploy asset to microsite
CM->>S3pub: Read asset file from public storage
CM->>S3ms: Copy file to microsite bucket
CM-->>Admin: URL: https://subdomain.velocityengine.io/path/asset.pdf
- S3 bucket per company:
{subdomain}.velocityengine.io - Static website hosting enabled on the bucket
- Optional HTTPS via CloudFront distribution with wildcard SSL certificate (
*.velocityengine.io) - Route53 DNS records for subdomain resolution
- SFTP access - microsite users can upload files directly via SFTP (AWS Transfer Family)
SFTP Direct Access
For power users and external tools, microsite users can upload files directly via SFTP:
- Each user gets isolated credentials (username prefixed with subdomain)
- IAM roles restrict access to that company's microsite bucket only
- Password management: generate, reset, delete
- Use case: designers uploading assets from external tools, CI/CD pipelines
Asset Approval Chain
stateDiagram-v2
[*] --> Created : generated or uploaded
Created --> ReadyForReview : submit for review
ReadyForReview --> AssetApproved : reviewer approves content
ReadyForReview --> NotPublished : rejected
AssetApproved --> PublicationApproved : publisher approves
AssetApproved --> NotPublished : publication denied
PublicationApproved --> Published : deployed to microsite
Published --> NotPublished : unpublished
Created --> Error : generation failed
note right of Published
File copied from campaign storage
to microsite S3 bucket.
Public URL generated.
end note
Auto-Publishing Stages
| Publishing Stage | When assets are deployed |
|---|---|
| No deploy | Never auto-deployed |
| On demand | Each asset deployed immediately after it is computed (one by one) |
| On execution done (all) | All assets deployed after full computation completes |
| On execution done (not generated) | Only non-generated assets deployed after computation |
CloudFront & Caching
- HTTPS microsites use CloudFront with a shared wildcard SSL certificate (
*.velocityengine.co) - Cache invalidation happens automatically when the origin path changes
- After asset deployment, changes may take a few minutes to propagate globally due to CDN caching
- Manual invalidation can be requested for urgent updates
Only public storage assets can be deployed
Assets in private storage cannot be deployed to the microsite. If an asset references a file in private storage, deployment will fail with a "Private Asset" error. Move the file to public storage first.
7. Key Concepts Glossary
| Term | What it means |
|---|---|
| Campaign | A project container holding all phases, steps, variables, and assets for a marketing initiative |
| Phase | A logical grouping of steps (e.g., "Research", "Content Creation", "Review") |
| Step | A unit of work inside a phase, containing computation variables and optionally a document template |
| CC Item | Computation Context Item - a variable that holds a value (text, AI output, function result, image, etc.) |
| Environment Variable | Campaign-level configuration: prompt templates, functions, shared data available across all steps |
| Asset | A deliverable produced by the campaign (AI-generated document, uploaded file, or external link) |
| Computation | The process of executing all variables to produce results via dependency graph |
| Computation Graph | The dependency network of all variables - determines execution order automatically |
| Document Template | XSL, FreeMarker, or static HTML template that renders step data into a formatted document |
| Company | An organization/tenant - all data isolated per company |
| Microsite | A company-specific hosted website (S3 + optional CloudFront) for publishing campaign assets |
| Application | A campaign app configuration controlling which features are available in the Customer App |
| MCT | Market Campaign Template - a reusable campaign structure managed via Git |
| Permission Group | A named set of permissions (like a role) that can be assigned to users |
| Override | A manually-entered value that replaces an AI-computed or function-computed result |
| Field Handler | A mechanism in the Customer App that triggers backend computation for a single field when it changes |