> ## 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.

# Web-Based CTV

> Integrate Permutive on Samsung Tizen, LG WebOS, and HbbTV platforms using the JavaScript SDK

<CardGroup cols={3}>
  <Card title="Samsung Tizen" href="#samsung-tizen" icon="tv" />

  <Card title="LG WebOS" href="#lg-webos" icon="tv" />

  <Card title="HbbTV" href="#hbbtv" icon="tower-broadcast" />
</CardGroup>

## Overview

Web-based CTV platforms run JavaScript applications in embedded browser engines. Permutive's JavaScript SDK integrates with these platforms through the **CTV addon**, which provides specialized video tracking capabilities for streaming content.

**Supported Platforms:**

* [Samsung Tizen](https://docs.tizen.org/platform/what-is-tizen/profiles/tv/)
* [LG WebOS](https://webostv.developer.lge.com/)
* [HbbTV](https://developer.hbbtv.org/)

## Prerequisites

<Steps>
  <Step title="Permutive Workspace">
    An active Permutive workspace. If you're not yet a customer, [get in touch](mailto:support@permutive.com).
  </Step>

  <Step title="Public API Key">
    Your API key from the [Dashboard](https://dash.permutive.com/settings/keys/).
  </Step>

  <Step title="CTV Addon">
    Enable the CTV addon in your [Dashboard integrations](https://dash.permutive.com/settings/integrations/).
  </Step>
</Steps>

***

## Setup

### The JavaScript Tag

Deploy the Permutive JavaScript tag in your CTV web application. The tag initializes the SDK and downloads your workspace-specific bundle.

```html theme={"dark"}
<!-- start Permutive -->
<script>
  !function(e,o,n,i){if(!e){e=e||{},window.permutive=e,e.q=[];var t=function(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,function(e){return(e^(window.crypto||window.msCrypto).getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)})};e.config=i||{},e.config.apiKey=o,e.config.workspaceId=n,e.config.environment=e.config.environment||"production",(window.crypto||window.msCrypto)&&(e.config.viewId=t());for(var g=["addon","identify","track","trigger","query","segment","segments","ready","on","once","user","consent"],r=0;r<g.length;r++){var w=g[r];e[w]=function(o){return function(){var n=Array.prototype.slice.call(arguments,0);e.q.push({functionName:o,arguments:n})}}(w)}}}(window.permutive,"<WORKSPACE_API_KEY>","<WORKSPACE_ID>",{});
  window.googletag=window.googletag||{},window.googletag.cmd=window.googletag.cmd||[],window.googletag.cmd.push(function(){if(0===window.googletag.pubads().getTargeting("permutive").length){var e=window.localStorage.getItem("_pdfps");window.googletag.pubads().setTargeting("permutive",e?JSON.parse(e):[]);var o=window.localStorage.getItem("permutive-id");o&&(window.googletag.pubads().setTargeting("puid",o),window.googletag.pubads().setTargeting("ptime",Date.now().toString())),window.permutive.config.viewId&&window.googletag.pubads().setTargeting("prmtvvid",window.permutive.config.viewId),window.permutive.config.workspaceId&&window.googletag.pubads().setTargeting("prmtvwid",window.permutive.config.workspaceId)}});
</script>
<script async src="https://<ORGANIZATION_ID>.edge.permutive.app/<WORKSPACE_ID>-<CTV_PLATFORM>.js"></script>
<!-- end Permutive -->
```

Replace the following placeholders:

* `<WORKSPACE_API_KEY>` - Your workspace API key
* `<WORKSPACE_ID>` - Your workspace ID
* `<ORGANIZATION_ID>` - Your organization ID
* `<CTV_PLATFORM>` - The platform identifier (e.g., `hbbtv`, `lg-webos-24`)

### Deployment Considerations

<Info>
  **CTV Browser Limitations:** CTV platforms use embedded browser engines with different JavaScript capabilities than desktop browsers. Always use the platform-specific SDK bundle that matches your target platform version.
</Info>

* **Use the correct platform bundle** - Each platform version has a specific SDK bundle optimized for its JavaScript engine
* **Initialize before video playback** - Ensure the SDK is loaded before initializing the CTV addon
* **Test on device** - Emulators may not reflect actual device behavior; test on physical CTV hardware
* **Configure network access** - Add required domains to your app's external access policy (especially on Tizen)

***

## Samsung Tizen

### Platform Version SDKs

Tizen requires platform-specific SDK bundles. Select the bundle matching your target [Tizen platform version](https://developer.samsung.com/smarttv/develop/specifications/web-engine-specifications.html):

| Platform Version | Script Source                 |
| :--------------- | :---------------------------- |
| Tizen 8.0        | `<WORKSPACE_ID>-tizen-8-0.js` |
| Tizen 7.0        | `<WORKSPACE_ID>-tizen-7-0.js` |
| Tizen 6.5        | `<WORKSPACE_ID>-tizen-6-5.js` |
| Tizen 6.0        | `<WORKSPACE_ID>-tizen-6-0.js` |
| Tizen 5.5        | `<WORKSPACE_ID>-tizen-5-5.js` |
| Tizen 5.0        | `<WORKSPACE_ID>-tizen-5-0.js` |
| Tizen 4.0        | `<WORKSPACE_ID>-tizen-4-0.js` |
| Tizen 3.0        | `<WORKSPACE_ID>-tizen-3-0.js` |
| Tizen 2.4        | `<WORKSPACE_ID>-tizen-2-4.js` |
| Tizen 2.3        | `<WORKSPACE_ID>-tizen-2-3.js` |

<Warning>
  **Tizen Deployment Note:** The Tizen script path includes a `/tizensdk` prefix:

  ```html theme={"dark"}
  <script async src="https://<ORGANIZATION_ID>.edge.permutive.app/tizensdk/<WORKSPACE_ID>-tizen-8-0.js"></script>
  ```
</Warning>

### Privileges

Configure the required [Tizen privileges](https://docs.tizen.org/application/web/tutorials/sec-privileges/) in your application:

| Privilege                             | Description                 |
| :------------------------------------ | :-------------------------- |
| `http://tizen.org/privilege/internet` | Required for network access |

### External Access Policy

Add Permutive's CDN and APIs to your [external access policy](https://docs.tizen.org/application/web/tutorials/process/setting-properties/#defining-external-access-policies-in-the-policy-tab) in `config.xml`:

```xml theme={"dark"}
<access origin="https://<ORGANIZATION_ID>.edge.permutive.app" subdomains="true"/>
<access origin="https://api.permutive.com" subdomains="true"/>
<access origin="https://api.permutive.app" subdomains="true"/>
```

### TIFA (Tizen ID for Advertising)

Samsung provides [TIFA](https://www.samsungtifa.com/) as a unique advertising identifier. To use TIFA with Permutive:

1. Add `tifa` as an identifier in your [Dashboard identifiers settings](https://dash.permutive.com/settings/identifiers)
2. Identify users using the TIFA ID:

```javascript theme={"dark"}
permutive.identify([{
  id: "<TIFA_ID>",
  tag: "tifa"
}])
```

See the [Tizen TIFA documentation](https://developer.samsung.com/smarttv/develop/guides/unique-identifiers-for-smarttv/tizen-id-for-advertising.html) for retrieving the TIFA value.

***

## LG WebOS

### Platform Version SDKs

LG WebOS requires platform-specific SDK bundles. Select the bundle matching your target [WebOS platform version](https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine#web-engine):

| Platform Version | Script Source                   |
| :--------------- | :------------------------------ |
| webOS TV 24      | `<WORKSPACE_ID>-lg-webos-24.js` |
| webOS TV 23      | `<WORKSPACE_ID>-lg-webos-23.js` |
| webOS TV 22      | `<WORKSPACE_ID>-lg-webos-22.js` |
| webOS TV 6.x     | `<WORKSPACE_ID>-lg-webos-6.js`  |
| webOS TV 5.x     | `<WORKSPACE_ID>-lg-webos-5.js`  |
| webOS TV 4.x     | `<WORKSPACE_ID>-lg-webos-4.js`  |
| webOS TV 3.x     | `<WORKSPACE_ID>-lg-webos-3.js`  |
| webOS TV 2.x     | `<WORKSPACE_ID>-lg-webos-2.js`  |
| webOS TV 1.x     | `<WORKSPACE_ID>-lg-webos-1.js`  |

### Script Example

```html theme={"dark"}
<script async src="https://<ORGANIZATION_ID>.edge.permutive.app/<WORKSPACE_ID>-lg-webos-24.js"></script>
```

***

## HbbTV

HbbTV (Hybrid Broadcast Broadband TV) applications use the standard CTV script path:

```html theme={"dark"}
<script async src="https://<ORGANIZATION_ID>.edge.permutive.app/<WORKSPACE_ID>-hbbtv.js"></script>
```

***

## CTV Addon

The CTV addon provides video-specific tracking APIs. Initialize it when video content starts:

### Initialization

```javascript theme={"dark"}
const props = {
  videoProperties: {
    title: "Episode Title",
    genre: ["Drama", "Thriller"],
    audio_language: "en"
  },
  duration: 3600000 // Duration in milliseconds (1 hour)
}

// Tracks Videoview event
permutive.addon("ctv", props)
```

The addon becomes available at `permutive.addons.ctv`.

### Addon Methods

| Method                              | Description                                                                                        | Events Tracked    |
| :---------------------------------- | :------------------------------------------------------------------------------------------------- | :---------------- |
| `.play(position?)`                  | Call when content plays or position changes. Starts engagement tracking. Position in milliseconds. | -                 |
| `.pause(position?)`                 | Call when content pauses. Stops engagement tracking. Position in milliseconds.                     | -                 |
| `.stop(position?)`                  | Call when user stops/exits content. Sends completion event. Position in milliseconds.              | `VideoCompletion` |
| `.setDuration(duration)`            | Update content duration in milliseconds. Set before stopping for accurate completion.              | -                 |
| `.track(name, properties, options)` | Track custom events (e.g., ad events).                                                             | Specified event   |

### Video Properties

The following properties are available for video events. All are optional but tracking more enables richer cohort building:

| Property               | Type      | Description                     |
| :--------------------- | :-------- | :------------------------------ |
| `title`                | string    | Video title                     |
| `genre`                | string\[] | List of genres                  |
| `content_type`         | string\[] | Content types                   |
| `age_rating`           | string    | Age rating (e.g., "PG-13")      |
| `runtime`              | number    | Runtime in seconds              |
| `country`              | string    | Origin country                  |
| `original_language`    | string    | Original language code          |
| `audio_language`       | string    | Audio language being watched    |
| `subtitles.enabled`    | boolean   | Whether subtitles are enabled   |
| `subtitles.language`   | string    | Subtitle language               |
| `season_number`        | number    | Season number                   |
| `episode_number`       | number    | Episode number                  |
| `consecutive_episodes` | number    | Consecutive episodes watched    |
| `iab_categories`       | string\[] | IAB content taxonomy categories |

<Info>
  Properties are sanitized to match expected types. Invalid types are removed rather than causing errors.
</Info>

### Complete Example

```javascript theme={"dark"}
// Initialize CTV addon with video metadata
permutive.addon("ctv", {
  duration: 3600000, // 1 hour in milliseconds
  videoProperties: {
    title: "Breaking Bad",
    genre: ["Drama", "Crime"],
    season_number: 1,
    episode_number: 1,
    audio_language: "en",
    runtime: 3600
  }
})

// Get video element reference
const videoElement = document.querySelector("video")

// Track play events
videoElement.addEventListener("play", function() {
  permutive.addons.ctv.play(videoElement.currentTime * 1000)
})

// Track pause events
videoElement.addEventListener("pause", function() {
  permutive.addons.ctv.pause(videoElement.currentTime * 1000)
})

// Track ad breaks
const onAdStart = () => {
  videoElement.pause()
  permutive.addons.ctv.track("VideoAdView", {
    ad_id: "ad_12345",
    ad_position: "midroll"
  })
}

const onAdComplete = () => {
  permutive.addons.ctv.track("VideoAdCompletion", {
    ad_id: "ad_12345"
  })
}

// Track video completion
const onVideoEnd = () => {
  permutive.addons.ctv.stop(videoElement.currentTime * 1000)
}
```

***

## Event Tracking

### Automatic Events

The CTV addon automatically tracks:

| Event               | Description                                                                            |
| :------------------ | :------------------------------------------------------------------------------------- |
| **Videoview**       | Tracked when addon is initialized. Indicates user intent to watch content.             |
| **VideoCompletion** | Tracked when `.stop()` is called. Includes `completion` percentage and `engaged_time`. |

### Manual Events

Track additional video events using `.track()`:

| Event                 | Description              |
| :-------------------- | :----------------------- |
| **VideoAdView**       | Video ad started playing |
| **VideoAdCompletion** | Video ad finished        |
| **VideoAdClicked**    | User clicked on video ad |

```javascript theme={"dark"}
// Track ad view
permutive.addons.ctv.track("VideoAdView", {
  ad_id: "creative_123",
  ad_duration: 30,
  ad_position: "preroll"
})

// Track ad completion
permutive.addons.ctv.track("VideoAdCompletion", {
  ad_id: "creative_123"
})
```

***

## JavaScript SDK Reference

For advanced use cases, the full [JavaScript SDK](/sdks/web/javascript-sdk) is available. Key APIs include:

* `permutive.identify([...])` - Set user identities
* `permutive.track(eventName, properties)` - Track custom events
* `permutive.segments()` - Get current cohort memberships
* `permutive.ready(callback)` - Execute code when SDK is ready

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="SDK not loading">
    **Problem:** Permutive SDK fails to load on CTV device.

    **Solutions:**

    * Verify external access policy includes Permutive domains
    * Check network connectivity on the device
    * Ensure correct platform-specific script URL
    * Verify API key and workspace ID are correct
  </Accordion>

  <Accordion title="Events not being tracked">
    **Problem:** Events don't appear in the dashboard.

    **Solutions:**

    * Verify CTV addon is enabled in dashboard settings
    * Check browser console for JavaScript errors
    * Ensure events match your workspace schema
    * Wait 5-10 minutes for events to process
  </Accordion>

  <Accordion title="Engagement time incorrect">
    **Problem:** Engagement metrics don't match expected values.

    **Solutions:**

    * Ensure `.play()` is called when video plays
    * Ensure `.pause()` is called when video pauses or buffers
    * Call `.stop()` when video ends or user exits
    * Set correct duration before calling `.stop()`
  </Accordion>

  <Accordion title="GAM targeting not working">
    **Problem:** Google Ad Manager targeting values not applied.

    **Solutions:**

    * Ensure GPT library loads before Permutive
    * Verify `_pdfps` exists in localStorage
    * Check that ad requests fire after Permutive initializes
  </Accordion>
</AccordionGroup>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="CTV Overview" icon="tv" href="/sdks/ctv/overview">
    Platform selection guide
  </Card>

  <Card title="Video Tracking" icon="video" href="/sdks/ctv/video-tracking">
    Video event best practices
  </Card>

  <Card title="JavaScript SDK" icon="js" href="/sdks/web/javascript-sdk">
    Full JavaScript SDK reference
  </Card>

  <Card title="Google Ad Manager" icon="google" href="/integrations/advertising/ad-servers/google-ad-manager">
    GAM integration guide
  </Card>
</CardGroup>
