Trillboards

Trillboards CTV Measurement SDK β€” Quickstart

Use the Android SDK for native Android TV, Fire TV, and tablet players. It is built from agent-core-lite, the same heartbeat, identity, and proximity code used by Trillboards-managed players.

The Android heartbeat is deliberately simple:

POST /openrtb/v1/heartbeat

Register the device once, then initialize the SDK with that same stable device identifier. Heartbeat liveness, MAID, HEM, and UID2 ingestion do not require a Partner API key. The registered device/screen binding is the trust boundary.

The separate @trillboards/ctv-measurement web package still accepts a Partner API key because it also calls authenticated /v2/sdk/* enrichment and attribution endpoints.

1. Register each device

Registration is an authenticated provisioning action:

curl -X POST https://api.trillboards.com/v1/partner/device \
  -H "Authorization: Bearer trb_partner_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "store-123-player-1",
    "name": "Store 123 front screen"
  }'

Keep external_id stable for the physical player. The app bundle and screen binding belong to that registered device; do not substitute a screen UUID for a platform advertising identifier.

2. Install the Android SDK

dependencyResolutionManagement {
  repositories {
    google()
    mavenCentral()
    maven { url = uri("https://maven.trillboards.com") }
  }
}
dependencies {
  implementation("com.trillboards:ctv-measurement:1.3.2")
}

Requires Android API 24+ and JDK 17 for the build.

3. Initialize with the registered device ID

import com.trillboards.measurement.MeasurementConfig
import com.trillboards.measurement.TrillboardsMeasurement

val config = MeasurementConfig.Builder(
  deviceId = "store-123-player-1",
)
  .scanIntervalMs(30_000)
  .build()

TrillboardsMeasurement.initialize(applicationContext, config)
TrillboardsMeasurement.setConsentStatus(true)
TrillboardsMeasurement.startScheduledScans()

No API key is needed for this heartbeat flow. The legacy MeasurementConfig.Builder(apiKey, deviceId) overload remains available for apps that also call authenticated Partner API operations.

The AAR declares its own networking, Bluetooth, Wi-Fi, and Android advertising ID permissions. Request the runtime Bluetooth/Wi-Fi permissions appropriate to the device OS.

4. Send targeting identities

The built-in collector sends the actual platform advertising identifier (GAID/AAID) when the platform exposes one. It never fabricates a screen ID as device.ifa.

For audience IDs owned by the host, provide typed extended identities:

import com.trillboards.ctv.core.identity.ExtendedIdentity
import com.trillboards.ctv.core.identity.ExtendedIdentityProvider

val identityProvider = ExtendedIdentityProvider {
  listOf(
    ExtendedIdentity.hemSha256(
      digest = existingSha256EmailDigest,
      source = "your-identity-domain.example",
      observedAt = observedAtIso8601,
    ),
    ExtendedIdentity.uid2Token(
      token = currentUid2AdvertisingToken,
      source = "uidapi.com",
      observedAt = observedAtIso8601,
      expiresAt = uid2ExpiryIso8601,
    ),
  )
}

val config = MeasurementConfig.Builder("store-123-player-1")
  .extendedIdentityProvider(identityProvider)
  .build()

Value semantics are exact:

  • MAID/GAID is persisted raw and emitted as OpenRTB device.ifa.
  • hem_sha256 must already be one SHA-256 digest. The server lowercases the hexadecimal representation once and does not hash it again.
  • uid2_token is opaque and case-sensitive. Send it exactly as issued, with its expiration time so the host can refresh it.
  • HEM and UID2 are emitted under OpenRTB 2.6 user.eids.

5. Verify ingress and egress

Heartbeat success proves only that the registered-device contract accepted the request. For revenue readiness, verify the full chain:

  1. PostgreSQL earner_screen_devices has the raw MAID state.
  2. PostgreSQL device_extended_identities has the exact active HEM/UID2 rows.
  3. Redis dtwin:{screenId} contains the same active identity projection.
  4. A real outbound OpenRTB request contains device.ifa and/or user.eids.
  5. A buyer response produces a VAST render and a canonical completed_impressions row.

Never use request volume or a VAST tracker alone as revenue proof. Canonical revenue is a completed impression tied to the actual demand source and creative.

Endpoint authentication

EndpointAuthentication
POST /v1/partner/devicePartner API key; provisioning
POST /v1/partner/device/{deviceId}/heartbeatNone after device registration
POST /openrtb/v1/heartbeatNone after device/screen registration
GET /v1/partner/device/{deviceId}/heartbeatPartner API key; readback
/v2/sdk/* enrichment and attributionPartner API key

Full OpenAPI: https://api.trillboards.com/docs/openapi/partner-api.yaml.

Deep integration guide: https://api.trillboards.com/docs/integrations/ctv-measurement-partner-integration.md.