Integrate Banner X Video Interaction Reporting

What this turns on

Video reporting shows how far shoppers watch — creative view, play, quartile progress, and completion — plus control actions (skip, pause, mute). It works for Banner X video creative.

Banner X video is typically rendered through a VAST 4.0 player that you also have integrated, so the fastest way to enable this reporting is to let the player fire the events for you: Epsilon returns a VAST tag in the adm field of the ad response, and you inject a <TrackingEvents> block that points each playback milestone at the interaction endpoint. This guide shows how to build that block. If your player can't emit VAST tracking (or the video isn't served through VAST), use the manual beacon fallback instead.

This guide covers only the video interaction types and how to wire them through VAST. For the endpoint, authentication, core fields, beacon mechanics, dedup, and testing, see the Technical Reference.

Prerequisites

PrerequisiteWhy it matters
Read the Technical ReferenceCovers the endpoint, authentication, core fields, and deduplication rules used by all video events.
Served ads return a realised adId (citrusAdId)Ensures every video interaction can be attributed back to the delivered ad.
A VAST 4.0 capable player that renders the adm tag and fires <TrackingEvents>The player fires the injected beacons on real playback progress.
The player expands VAST macros (e.g. [TIMESTAMP], [CACHEBUSTING])Lets the player stamp each beacon at fire time instead of you inferring it.
You read UniversalAdId idValue from each <Creative>This is the stable, per-video id Epsilon uses as videoId to tie the funnel together (a single ad can carry more than one video).
Consistent tracking id per ad sessionUse sessionId, customerId, or dtmToken consistently so reporting can join events across the session.

What Epsilon serves today (and what you add)

The adm object in the Banner X response is a VAST 4.0 tag. Epsilon already wires impression and click tracking into it (<Impression> and <VideoClicks><ClickTracking>). It does not serve the progress and interaction tracking — that is what you inject.

The interaction endpoint already accepts every video type below. You bridge the two by adding a <TrackingEvents> block whose URLs are GET /v1/events/ad/interaction beacons. When the player crosses each milestone, it fires the matching URL. Leave the Epsilon-served <Impression> and <ClickTracking> nodes exactly as they are — you are only adding <TrackingEvents>.

Interaction Types for Video Reporting

Send the core fields plus videoId on every video event. iabConsentString is the optional field on all types (URL-encoded). Fire funnel events in order as the player reaches each milestone, reusing the same adId, videoId, and tracking id for the whole view session.

Funnel Events

interactionTypeWhen to fireReporting purpose
videoCreativeViewFirst video frame renderedVideo impression baseline
videoPlayPlayback starts (user-initiated or autoplay per policy)Video start rate
videoFirstQuartile25% of duration watchedVideo funnel Q1
videoMidpoint50% watchedVideo funnel Q2
videoThirdQuartile75% watchedVideo funnel Q3
videoComplete100% watchedCompletion rate — creative effectiveness and video spend justification

Control Events

interactionTypeWhen to fireReporting purpose
videoSkipUser skips before completeSkip rate — drop-off / creative issues
videoPauseUser pausesEngagement depth / distraction
videoResumeUser resumes after pauseRe-engagement after pause
videoMuteUser mutes audioAudio engagement preference
videoUnmuteUser unmutesActive audio interest

Map VAST events to Epsilon interaction types

VAST players fire standard <Tracking event="…"> callbacks. Inject one <Tracking> node per row below, pointing at the interaction endpoint with the mapped interactionType.

VAST <Tracking event>Epsilon interactionType
creativeViewvideoCreativeView
startvideoPlay
firstQuartilevideoFirstQuartile
midpointvideoMidpoint
thirdQuartilevideoThirdQuartile
completevideoComplete
skipvideoSkip
pausevideoPause
resumevideoResume
mutevideoMute
unmutevideoUnmute

Other VAST events with no mention above (e.g. progress, fullscreen, exitFullscreen, rewind, close) are not part of Epsilon video reporting — don't inject beacons for them.

Construct the tracking URL

Each injected <Tracking> URL is a single, URL-encoded GET beacon. Populate it at build time with values from the ad response, and use a VAST macro for the timestamp so the player stamps it at fire time.

https://integration.{url}.citrusad.com/v1/events/ad/interaction
  ?adId={citrusAdId}
  &interactionType={mapped type, e.g. videoFirstQuartile}
  &videoId={UniversalAdId idValue for this creative}
  &sessionId={your session tracking id}
  &timestamp=[TIMESTAMP]
  • adId — read citrusAdId from the served banner.
  • videoId — read idValue from the <UniversalAdId> of the creative you're wiring. This is per-creative: if an ad carries multiple videos, each <Creative> gets its own <TrackingEvents> block using that creative's idValue, so the funnel ties back to the video that was actually watched.
  • sessionId — inject your sessionId (or customerId / dtmToken) at build time; the endpoint rejects events with no tracking id.
  • timestamp — use the [TIMESTAMP] VAST macro so the player substitutes the real ISO 8601 fire time. If your player doesn't support it, stamp the beacon another way, but don't hard-code a single time for all events.
  • Add [CACHEBUSTING] as a throwaway parameter if your player caches identical URLs.

Inject <TrackingEvents> into the served VAST tag

Add a <TrackingEvents> block inside each <Creative>'s <Linear> element (after <VideoClicks>, matching the VAST 4.0 sample). Below, the <Impression> and <ClickTracking> are Epsilon-served and left untouched; the highlighted <TrackingEvents> block is what you add. Note videoId reuses this creative's idValue (…000003).

<Creative>
  <UniversalAdId idRegistry="citrusad.com" idValue="00000000-0000-0000-0000-000000000003">
    00000000-0000-0000-0000-000000000003
  </UniversalAdId>
  <Linear>
    <Duration>00:00:15</Duration>
    <MediaFiles>
      <MediaFile delivery="progressive" type="video/mp4" width="1920" height="1080">
        <![CDATA[https://example.com/media/example-video-2.mp4]]>
      </MediaFile>
    </MediaFiles>
    <VideoClicks>
      <ClickTracking>
        <![CDATA[https://integration.{retailer}.citrusad.com/v1/resource/second-c/example_ad_id]]>
      </ClickTracking>
      <ClickThrough></ClickThrough>
    </VideoClicks>

    <!-- Injected by the retailer: maps VAST playback events to the Epsilon interaction endpoint -->
    <TrackingEvents>
      <Tracking event="creativeView"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoCreativeView&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="start"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoPlay&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="firstQuartile"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoFirstQuartile&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="midpoint"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoMidpoint&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="thirdQuartile"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoThirdQuartile&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="complete"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoComplete&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="skip"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoSkip&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="pause"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoPause&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="resume"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoResume&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="mute"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoMute&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
      <Tracking event="unmute"><![CDATA[https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?adId=example_ad_id&interactionType=videoUnmute&videoId=00000000-0000-0000-0000-000000000003&sessionId=sess_001&timestamp=[TIMESTAMP]]]></Tracking>
    </TrackingEvents>
  </Linear>
</Creative>
📘

Multiple videos in one ad

Repeat the <TrackingEvents> block for every <Creative>, each using its own UniversalAdId idValue as videoId. Never share one idValue across creatives — that's what lets Epsilon attribute the funnel to the specific video served.

Playback Sequence

Funnel events follow this order over a full view:

videoCreativeViewvideoPlayvideoFirstQuartilevideoMidpointvideoThirdQuartilevideoComplete

RuleGuidance
Funnel milestonesMust be tied to real playback progress. VAST players fire quartiles on genuine progress; don't synthesise them on seek.
Control eventsvideoSkip, videoPause, videoResume, videoMute, and videoUnmute may fire at any point during playback.
Session attributionReuse the same adId, videoId (idValue), and tracking id across the session so the funnel ties together.

Alternative — fire beacons from player callbacks

If your player can't emit VAST <TrackingEvents>, or the video isn't served through VAST, fire the same beacons directly from player callbacks. The endpoint and fields are identical — you're just constructing the URL in code instead of in the VAST tag.

Step 1 — Capture context

Read adId (citrusAdId) and the creative's UniversalAdId idValue to use as videoId. Reuse the session tracking id.

Step 2 — Fire funnel milestones from player callbacks

function fireVideo(interactionType) {
  navigator.sendBeacon(
    "https://integration.{retailer}.citrusad.com/v1/events/ad/interaction?" +
    new URLSearchParams({
      adId, interactionType, videoId, sessionId,
      timestamp: new Date().toISOString()
    })
  );
}
// e.g. player.on("firstquartile", () => fireVideo("videoFirstQuartile"));

Step 3 — Fire control events as they happen

Hook skip/pause/resume/mute/unmute and fire the matching type. Debounce rapid toggles.

Sample Requests

The injected VAST URLs and the manual beacons resolve to the same GET request. Full URLs below are line-wrapped for readability — send as a single encoded query string.

Video play

GET https://integration.retailer.citrusad.com/v1/events/ad/interaction
  ?adId=banner_vid001
  &timestamp=2026-05-20T10:20:00Z
  &sessionId=sess_001
  &interactionType=videoPlay
  &videoId=00000000-0000-0000-0000-000000000003

Quartile milestone

Same shape for videoFirstQuartile / videoMidpoint / videoThirdQuartile / videoComplete.

GET https://integration.retailer.citrusad.com/v1/events/ad/interaction
  ?adId=banner_vid001
  &timestamp=2026-05-20T10:20:15Z
  &sessionId=sess_001
  &interactionType=videoFirstQuartile
  &videoId=00000000-0000-0000-0000-000000000003

Control event

Same shape for videoSkip / videoPause / videoResume / videoMute / videoUnmute.

GET https://integration.retailer.citrusad.com/v1/events/ad/interaction
  ?adId=banner_vid001
  &timestamp=2026-05-20T10:20:40Z
  &sessionId=sess_001
  &interactionType=videoSkip
  &videoId=00000000-0000-0000-0000-000000000003

What Good Looks Like

AreaExpected outcome
Injected trackingEach <Creative> carries one <TrackingEvents> block; every <Tracking> URL uses that creative's idValue as videoId.
Quartile eventsQuartiles arrive in order, with exactly one videoComplete for a full watch.
Control eventsEvents don't duplicate beyond the deduplication rules.
Re-watchesRe-watches in the same session reuse videoId and remain attributable.

Troubleshooting (Video specifics)

IssueLikely causeAction
No progress events arrive, only impression/click<TrackingEvents> not injected, or added outside <Linear>.Add the block inside each <Creative>'s <Linear> and confirm the player parses it.
All events share one timestamp[TIMESTAMP] macro not expanded by the player.Confirm macro support, or stamp per fire; don't hard-code one time.
Events land but can't be attributed to a videovideoId missing or reused across creatives.Set videoId to each creative's own UniversalAdId idValue.
Events return HTTP 400Unencoded URL, missing tracking id, or unknown interactionType.URL-encode the whole <Tracking> URL; include sessionId/customerId/dtmToken; use the exact mapped type.
Events appear at fixed intervals rather than matching playbackBeacons fired on a timer instead of real progress.Bind each event to the player's actual progress callbacks (VAST does this for you).
videoComplete fires more than onceCompletion handler also fires on loop or replay.Debounce and gate to a single completion per playback.
videoComplete fires on loadThe completion event is tied to load rather than 100% playback.Tie videoComplete to genuine end-of-playback.

For general endpoint troubleshooting, see the Technical Reference troubleshooting section.