Playback & Engagement Events
Session, milestone, completion rate, and player error events.
These events are generated by the telemetry aggregator as it processes playback heartbeats, and by daily metric jobs for aggregate thresholds. They reflect viewer behaviour after a video is published. For the envelope format, shared headers, and retry policy, see Outbound Webhooks.
Telemetry-driven events flush when the aggregator processes stored heartbeats. The delay between a viewer watching and the webhook firing depends on how frequently the aggregator runs: around five minutes in typical setups.
Examples reuse the following placeholders throughout this page:
organization_id → 11111111-1111-1111-1111-111111111111,
video id → 00000000-0000-4000-8000-00000000aa01,
session id → 33333333-3333-4333-8333-333333333333.
playback.session.ended
When it fires. The telemetry aggregator processes a session ended playback event. Watch time and completion are derived from heartbeats and video duration.
{
"id": "evt_e3f4a5b6-c7d8-4901-e234-567890123456",
"type": "playback.session.ended",
"created_at": "2026-05-09T22:00:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": {
"id": "33333333-3333-4333-8333-333333333333",
"watch_time_seconds": 87,
"completion_percent": 69,
"country": "BR",
"device_type": "desktop",
"referrer": "https://example.com/page"
}
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Playback session UUID. |
data.session.watch_time_seconds | number | Estimated watch time: heartbeat interval × count. |
data.session.completion_percent | number | 0-100; max position reached divided by duration. |
data.session.country | string | Optional; ISO country code derived from session geo. |
data.session.device_type | string | Optional; coarse device class (for example desktop, mobile). |
data.session.referrer | string | Optional; referrer URL captured when the session started. |
playback.cta.clicked
When it fires. The player ingests a CTA click telemetry event with a cta_id
attached.
{
"id": "evt_f4a5b6c7-d8e9-4012-f345-678901234567",
"type": "playback.cta.clicked",
"created_at": "2026-05-09T22:01:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": { "id": "33333333-3333-4333-8333-333333333333" },
"cta": {
"id": "66666666-6666-4666-8666-666666666666",
"position_seconds": 45
}
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Session UUID. |
data.cta.id | string | CTA UUID. |
data.cta.position_seconds | number | Optional; player position (in seconds) when the CTA was clicked, if reported. |
playback.milestone.25
When it fires. During telemetry aggregation, a heartbeat shows the viewer reached ≥ 25% of the video's duration in that session. Fires at most once per session per milestone (deduplication via the milestone tracking table).
{
"id": "evt_a5b6c7d8-e9f0-4123-a456-789012345678",
"type": "playback.milestone.25",
"created_at": "2026-05-09T22:02:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": { "id": "33333333-3333-4333-8333-333333333333" },
"milestone": 25
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Session UUID. |
data.milestone | number | Always 25 for this event type. |
playback.milestone.50
When it fires. Same conditions as playback.milestone.25, for the 50% threshold.
{
"id": "evt_b6c7d8e9-f0a1-4234-b567-890123456789",
"type": "playback.milestone.50",
"created_at": "2026-05-09T22:03:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": { "id": "33333333-3333-4333-8333-333333333333" },
"milestone": 50
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Session UUID. |
data.milestone | number | Always 50 for this event type. |
playback.milestone.75
When it fires. Same conditions, for the 75% threshold.
{
"id": "evt_c7d8e9f0-a1b2-4345-c678-901234567890",
"type": "playback.milestone.75",
"created_at": "2026-05-09T22:04:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": { "id": "33333333-3333-4333-8333-333333333333" },
"milestone": 75
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Session UUID. |
data.milestone | number | Always 75 for this event type. |
playback.milestone.100
When it fires. Same conditions, for the 100% threshold: the session reached (or exceeded) the full video duration.
{
"id": "evt_d8e9f0a1-b2c3-4456-d789-012345678901",
"type": "playback.milestone.100",
"created_at": "2026-05-09T22:05:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": { "id": "33333333-3333-4333-8333-333333333333" },
"milestone": 100
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Session UUID. |
data.milestone | number | Always 100 for this event type. |
video.view.milestone
When it fires. During daily metric aggregation, cumulative views for a video cross
100, 1 000, or 10 000 for the first time. Only video.id is sent under data: the full data.video snapshot is not included.
{
"id": "evt_e9f0a1b2-c3d4-4567-e890-123456789012",
"type": "video.view.milestone",
"created_at": "2026-05-10T08:00:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": { "id": "00000000-0000-4000-8000-00000000aa01" },
"milestone_views": 1000
}
}| Path | Type | Description |
|---|---|---|
data.video.id | string | Video UUID. No other video fields are sent. |
data.milestone_views | number | One of 100, 1000, 10000. |
video.completion_rate.threshold
When it fires. While updating daily metrics, the average completion rate (watch time ÷ plays ÷ duration) crosses 40% or 50% from below to at-or-above. Fires once per threshold per direction per day.
{
"id": "evt_f0a1b2c3-d4e5-4678-f901-234567890123",
"type": "video.completion_rate.threshold",
"created_at": "2026-05-10T08:01:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": { "id": "00000000-0000-4000-8000-00000000aa01" },
"completion_rate_percent": 45,
"threshold_percent": 40,
"date": "2026-05-10"
}
}| Path | Type | Description |
|---|---|---|
data.video.id | string | Video UUID. No other video fields are sent. |
data.completion_rate_percent | number | Rounded completion rate after the metric update. |
data.threshold_percent | number | Config threshold crossed: 40 or 50. |
data.date | string | Aggregate calendar day (YYYY-MM-DD). |
playback.error
When it fires. The player reports a playback error event for a session. The error
code is forwarded as-is from the telemetry payload; unknown is used as a fallback if the
code is missing.
{
"id": "evt_a1b2c3d4-e5f6-4789-a012-3456789abcde",
"type": "playback.error",
"created_at": "2026-05-09T22:10:00.000Z",
"organization_id": "11111111-1111-1111-1111-111111111111",
"data": {
"video": {
"id": "00000000-0000-4000-8000-00000000aa01",
"external_id": "c3d7e9a1-2b4f-4c6d-8e0a-1f3b5d7c9e0a",
"status": "complete",
"title": "My video.mp4",
"duration_ms": 125400,
"width": 1920,
"height": 1080,
"collection_id": "22222222-2222-4222-8222-222222222222"
},
"session": { "id": "33333333-3333-4333-8333-333333333333" },
"error": { "code": "network_failed" }
}
}| Path | Type | Description |
|---|---|---|
data.video | object | See shared data.video. |
data.session.id | string | Session UUID. |
data.error.code | string | Error code from the telemetry event; unknown if absent. |