Track video engagement to build video-based cohorts and understand viewing behavior.Documentation Index
Fetch the complete documentation index at: https://docs.permutive.com/llms.txt
Use this file to discover all available pages before exploring further.
Initialization
Player Lifecycle
Best Practices
Overview
Video tracking in the Permutive JavaScript SDK is handled by the CTV addon. The addon manages engagement timing automatically and tracks the standard Permutive video events —Videoview and VideoCompletion — without requiring manual interval logic or custom event construction.
The CTV addon is the correct approach for video tracking on:
- Standard web pages with embedded video players
- Web-based CTV applications (Samsung Tizen, LG WebOS, HbbTV)
The CTV addon must be enabled for your project before use. Enable it in the Permutive dashboard under Settings > Integrations.
Initializing the CTV Addon
Initialize the addon when a user starts watching a video. Callingpermutive.addon("ctv", ...) automatically tracks a Videoview event and prepares the addon for engagement tracking.
permutive.addons.ctv.
Duration
Passduration in milliseconds. This is required for the VideoCompletion event to include an accurate completion percentage. If the duration is not known at initialization time, you can set it later using .setDuration() before calling .stop().
Player Lifecycle
Map your video player’s events to the CTV addon methods. The addon handles engagement timing internally — there is no need to implement your own interval or timer logic.| Addon Method | When to Call | Effect |
|---|---|---|
.play(position?) | Player starts playing or user scrubs | Starts or resumes engagement timer |
.pause(position?) | Player pauses | Pauses engagement timer |
.stop(position?) | User exits or content ends | Tracks VideoCompletion with aggregated engagement |
Generic HTML5 Video Example
Handling Duration Not Yet Available
Ifduration is not available when the video starts (common with live streams or when metadata loads asynchronously), initialize without it and call .setDuration() once the value is known:
Handling Seek and Scrub
When a user seeks to a new position, call.play() with the new position. This correctly updates the engagement tracker’s reference point without inflating engaged time:
Handling Buffering
Pause engagement tracking while the player is buffering so that buffer time is not counted as active engagement:Video Ad Tracking
Track video ad events using the addon’s.track() method. For mid-roll ads, pause the main content tracker while the ad plays and resume it when the ad finishes.
Ad Event Reference
| Event | When to Track |
|---|---|
VideoAdView | When a video ad starts playing |
VideoAdCompletion | When a video ad finishes playing |
VideoAdClicked | When a user clicks on a video ad |
Ad Event Properties
Properties for video ad events are nested under thead key:
| Property | Type | Description |
|---|---|---|
ad.title | string | Title of the video advert |
ad.duration | number | Duration of the advert in seconds |
ad.muted | boolean | Whether the advert was muted |
ad.campaign_id | string | Campaign identifier |
ad.creative_id | string | Creative identifier |
Pre-roll Ads
Mid-roll Ads
Post-roll Ads
Video Properties
Pass video metadata under thevideoProperties key when initializing the addon. All properties are optional but richer metadata enables more precise cohort building.
| Property | Type | Description |
|---|---|---|
title | string | Video title |
genre | string[] | List of genres |
content_type | string[] | List of content types |
age_rating | string | Age rating (e.g. “PG-13”, “TV-MA”) |
runtime | number | Runtime in seconds |
country | string | Origin country |
original_language | string | Original language of the content |
audio_language | string | Language of the audio track being watched |
subtitles.enabled | boolean | Whether subtitles were enabled |
subtitles.language | string | Subtitle language |
season_number | number | Season number |
episode_number | number | Episode number |
consecutive_episodes | number | Number of episodes watched consecutively |
iab_categories | string[] | IAB content taxonomy categories |
audio_language as an array instead of a string) will be silently removed from the event rather than causing a schema error.
Complete Working Example
A full integration with an HTML5 video element, including ad break handling:Engagement Tracking Patterns
The CTV addon tracks engagement automatically. You do not need to implement interval timers or milestone checks manually. The addon’s internal engagement model works as follows:- Play — When
.play()is called, the engagement clock starts. - Pause — When
.pause()is called, the clock stops. Time elapsed since the last.play()call is accumulated. - Resume — When
.play()is called again, the clock resumes from the current accumulated total. - Stop — When
.stop()is called, the final accumulatedengaged_timeand the calculatedcompletionpercentage are included in theVideoCompletionevent automatically.
.pause() during buffer states.
Best Practices
Always Call stop() When the User Exits
Always Call stop() When the User Exits
The
VideoCompletion event is only tracked when .stop() is called. Make sure you call it not just when a video ends naturally, but also when a user navigates away or closes the player early.Provide Duration Accurately
Provide Duration Accurately
The
completion percentage in VideoCompletion is calculated from the duration you provide. Set duration in milliseconds at initialization, or call .setDuration() once metadata is available. Do not guess or hardcode durations.Initialize Once Per Content Session
Initialize Once Per Content Session
Call
permutive.addon("ctv", ...) once per piece of content. Calling it again reinitializes the addon and tracks a new Videoview event. If the user watches multiple videos in a session, reinitialize for each new piece of content after calling .stop() on the previous one.Pause During Ad Breaks
Pause During Ad Breaks
Call
.pause() when a mid-roll ad starts so that ad playback time is not included in the main content’s engaged_time. Resume with .play() when the ad finishes.Include Rich Video Metadata
Include Rich Video Metadata
Every property you pass in
videoProperties becomes available for cohort building and targeting. At minimum, include title. Adding genre, content_type, season_number, and episode_number significantly increases the granularity of video-based cohorts.Troubleshooting
Videoview event is not appearing in the dashboard
Videoview event is not appearing in the dashboard
Problem: The
Videoview event is not visible in Permutive analytics.Solutions:- Confirm the CTV addon is enabled under Settings > Integrations in the Permutive dashboard.
- Verify
permutive.addon("ctv", ...)is being called in the browser console — it should not throw an error. - Check that the SDK is initialized before
permutive.addon("ctv", ...)is called. - Allow 5-10 minutes for events to appear in the dashboard after they fire.
permutive.addons.ctv is undefined
permutive.addons.ctv is undefined
Problem: Calling
permutive.addons.ctv.play() throws because the addon is not available.Solutions:- Ensure
permutive.addon("ctv", ...)has been called before accessingpermutive.addons.ctv. - Add a guard check:
if (permutive.addons.ctv) { ... }before calling addon methods. - Confirm the CTV addon is enabled in your workspace settings — it will not be registered if it has not been enabled.
VideoCompletion shows 0% completion
VideoCompletion shows 0% completion
Problem: The
VideoCompletion event has a completion value of 0 or is missing it.Solutions:- Ensure
durationis passed (in milliseconds) when callingpermutive.addon("ctv", ...). - If duration is not available at initialization, call
.setDuration(ms)before.stop()is called. - Verify that the duration value is in milliseconds, not seconds.
Engaged time is higher than expected
Engaged time is higher than expected
Problem: The
engaged_time in VideoCompletion includes buffering or paused time.Solutions:- Call
.pause()when the player enters a buffering state (waitingevent on HTML5 video). - Call
.pause()when the player is paused by the user. - Verify that
.pause()is being called before ad breaks start.
Duplicate Videoview events
Duplicate Videoview events
Problem: Multiple
Videoview events fire for a single piece of content.Solutions:permutive.addon("ctv", ...)fires aVideovieweach time it is called. Guard the initialization so it only runs once per content session.- Use a flag or check
permutive.addons.ctvto avoid re-initializing on repeatedplayevents from the player.
Related Documentation
CTV Video Tracking
Video event schema and cross-platform tracking patterns
Video Integrations
Pre-built integrations for JW Player, Brightcove, YouTube, and more
Event Tracking
General event tracking with the JavaScript SDK
Web CTV
Full CTV addon reference for web-based CTV platforms