Architecture: CTV Ad Server, SSP, and DSP
Understanding how LtvAdx's control plane, data plane, and event pipeline interact is essential for planning your integration and debugging issues at scale.
The three planes
LtvAdx is organised into three functional planes that work together to deliver ads from advertiser to CTV viewer.
Control plane (Dashboard)
The Dashboard is where publishers, advertisers, agencies, and operators configure the platform. Publishers create channels, ad breaks, floor prices, and deal terms. Advertisers create campaigns, line items, and upload creatives. Changes are staged as a configuration snapshot and published to the serving layer via the Config API. Unpublished changes do not affect live traffic.
Data plane (Ad Server + SSAI)
The Ad Server is a stateless, low-latency service that processes VAST ad requests from CTV apps and linear TV head-ends in real time. When a player calls the VAST endpoint, the ad server loads the current serving snapshot from Redis, runs the 20-filter waterfall, and returns VAST 4.2 XML in under 10ms. The SSAI service runs alongside the ad server — it stitches winning creatives into HLS/DASH manifests and fires server-side quartile beacons as segments are delivered.
Event pipeline (Redis → MongoDB)
Every ad decision, VAST impression, quartile beacon, click, and conversion event is written to a Redis-backed event pipeline. Workers consume the queue and write to MongoDB reporting collections. Events appear in dashboard reports within 2 minutes of the ad serving event.
CTV request flow walkthrough
- CTV app or SSAI proxy calls
GET https://ads.ltvadx.com/vast?channelId=...&householdId=... - Ad Server loads the serving snapshot from Redis (hot path — no MongoDB read on the critical path).
- 20-filter waterfall runs: direct line items → PMP deals → addressable inventory → programmatic guaranteed → open RTB → house ad → empty VAST.
- If RTB is enabled on the channel, a fan-out bid request is sent to all active DSP seats simultaneously with a 100ms global deadline.
- The winning creative is selected; VAST 4.2 XML is constructed and returned.
- For SSAI: the winning creative is stitched into the HLS/DASH manifest and server-side beacons fire as segments are delivered.
- For client-side VAST: the CTV player fires quartile beacons as playback progresses.
- All events are written asynchronously to the event pipeline — no reporting writes on the critical path.
Serving snapshot model
The ad server never reads from MongoDB at request time. Instead, the control plane publishes a complete serving snapshot — all eligible campaigns, line items, creatives, floor prices, deal terms, frequency cap state, and pacing budgets — to Redis. The ad server loads this snapshot exclusively.
This design delivers sub-10ms waterfall decisions and eliminates DB latency spikes from the critical path. Snapshots are versioned; rollback to any prior snapshot takes effect across all serving nodes within 30 seconds.
Serving snapshot vs dashboard config
Changes made in the dashboard (new line items, updated floor prices, paused campaigns) do not take effect until you publish the snapshot. Use Dashboard → Settings → Publish to push changes. The diff view shows exactly what will change before you confirm.
Identity graph
LtvAdx resolves device-level signals to a household-level identifier (HouseholdID) using a 7-tier resolution chain:
Resolution tier priority (highest to lowest):
T1: UID2 hash — cryptographic privacy-preserving ID
T2: Roku Advertising ID (RIDA) — device-level deterministic
T3: Android Advertising ID (IFA) — device-level deterministic
T4: Samsung TIFA — device-level deterministic
T5: PPID (Publisher Provided ID) — publisher-scoped deterministic
T6: IP + User Agent fingerprint — probabilistic, lower confidence
T7: IP subnet only — household-level, lowest confidence
Output: { householdId, qualityScore 0–100, resolvedVia, deviceCount }20-filter ad selection waterfall
The waterfall applies filters in order. The first filter that reduces the eligible set to zero triggers a fallback to the next demand tier:
Filters (applied in order): 1. Channel ID match 2. Ad break type match (PRE_ROLL / MID_ROLL / POST_ROLL) 3. Platform match (ROKU / FIRE_TV / SAMSUNG_TIZEN / etc.) 4. Content rating filter (G / PG / PG-13 / R) 5. Content model match (AVOD / FAST / BVOD / LINEAR_TV) 6. Content genre / IAB category targeting 7. Geography (country / DMA / postal code) 8. Hour parting (day-of-week × hour-of-day grid) 9. Frequency cap (per household, per line item) 10. Pacing budget check (daily + total) 11. Line item flight date range 12. Creative approval status (must be APPROVED) 13. Creative duration match (max break duration) 14. Deal type priority (DIRECT_IO > PMP > ADDRESSABLE > PG > OPEN_AUCTION) 15. Floor CPM check (bid ≥ channel floor) 16. Min VCR threshold gate (ML-predicted VCR ≥ threshold) 17. Min channel quality score gate 18. ACR segment targeting (TV-exposed household segments) 19. Competitor brand exclusion 20. IVT score gate (household fraud score below threshold)
RTB fan-out
When the direct waterfall finds no fill and RTB is enabled on the channel, the ad server sends bid requests to all active DSP seats simultaneously:
OpenRTB 2.6 fan-out: ┌─────────────────────────────────────────────┐ │ Ad Server (waterfall → no direct fill) │ │ ↓ fan-out at t=0ms │ │ DSP 1 bid endpoint ──→ response in 80ms │ │ DSP 2 bid endpoint ──→ response in 120ms │ ← too late, ignored │ DSP 3 bid endpoint ──→ response in 65ms │ │ ↓ │ │ Deadline: 100ms from fan-out │ │ Winner: highest bid above floor (DSP 1 or 3)│ └─────────────────────────────────────────────┘
Multi-tenancy model
LtvAdx uses strict multi-tenancy. Every document in MongoDB is scoped by networkId. Publishers, advertisers, agencies, and operators operate within their own isolated data context. A network admin can see aggregate data and configure shared deal terms; sub-accounts see only their own data. RBAC is enforced at the API layer via JWT claims.
API versioning
All LtvAdx APIs use path-based versioning (/v1/). Breaking changes are introduced in a new version with at least 6 months of parallel support for the prior version. Non-breaking additions (new optional fields, new endpoints) are added without a version bump.