Skip to content

DOOH Feasibility: Cavai x JCDecaux

Context: Following Billups pitch, JCDecaux highlighted opportunities and capability gaps for Cavai in the DOOH space. This document assesses the technical feasibility of each.

Background: How Cavai Creatives Work Today

Cavai creatives are self-contained HTML/JS packages. The build pipeline:

  1. User configures a creative in Application-Frontend (builder UI)
  2. Creative-Composer compresses and packages the configuration + Creative-Engine into build artifacts (stub.js, creative-engine.js, creative.json)
  3. Build artifacts are stored on Application-Backend and served via CDN (delivery-3.cavai.com)
  4. A tag (script tag, VAST XML, or VPAID) is embedded on the publisher's page/player
  5. The tag loads stub.js, which bootstraps the Creative-Engine in the browser
  6. The engine renders the creative, tracks events, and sends analytics to two pipelines (Legacy Cloudflare Worker + Bunny CDN)

For DOOH delivery, creatives would typically be served via:

  • VAST/VPAID tags (already supported) -- standard for video-capable DOOH screens
  • URL endpoint -- DOOH player loads a URL in a chromium-based browser/webview
  • API feed -- DOOH CMS polls an API for creative content/assets to display

Question 1: Per-Version Serving Reports

"If we're delivering creatives via an API feed based on live data (e.g. football scores), can we report on how many times each creative version was served? For example, how many times the 'England vs Argentina' creative was displayed?"

What Exists Today

  • Per-creative reporting: Every creative has a unique ID. The analytics pipeline tracks impressions, interactions, and engagement per creative ID. This is solid and well-established.
  • Custom metrics: The engine supports sendMetric(name, value) for arbitrary tracking. String-valued metrics are stored as composite keys (name__value) in a MAP column in Parquet files. These are queryable via the Backend's Bunny pipeline.
  • Slider feed (production): A runtime feed system already exists for the Slider block. It fetches external JSON data via a remapper/v2 proxy (delivery-0.cavai.com/proxy/remapper/v2), supports filter rules (EQ/NE/GTE/LTE), custom params, sorting, and populates slide content via template interpolation (populateStringFromObject()). This proves runtime feed fetching works -- it just needs to be generalized beyond sliders.
  • No general DCO system: The slider feed is the only feed mechanism. A feasibility doc exists (todos/FeedDCO/feasibility.md) describing a broader feed-binding architecture for all block types, but nothing beyond slider is built.

Feasibility: High

This is achievable through multiple approaches, listed from simplest to most comprehensive:

Approach A: One Creative Per Variant (simplest, works today)

Create a separate creative for each match/variant ("England vs Argentina", "Brazil vs Germany"). Each gets its own creative ID and full analytics tracking.

  • Pros: Works with zero development. Full existing reporting per creative. Self-serve via the platform.
  • Cons: Manual creation of each variant. Doesn't scale to hundreds of variants. Requires rebuilding when matches change.
  • Best for: Small-scale campaigns with known, limited variants.

Approach B: Custom Metrics Per Variant (low effort)

Use a single creative with a script that fetches live data and calls sendMetric('match', 'England vs Argentina'). The entire pipeline already supports this end-to-end:

  • Engine side: Already supported -- sendMetric(name, value) with string values creates name__value entries in the custom_metrics MAP column.
  • Backend side: Already supported -- GET /v2/analytics/bunny/metrics/:creative_id discovers available metrics, custom_metric[] query params include them in hourly summaries.
  • Reporting dashboard: Already supported. The reporting dashboard (/reporting/) has full custom metric support:
    • Metric discovery: calls /v2/analytics/bunny/metrics/:id per creative, shows "tracked by N of M creatives"
    • Metric picker UI: searchable panel where users check desired custom metrics
    • Visualization: custom metrics are treated identically to standard metrics in all chart types (time series, bar comparison, KPI cards, per-resource breakdown tables)
    • URL encoding: custom metrics stored as custom:metric_name in the report URL
  • Effort estimate: Very low. The only work is writing the script that calls sendMetric() with the variant identifier when a feed-driven creative renders. Everything else -- pipeline, storage, discovery, visualization -- already works.

Approach C: Feed System with Variant Tracking (full solution)

Build the Feed/DCO system described in todos/FeedDCO/feasibility.md: external data source (API, Google Sheets) maps to block properties. Each feed row gets a variant identifier that's automatically sent with analytics events.

  • New components needed:
    • Feed Service in Application-Backend (data source connectors, caching, validation)
    • Feed-binding UI in Application-Frontend (mapping editor, preview)
    • Engine integration (property resolution at render time + variant ID in analytics)
    • Logs-Parser update to aggregate variant-level data
    • Reporting dashboard variant breakdown view
  • Effort estimate: Medium-high. This is a significant feature, but the architecture is well-understood and documented.

Recommendation

Start with Approach B (custom metrics). It requires almost no development -- the entire pipeline from engine to reporting dashboard already handles custom metrics end-to-end. The only new code is a small script in the creative that calls sendMetric('match', 'England vs Argentina') when live data is fetched. JCDecaux could see per-variant impression counts in the reporting dashboard immediately.

Approach C (full feed system) is the long-term play and should be planned as a separate project. It unlocks not just DOOH but also web-based DCO, product feeds, and A/B testing -- making it a much broader investment.


Question 2: Failed Render Detection

"Can we detect when a creative fails to render on screen (e.g. due to connectivity issues) and notify the media owner?"

What Exists Today

  • init event: Sent once when the creative loads successfully. If the engine boots, it sends init to both analytics pipelines.
  • No negative signal: There is no mechanism that fires when a creative fails to load. If the network is down or the screen can't reach delivery-3.cavai.com, nothing happens -- no request is made, no event is sent. Silence.
  • VAST error tracking: The VAST tag includes an <Error> URL that fires when the video player encounters an error (e.g. media file fails to load). This only covers VAST-based delivery, not direct URL or tag-based delivery.
  • No health endpoint: There is no lightweight health-check URL that DOOH players could ping to verify connectivity before attempting to load the full creative.

Feasibility: Medium

This is fundamentally a challenge of proving a negative -- detecting the absence of a successful load. There are several approaches:

Approach A: Expected vs Actual Impressions (no dev needed)

Compare the DOOH media owner's play logs (how many times they attempted to show the creative) against Cavai's impression count (how many init events we received). The delta represents failed renders.

  • Pros: No development. Uses existing data. Works immediately.
  • Cons: Requires data sharing from the media owner. Delayed (next-day reporting). Doesn't pinpoint individual failures in real-time.
  • Best for: Initial offering. Most DCO providers probably do something similar.

Approach B: Heartbeat/Health Endpoint (low effort)

Create a lightweight endpoint (e.g. delivery-3.cavai.com/health/:creative_id) that returns a small response (HTTP 200 + creative metadata). The DOOH player pings this before loading the creative. If it fails, the player knows to use a fallback.

  • New components: One simple endpoint on the CDN or Backend. Documentation for DOOH players on how to use it.
  • Effort estimate: Very low. A few hours of backend work.
  • Limitation: Only proves CDN reachability, not that the creative will render correctly in the player's browser/webview.

Approach C: Load Confirmation with Timeout Alert (medium effort)

  1. When the DOOH CMS queues a creative for display, it registers an "expected impression" via a Cavai API call
  2. Cavai's backend starts a timer (e.g. 30 seconds)
  3. If the creative loads, the engine sends init as usual -- the expected impression is matched
  4. If no init arrives within the timeout, an alert fires (webhook, email, or dashboard notification)
  • New components:
    • "Expected impression" API endpoint
    • Matching service (correlates expected impressions with actual init events)
    • Alert/notification system (webhook callback to DOOH CMS)
  • Effort estimate: Medium. The matching service is the complex part -- it needs near-real-time event processing.
  • Limitation: Relies on the DOOH player integrating with our API. Some latency between failure and notification.

Approach D: Client-Side Fallback Detection (medium effort)

Embed a lightweight loader script that runs before the full creative. This loader:

  1. Attempts to load stub.js
  2. If loading fails (timeout, network error, JS error), it sends a failure beacon to a dedicated error endpoint
  3. Optionally renders a static fallback image
  • New components:
    • Lightweight loader script (separate from creative-engine, very small footprint)
    • Error/failure tracking endpoint
    • Failure reporting in dashboard
  • Effort estimate: Medium. The loader script is simple, but the error reporting pipeline needs to handle a different event type.
  • Advantage: Works without DOOH CMS integration. The loader script is part of the tag/URL itself.

Recommendation

Approach A (expected vs actual) can be offered immediately with no development. Present it as a reporting feature.

Approach B (health endpoint) is quick to build and demonstrates technical capability.

Approach D (client-side loader with failure beacon) is the strongest long-term solution -- it's self-contained, doesn't require DOOH CMS integration, and provides both detection and fallback capability. Combine it with a simple alerting webhook.


Question 3: AI-Generated Backup Creative

"Some DCO providers use AI to generate a backup creative that predicts what would have been shown if the live feed failed."

What This Means in Practice

When a live data feed fails (e.g. the football scores API is down), the system needs to show something. The "AI prediction" angle is likely marketing speak for one of two things:

  1. Cached last-known-good state: The system remembers the last successfully fetched data and re-uses it. Not really AI, but reliable.
  2. Predictive content selection: Given the context (time, location, audience), AI selects the most appropriate pre-built variant from a library. Slightly more sophisticated.
  3. Generative fallback: AI generates entirely new creative content on the fly. Very advanced and likely not what's actually deployed by competitors.

What Exists Today

  • No feed system means no feed failure scenario exists yet
  • No pre-rendering or caching of creative variants
  • No AI integration in the creative pipeline

Feasibility Assessment

Level 1: Cached Fallback (low effort, high value)

When building the Feed/DCO system (Question 1, Approach C), include a caching layer:

  • Feed Service caches the last successful response per data source
  • If the live feed fails, the creative renders with cached data
  • A flag is set in analytics to mark "served from cache" vs "served from live feed"

This is standard practice and should be a baseline requirement of any feed system. Not AI, but solves the actual problem.

Effort: Built into the Feed Service design. Minimal additional work beyond the feed system itself.

Level 2: Static Fallback Creative (low effort)

Allow users to designate a "fallback creative" -- a fully static version that displays when the feed is unavailable. This could be:

  • A specific creative variant selected by the user in the builder
  • A simplified version with brand-safe default content
  • An auto-generated snapshot of the creative with placeholder content

Effort: Low. UI to select/configure a fallback + logic to serve it when the feed fails.

Level 3: Context-Aware Selection (medium effort, could leverage AI)

Given multiple pre-built variants, use contextual signals to select the most relevant one:

  • Time-based: Morning creative vs evening creative
  • Location-based: City-specific content (if DOOH player sends location data)
  • Recency-based: Most recently updated variant
  • Performance-based: Highest-engagement variant from historical data

This could use a simple rules engine or a lightweight ML model. The "AI" label would be justified here.

Effort: Medium. Needs a selection service with access to historical performance data and contextual signals.

Level 4: Generative Fallback (high effort, future)

Use an LLM/image model to generate creative content when a feed fails. For example, if the football scores feed is down, generate a generic "Follow the action live" message with the right team branding.

Effort: High. Needs generative AI integration, brand safety guardrails, approval workflows. Not recommended as an initial capability.

Recommendation

Level 1 (caching) should be built into the feed system by default -- it's not a separate feature, it's a reliability requirement.

Level 2 (static fallback) is easy to implement and gives users control.

Level 3 (context-aware selection) is the differentiator worth pursuing. Position it as "intelligent fallback" or "predictive creative selection". It doesn't require heavy AI -- a rules engine with performance data covers most use cases.

Level 4 (generative) is premature for now but worth keeping on the roadmap.


Positive Differentiators (Already Strong)

JCDecaux highlighted two things they liked. Worth reinforcing:

Self-Serve Platform

Cavai's builder is fully self-serve. DOOH media owners and their clients can create, manage, and update creatives without needing Cavai's involvement. This is a genuine differentiator -- most DCO providers require managed service.

Reporting Dashboard

The reporting dashboard (separate Vue 3 SPA at /reporting/, Bunny pipeline) already provides:

  • 17 standard metrics organized by category (Core, Video, Companion, Rates, Time)
  • Time-series charts (line, bar, comparison, heatmap)
  • Per-creative bar chart comparison
  • Per-resource breakdown table (searchable, sortable)
  • Engagement funnel visualization
  • KPI cards with sparklines
  • CSV export
  • Custom date ranges
  • Full custom metric support: discovery, searchable picker UI, and visualization in all chart types -- custom metrics are treated identically to standard metrics

This means per-variant reporting (e.g. "how many times was the England vs Argentina creative shown?") works today via custom metrics, with no development needed beyond the creative script itself.

Adding DOOH-specific reporting views (per-screen, per-location) would strengthen this further. The Device column already exists in the Bunny CSV but isn't exposed in the UI yet -- adding device-level breakdown would be a small enhancement.


Summary: Priority and Effort

CapabilityEffortPriorityApproach
Per-variant reporting (custom metrics)Very LowHighApproach B -- full pipeline already works end-to-end
Expected vs actual impression comparisonNoneHighApproach A -- use existing data
Health check endpointVery LowMediumQuick win for demo
Client-side failure beacon + fallbackMediumHighApproach D -- self-contained, no CMS integration
Feed/DCO system (full)Medium-HighMediumSeparate project, unlocks broader value
Cached fallback for feedsLowHighBuilt into feed system design
Static fallback creativeLowMediumSimple UI addition
Context-aware creative selectionMediumMedium"AI-powered" differentiator
Generative fallbackHighLowFuture roadmap

Suggested Sequence

  1. Now (no dev): Offer expected-vs-actual impression comparison as a reporting service
  2. Quick wins (days): Health endpoint, custom metric reporting visualization
  3. Near-term (weeks): Client-side loader with failure beacon, static fallback creative option
  4. Medium-term (months): Feed/DCO system with caching, per-variant analytics, DOOH-specific reporting views
  5. Future: Context-aware selection, generative fallback

Technical Notes

DOOH Delivery Modes

DOOH screens typically support one or more of:

ModeCavai SupportNotes
VAST 4.xYes (existing)Full VAST 4.2 tag generation with tracking, companions, error reporting
VPAIDYes (existing)JavaScript-based, full interactivity
Direct URL (iframe/webview)Yes (existing)Load tag-vast.html or standalone preview URL in webview
HTML5 package (self-contained)No (new)Bundle engine + config + assets into single HTML file for player upload. Required by JCDecaux (Broadsign/KIS) for Standard Dynamic and Advanced Dynamic campaigns
Static image/video (JPEG/MP4)No (manual only)Render creative at target resolution and export. No automated pipeline exists, would need headless Chrome rendering
API feed (JSON)No (new)Needs Feed Service -- DOOH CMS polls for content
Static image fallbackPartialScreenshots exist for previews but no dedicated fallback system

Analytics Pipeline Capacity

The existing dual analytics pipeline (Cloudflare Worker + Bunny CDN) handles per-session tracking with:

  • Session-level aggregation via DuckDB
  • Hourly bucketed Parquet files
  • Custom metric MAP columns

For DOOH, volumes would be much lower than web display (thousands vs millions of impressions), so the pipeline can handle it without scaling concerns.

Key Constraint

The Logs-Parser aggregation uses hardcoded SELECT statements. Any new fields sent from the engine must also be added to Logs-Parser and Backend query logic. This is a known pipeline constraint documented in the analytics architecture.


Question 4: HTML5 Package Delivery (JCDecaux / Sophie Gunyon, July 2026)

"In regard to your tags, is the only way to engage through HTML files? Are you compliant with HTML 5.0? This is for DOOH."

Context

Sophie asked whether Cavai can deliver HTML5-compliant creatives for DOOH. The underlying question is about delivery format, not technology -- Cavai creatives are already 100% HTML5 (HTML/CSS/JS, no Flash, no plugins), but how they're delivered today (a JavaScript tag loading from CDN) is different from what DOOH players expect.

JCDecaux's Specific Requirements

JCDecaux uses Broadsign and KIS (Digiboard) as their player systems. They define four campaign types:

TypeFormat RequiredDescription
Standard LinearJPEG / MP4Basic equal rotation of static content
Advanced LinearJPEG / MP4Rule-based scheduling (location, time)
Standard DynamicHTML fileBespoke HTML template with pre-set data (town names, weather, store info)
Advanced DynamicHTML fileHTML reading external data feeds for real-time changes

Source: JCDecaux UK Dynamic Specifications

HTML file requirements:

  • Must be tested in both Digiboard (KIS) and Broadsign Administration
  • Must include embedded fallback copy (static default)
  • Designed to correct screen specs per Broadsign & KIS IDs
  • External data sources via HTTPS (preferred) or FTP
  • New data sources require network whitelisting
  • Lead time: 10 working days per file (1-10 files), 15 days (11-30 files), 20 days (30-50 files)

What Cavai Can Deliver Today

  • Standard/Advanced Linear (JPEG/MP4): Not automated. Would require manually rendering the creative at the target resolution via screenshot or screen recording. No export pipeline exists. Feasibility: low effort to do manually for a one-off, medium effort to automate.
  • Standard/Advanced Dynamic (HTML): Cannot deliver today. The build pipeline produces a JS tag + CDN-hosted assets, not a self-contained HTML file.

What's Needed for HTML5 Package Delivery

A new build target in Creative-Composer that produces a single self-contained HTML file:

  1. Bundle engine code inline -- the Creative-Engine JS that currently loads via CDN must be embedded in the HTML
  2. Embed creative.json -- the creative configuration that currently loads at runtime must be inlined
  3. Embed all assets -- images, fonts, video as base64 data URIs or inline blobs (this will significantly increase file size)
  4. Remove CDN dependencies -- stub.js currently resolves everything against delivery-3.cavai.com. The self-contained build must work fully offline
  5. Player-specific hooks -- Broadsign requires BroadSignPlay() to be implemented. KIS may have its own requirements
  6. No-op analytics -- the tracking/impression system must degrade gracefully when there's no network, not throw errors
  7. Static fallback -- JCDecaux requires embedded fallback copy. The creative needs a static default state
  8. Screen dimensions -- DOOH screens use non-standard resolutions (e.g. 1080x1920 portrait). The creative must render at the exact target resolution

Open Questions

  • Do JCDecaux's screens have internet access? If yes, a Direct URL approach (point the player to a Cavai-hosted URL) might work without building the self-contained package at all. This is the simplest path.
  • Are the screens touch-enabled? If not, the core Cavai value proposition (interactive creatives, engagement tracking) is reduced. Non-touch screens would benefit more from MP4 render.
  • File size limits? Embedding video as base64 will produce very large HTML files. JCDecaux may have file size restrictions for their players.
  • Which campaign type does this client need? Sophie indicated "not DCO", which suggests Standard Dynamic at most, possibly just Standard Linear (JPEG/MP4).

Recommendation

  1. Clarify requirements first -- determine which of the four campaign types is needed and whether screens have internet
  2. If Standard Linear -- render creative as JPEG/MP4 manually for now. Low effort, no development needed
  3. If screens have internet -- explore Direct URL delivery as an alternative to self-contained packages
  4. If self-contained HTML is required -- scope the work with Kevin (backend/Composer) and Haakon (product). This is a new build target, not a config change

Internal documentation