Skip to main content

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.

Installation

Event Tracking

GAM Targeting

Overview

The Permutive Roku Client enables audience tracking and targeting on Roku devices. Built as a SceneGraph Task component, it integrates seamlessly with your Roku channel to collect events, manage identities, and target ads through Google Ad Manager.
BrightScript/SceneGraph: The Roku Client is implemented as a SceneGraph Task component (PermutiveTask.xml) that runs asynchronously alongside your channel.

Installation

1. Add the Component

Copy PermutiveTask.xml from the Permutive repository into your Roku channel’s components directory:
your-channel/
├── components/
│   ├── PermutiveTask.xml  ← Add here
│   └── ...
├── source/
└── manifest

2. Instantiate the Client

Create and configure the Permutive Task node in your root SceneGraph component:
sub init()
    permutive = CreateObject("roSGNode", "PermutiveTask")
    permutive.apiKey = "your-permutive-api-key-here"
    permutive.workspaceId = "your-permutive-workspace-id-here"
    permutive.control = "run"
end sub

3. Store Globally

Store the Permutive Task node globally for access from other components:
m.global.addFields({ permutive: permutive })

Configuration

FieldTypeRequiredDescription
apiKeyStringYesYour Permutive workspace API key
workspaceIdStringYesYour Permutive workspace ID
debugModeBooleanNoEnable schema validation logging
controlStringYesSet to "run" to start the client
permutive = CreateObject("roSGNode", "PermutiveTask")
permutive.apiKey = "your-api-key"
permutive.workspaceId = "your-workspace-id"
permutive.debugMode = true  ' Enable for development
permutive.control = "run"

Identity Management

Set user identities to enable cross-platform audience matching and enrichment.

Default Identity

Set a simple string identity:
m.global.permutive.identity = "some_default_identity"

Tagged Identities

Set identities with tags and optional priority:
' Set an email hash identity
m.global.permutive.identity = {
    tag: "email_sha256"
    id: "a1b2c3d4e5f6..."
    priority: 1
}

' Set a user account ID
m.global.permutive.identity = {
    tag: "user_id"
    id: "user_12345"
    priority: 2
}

' Set Roku advertising ID
m.global.permutive.identity = {
    tag: "rida"
    id: CreateObject("roDeviceInfo").GetRIDA()
}

Identity Fields

FieldTypeRequiredDescription
tagStringYesIdentifier type (must be configured in Dashboard)
idStringYesThe identifier value
priorityIntegerNoPriority for identity resolution (lower = higher priority)
Configure identity tags in your Dashboard identifiers settings before using them.

Event Tracking

Track events to capture user behavior and build audience segments.

View ID

Generate a new view ID for each piece of content the user engages with:
m.global.permutive.view_id = CreateObject("roDeviceInfo").GetRandomUUID()
Always set a new view_id when the user starts watching new content. This links all events for that viewing session.

Tracking Events

Track events with a name and properties:
m.global.permutive.event = {
    name: "Videoview"
    properties: {
        video: {
            title: "Show Title"
            genre: ["Drama"]
            season_number: 1
            episode_number: 5
        }
    }
}

Video Events

Track standard video events for consistent analytics:
' When video starts
m.global.permutive.view_id = CreateObject("roDeviceInfo").GetRandomUUID()
m.global.permutive.event = {
    name: "Videoview"
    properties: {
        video: {
            title: m.video.title
            genre: m.video.genres
            runtime: m.video.duration
        }
    }
}

' When video completes
m.global.permutive.event = {
    name: "VideoCompletion"
    properties: {
        video: {
            title: m.video.title
        }
        aggregations: {
            VideoEngagement: {
                completion: 0.95  ' 95% watched
                engaged_time: 3420  ' seconds
            }
        }
    }
}

' When ad starts
m.global.permutive.event = {
    name: "VideoAdView"
    properties: {
        ad: {
            title: "Acme Cereal"
            duration: 30
            muted: false
            campaign_id: "campaign_123"
            creative_id: "creative_456"
        }
    }
}

' When ad completes
m.global.permutive.event = {
    name: "VideoAdCompletion"
    properties: {
        ad: {
            title: "Acme Cereal"
            campaign_id: "campaign_123"
            creative_id: "creative_456"
        }
    }
}

Special Properties

The Roku Client supports special property constants that are replaced with device-derived data:

Geo Information

Add geographic data based on IP:
m.global.permutive.event = {
    name: "Videoview"
    properties: {
        video: {
            title: "Video Title"
        }
        geo_info: "$ip_geo_info"
    }
}

ISP Information

Add ISP data based on IP:
m.global.permutive.event = {
    name: "Videoview"
    properties: {
        video: {
            title: "Video Title"
        }
        isp_info: "$ip_isp_info"
    }
}
ConstantDescription
$ip_geo_infoGeographic information derived from IP address
$ip_isp_infoInternet Service Provider information derived from IP

Debug Mode

Enable debug mode during development to validate event schemas:
permutive.debugMode = true
permutive.control = "run"
Debug mode logs schema validation errors:
Failed to upload some events (HTTP 400). Not retrying.
One or more events failed validation. Errors: <list of schema errors>
Disable in production: Debug mode adds latency due to schema validation. Only use during development.

Retrieving Data

Get Cohorts

Retrieve the user’s matched cohort IDs:
cohorts = m.global.permutive.cohorts
' cohorts is an array of strings: ["cohort_1", "cohort_2", ...]

Get User ID

Retrieve the Permutive user ID:
userId = m.global.permutive.userId
' userId is a string

Attach Permutive targeting data to GAM ad requests:

Get Key-Values

gamKeyValues = m.global.permutive.gamKeyValues
' gamKeyValues is an associative array: { "permutive": "cohort1,cohort2", "puid": "user_id" }

Serialize for GAM

Convert key-values to the format GAM expects:
function SerializeGamCustParams(custParams as Object) as String
    paramsAsStrings = []
    for each kv in custParams.items()
        paramsAsStrings.Push(kv.key + "=" + kv.value)
    end for
    return paramsAsStrings.Join("&")
end function

Attach to Ad Request

gamKeyValues = m.global.permutive.gamKeyValues

' Create GAM stream request
gamRequest = gamSdk.CreateStreamRequest()

' Serialize and encode custom parameters
custParamsString = SerializeGamCustParams(gamKeyValues)
urlEncodedCustParams = CreateObject("roUrlTransfer").Encode(custParamsString)

' Attach to request
gamRequest.adTagParameters = "cust_params=" + urlEncodedCustParams
See Google’s IMA documentation for more on custom parameters.

Reset User State

Clear all Permutive data from disk and memory:
m.global.permutive.reset = true
Use this when:
  • User logs out
  • User requests data deletion
  • Testing fresh state

Complete Example

' main.brs - Channel initialization

sub Main(args as Dynamic)
    screen = CreateObject("roSGScreen")
    m.port = CreateObject("roMessagePort")
    screen.setMessagePort(m.port)
    scene = screen.CreateScene("MainScene")
    screen.show()

    while(true)
        msg = wait(0, m.port)
        msgType = type(msg)
        if msgType = "roSGScreenEvent"
            if msg.isScreenClosed() then return
        end if
    end while
end sub


' MainScene.brs - Scene initialization

sub init()
    ' Initialize Permutive
    permutive = CreateObject("roSGNode", "PermutiveTask")
    permutive.apiKey = "your-api-key"
    permutive.workspaceId = "your-workspace-id"
    permutive.control = "run"

    ' Store globally
    m.global.addFields({ permutive: permutive })

    ' Set user identity if logged in
    if m.user <> invalid
        m.global.permutive.identity = {
            tag: "user_id"
            id: m.user.id
        }
    end if
end sub


' VideoPlayer.brs - Video playback component

sub onVideoStart()
    ' Generate new view ID for this content
    m.global.permutive.view_id = CreateObject("roDeviceInfo").GetRandomUUID()

    ' Track video view
    m.global.permutive.event = {
        name: "Videoview"
        properties: {
            video: {
                title: m.currentVideo.title
                genre: m.currentVideo.genres
                runtime: m.currentVideo.duration
                season_number: m.currentVideo.season
                episode_number: m.currentVideo.episode
            }
            geo_info: "$ip_geo_info"
        }
    }
end sub

sub onVideoComplete()
    m.global.permutive.event = {
        name: "VideoCompletion"
        properties: {
            video: {
                title: m.currentVideo.title
            }
            aggregations: {
                VideoEngagement: {
                    completion: m.player.position / m.player.duration
                    engaged_time: m.engagedSeconds
                }
            }
        }
    }
end sub

sub onAdStart(ad as Object)
    m.global.permutive.event = {
        name: "VideoAdView"
        properties: {
            ad: {
                title: ad.title
                duration: ad.duration
                muted: ad.muted
                campaign_id: ad.campaignId
                creative_id: ad.creativeId
            }
        }
    }
end sub

sub loadAds()
    gamKeyValues = m.global.permutive.gamKeyValues
    custParams = SerializeGamCustParams(gamKeyValues)

    adRequest = m.gamSdk.CreateStreamRequest()
    adRequest.adTagParameters = "cust_params=" + CreateObject("roUrlTransfer").Encode(custParams)

    m.gamSdk.requestStream(adRequest)
end sub

Troubleshooting

Problem: Events don’t appear in the Permutive dashboard.Solutions:
  • Verify control = "run" is set after configuration
  • Check API key and workspace ID are correct
  • Enable debugMode to see validation errors
  • Ensure network connectivity on the device
  • Wait 5-10 minutes for events to process
Problem: Debug mode shows schema validation failures.Solutions:
  • Verify event names match your workspace schema
  • Check property types match expected schema types
  • Ensure required properties are included
  • Contact Technical Services to update schemas
Problem: Ads don’t show Permutive targeting.Solutions:
  • Verify gamKeyValues is populated before ad request
  • Check URL encoding of custom parameters
  • Ensure cohorts are synced to GAM in Dashboard settings
  • Verify GAM line items target Permutive key-values
Problem: cohorts array is empty.Solutions:
  • Ensure events are being tracked successfully
  • Wait for cohort processing (can take minutes)
  • Verify user qualifies for configured cohorts
  • Check cohorts are enabled for Roku in Dashboard

API Reference

Task Fields

FieldTypeAccessDescription
apiKeyStringWriteWorkspace API key
workspaceIdStringWriteWorkspace ID
debugModeBooleanWriteEnable schema validation
controlStringWriteSet to “run” to start
identityString/ObjectWriteSet user identity
view_idStringWriteCurrent view session ID
eventObjectWriteTrack an event
resetBooleanWriteClear all state
cohortsString[]ReadUser’s cohort IDs
userIdStringReadPermutive user ID
gamKeyValuesObjectReadGAM targeting key-values

CTV Overview

Platform selection guide

Video Tracking

Video event best practices

Google Ad Manager

GAM integration guide

Identity Management

Cross-platform identity