EnfinitOSEnfinitOS
DevelopersVisual render
Production-ready scaffold

CTV App SDK

Reference SDK across Roku, Samsung Tizen, LG webOS, Fire TV / Android TV. Same core, four thin wrappers.

@enfinitos/sdk-ctv-appSubstrate CTVTypeScript, BrightScript, JavaScript
Install

Get the SDK

npm install @enfinitos/sdk-ctv-app

About this status badge

Typed, tested, documented, and wired to the EnfinitOS platform endpoints that exist today. Vendor-side SDK integrations (Broadsign / VIOOH / DJI / Tizen / Alexa / Twilio / Stripe / etc.) land per-customer at pilot integration time — those bring the renderer/transport/exchange-specific code; the EnfinitOS half is ready.

README

The developer-facing documentation in full

The same README the SDK package ships with — rendered here at build time so what you read matches exactly what you install.

@enfinitos/sdk-ctv-app

EnfinitOS reference SDK for the CTV substrate — TypeScript core plus four per-platform thin wrappers covering the four CTV hardware platforms that dominate the market:

  • Roku (BrightScript)
  • Samsung Tizen (JavaScript on WebKit)
  • LG WebOS (JavaScript on WebKit)
  • Apple TV / tvOS (Swift on UIKit/SwiftUI)

The SDK is what a CTV app embeds to plug its inventory into the EnfinitOS rights / consent / proof-of-play / audit plane.

Architecture

                       ┌──────────────────────────┐
                       │   EnfinitOS Platform     │
                       │  (runtime, rights,       │
                       │   audit, fleet health)   │
                       └────────────┬─────────────┘
                                    │  HTTPS REST
                                    │  (device JWT)
                                    │
   ┌────────────────────────────────▼─────────────────────────┐
   │                                                          │
   │   ┌──────────────────────────────────────────────────┐   │
   │   │       core/  (TypeScript — ~2000 lines)          │   │
   │   │                                                  │   │
   │   │   ┌──────────────────────┐  ┌─────────────────┐  │   │
   │   │   │ EnfinitOSCtvClient   │  │ ProofReporter   │  │   │
   │   │   │  (renderer-core +    │  │ (proof-of-play  │  │   │
   │   │   │   fetchAdSlot /      │  │  envelope ride  │  │   │
   │   │   │   reportPlay* /      │  │  on existing    │  │   │
   │   │   │   reportInteraction) │  │  event-ingest)  │  │   │
   │   │   └──────────────────────┘  └─────────────────┘  │   │
   │   │                                                  │   │
   │   │   ┌─────────────────────────────────────────┐    │   │
   │   │   │ ViewabilityScorer                       │    │   │
   │   │   │  (MRC-style score from 6 signals)        │    │   │
   │   │   └─────────────────────────────────────────┘    │   │
   │   └──────────────────────────────────────────────────┘   │
   │                          ▲                               │
   │                          │                               │
   │   ┌──────────────────────┴──────────────────────────┐    │
   │   │       platforms/  (~250 lines each)             │    │
   │   │                                                 │    │
   │   │     Roku        Tizen     WebOS    Apple TV     │    │
   │   │   (BrightScript) (JS)     (JS)     (Swift)      │    │
   │   │                                                 │    │
   │   │   Each: lifecycle wiring, remote-key mapping,   │    │
   │   │   native-API signals, thin facade over core.    │    │
   │   └─────────────────────────────────────────────────┘    │
   └─────────────────────┬────────────────────────────────────┘
                         │
                         ▼
              CTV hardware
              (smart TV / set-top box / streaming stick)

Why a TS core + four thin wrappers

Three of the four CTV platforms (Tizen / WebOS / Apple TV) can host the TypeScript core through their JS runtimes or through native-side plumbing. The fourth (Roku) cannot — BrightScript is the only language Roku channel apps speak.

Rather than build four parallel implementations, we ship:

  1. The TS core with all the substantive logic — fetch / resolve / grant / event reporting / viewability scoring / proof-of-play assembly / DRM probe scaffolding. Tested with full vitest coverage. ~2000 lines.
  2. Per-platform wrappers that handle each platform's lifecycle and remote-control surface. Each ~150-300 lines, thin enough that their semantics are 1:1 with the TS core.

The Roku BrightScript wrapper is the only platform where the substrate-side code re-implements logic from the TS core (because BrightScript can't import the JS module); it mirrors the TS core's behaviour function-for-function and ships a roca-test target.

SDKs

SDKLocationLanguageTests
TS corecore/TypeScriptvitest (4 test files, ~80 cases)
Roku wrapperplatforms/roku/BrightScript(substantive coverage on TS core)
Tizen wrapperplatforms/tizen/JavaScript(substantive coverage on TS core)
WebOS wrapperplatforms/webos/JavaScript(substantive coverage on TS core)
Apple TV wrapperplatforms/appletv/SwiftXCTest skeleton (Tests/EnfinitOSCtvTests.swift)

Getting started — TS core

pnpm add @enfinitos/sdk-ctv-app-core
import { EnfinitOSCtvClient } from "@enfinitos/sdk-ctv-app-core";

const client = new EnfinitOSCtvClient({
  apiBaseUrl: "https://api.enfinitos.com",
  appId: "app_streamcorp_v3",
  deviceId: getDeviceId(),
  deviceToken: getDeviceToken(),
  deviceClass: "tizen",                    // one of "roku" | "tizen" | "webos" | "appletv"
  viewerId: getPseudonymousSessionId(),    // optional
});

await client.start();

const asset = await client.fetchAdSlot("pre-roll", {
  contentCategory: "sports",
});
if (asset) {
  await client.reportPlayStart(asset);
  client.setDisplayOn(true);
  client.setAppForeground(true);
  client.setAudioOn(true);
  // ... feed asset.assetUrl into your player ...
  // On each positionUpdate from the player:
  client.recordPosition(currentS, expectedS);
  // ... slot ends ...
  const viewability = client.buildViewability();
  await client.reportPlayComplete(asset, viewability);
}

API surface

Constructor

new EnfinitOSCtvClient({
  apiBaseUrl: string;
  appId: string;
  deviceId: string;
  deviceToken: string;
  deviceClass: "roku" | "tizen" | "webos" | "appletv";
  viewerId?: string;
  onAssetReady?, onError?: callbacks
})

Lifecycle

MethodDescription
start() / stop()Open / close the platform session.

Resolve

MethodDescription
fetchAdSlot(position, extra?)Resolve+grant an ad slot.

Reporting

MethodDescription
reportPlayStart(asset)Start of impression.
reportPlayComplete(asset, viewability)End + proof-of-play envelope.
reportInteraction(asset, interaction)Viewer-driven interaction.
reportPlayError(asset, error)Non-billable error.

Signals (forwarded into the scorer)

MethodDescription
setDisplayOn(on)Hardware power signal.
setAppForeground(fg)App lifecycle signal.
setAudioOn(on)Audio path on/off.
recordPosition(positionS, durationS)Quartile + dwell tick.
buildViewability()Snapshot current viewability score.

Health + introspection

MethodDescription
reportHealth(state, subsystems?)Substrate-agnostic heartbeat.
startHealthHeartbeat(ms)Periodic heartbeat.
queueDepth() / droppedEventCount()Renderer-core introspection.
rendererCore / viewability / proofReporterDirect access to composed instances.

Viewability scoring

The CTV substrate can't measure pixel-level visibility (no Intersection Observer; no in-tab visibility API). The SDK substitutes six platform-observable signals fused into a 0-1 score:

SignalSourceDefault weight
displayOnplatform power query0.30
appForegroundplatform lifecycle hook0.30
audioOnplayer muted state0.20
q2Reachedscorer.recordPosition0.10
q4Reachedscorer.recordPosition0.10

The MRC-flavoured "isMrcViewable" predicate requires displayOn, appForeground, q2Reached, and dwellMs >= 2000.

Proof-of-play

Every reportPlayComplete call submits a ProofOfPlay envelope:

{
  asset: ResolvedAsset;
  startedAt: string;
  endedAt: string;
  viewability: ViewabilityScore;
  completed: boolean;        // === viewability.q4Reached
  correlationId?: string;    // optional pod/cue id
}

The envelope rides the renderer-core's interaction-event path under kind: "proof_of_play" so the platform's existing audit-ledger absorbs it without a new endpoint.

Platform wrapper integration

PlatformWrapperHow it's loaded
RokuMain.brsCopied into channel's source/ folder
Tizenindex.jsImported via npm into the Tizen .wgt
WebOSindex.jsImported via npm into the WebOS app
Apple TVEnfinitOSCtv.swiftSwift Package or copied source

Each wrapper README documents the platform-specific configuration, remote-key mapping, and Tested-versions matrix.

Tests

cd core && pnpm test

The vitest suite covers four areas (~80 test cases):

AreaTests
viewabilityScorer.tsScore formula, weight overrides, clamping, quartile tracking, dwell, MRC predicate, reset.
proofReporter.tsEnvelope submission, validation, timestamp inversion, score out-of-range.
ctvCore.tsArgument validation, lifecycle, fetchAdSlot context shape, play lifecycle, signal forwarding, health, introspection.

The XCTest skeleton at platforms/appletv/swift/Tests/EnfinitOSCtvTests.swift covers the Swift wrapper's construction validation and viewability scoring. The BrightScript / Tizen / WebOS wrappers are thin enough that the TS core's coverage applies semantically; smoke-tested against the native runtimes during integration.

What's SDK-side vs platform-side

ConcernThis SDKPlatform
CTV slot vocabulary(ride existing resolve context)
Viewability scorer(audit ledger records the score)
Proof-of-play envelope✅ rides interaction events✅ existing event-ingest
Per-platform lifecycle wiring✅ in wrappers
Remote-control key mapping✅ in wrappers
DRM key fetching⏳ future tranche⏳ future tranche
Substrate-agnostic primitives(inherits from renderer-core)✅ existing runtime plane

See also

  • packages/sdks/renderer-core/README.md — substrate-agnostic foundation.
  • packages/sdks/dooh-renderer/README.md — sibling SDK for DOOH panels.
  • packages/sdks/streaming-player/README.md — sibling SDK for HLS / DASH / WebRTC streaming.
  • docs/launch/substrate-readiness-matrix.md — CTV substrate readiness.
API reference

Hit the HTTP surface directly

The CTV App SDK is a thin client over the same governed HTTP API every other SDK calls. The full OpenAPI 3.1 reference lives on the docs site, published alongside the April 2027 platform launch.

Sandbox

Run this SDK against a real tenant

The browser demo at enfinitos.com/sandbox runs today against a shared synthetic tenant. The dedicated developer sandbox — your own persistent tenant, API keys, full HTTP-contract coverage — opens ahead of the April 2027 platform launch.