Skip to main content
The shortkit SDK automatically collects engagement signals to power feed personalization, content ranking, and analytics dashboards. This page documents all collected events and how they’re used.

How signals work

The SDK batches engagement events and sends them to the Analytics Service at regular intervals:
  • Default interval: Every 5 seconds during active playback
  • On app state changes: When the app goes to background/foreground
  • On session end: When the user exits the feed
Events are processed for two purposes:
  1. Real-time ranking signals - Updates within 60 seconds to influence feed ordering
  2. Aggregate analytics - Hourly/daily rollups for dashboards and reporting

Event types

Impression

Fired when a video enters the viewport and begins rendering.
FieldTypeDescription
contentIdstringUnique content identifier
positionnumberPosition in the feed (0-indexed)
timestampdatetimeEvent timestamp
userIdstringUser identifier (anonymous or identified)
sessionIdstringCurrent session identifier

Play start

Fired when the first frame of video begins playback.
FieldTypeDescription
contentIdstringContent identifier
startupTimenumberMilliseconds from impression to first frame
initialRenditionstringStarting quality (e.g., “720p”)
codecstringVideo codec used (“h264” or “av1”)
autoplaybooleanWhether playback was automatic

Watch progress

Fired periodically during playback (default: every 1 second).
FieldTypeDescription
contentIdstringContent identifier
currentTimenumberCurrent playback position (seconds)
durationnumberTotal content duration (seconds)
percentCompletenumberPercentage watched (0-100)
currentRenditionstringCurrent quality level
currentBitratenumberCurrent bitrate (kbps)

Rebuffer

Fired when playback stalls waiting for data.
FieldTypeDescription
contentIdstringContent identifier
currentTimenumberPosition where rebuffer occurred
rebufferDurationnumberDuration of stall (milliseconds)
networkTypestringConnection type (wifi, cellular, etc.)

Quality change

Fired when adaptive bitrate streaming switches renditions.
FieldTypeDescription
contentIdstringContent identifier
fromRenditionstringPrevious quality level
toRenditionstringNew quality level
reasonstring”bandwidth” or “buffer”
currentTimenumberPosition when switch occurred

Error

Fired when a playback error occurs.
FieldTypeDescription
contentIdstringContent identifier
errorCodestringPlatform-specific error code
errorMessagestringHuman-readable error message
errorTypestring”network”, “decode”, or “player”
currentTimenumberPosition when error occurred
renditionstringQuality level at error time
networkTypestringConnection type
deviceInfoobjectDevice/browser information

Completion

Fired when video reaches the end or loops.
FieldTypeDescription
contentIdstringContent identifier
totalWatchTimenumberTotal seconds watched
didLoopbooleanWhether the video looped
loopCountnumberNumber of loops completed

Swipe

Fired when user navigates to a different video.
FieldTypeDescription
fromContentIdstringContent user swiped away from
toContentIdstringContent user swiped to
watchTimeOnFromnumberSeconds watched before swiping
directionstring”next” or “previous”

Interaction

Fired when user interacts with a control.
FieldTypeDescription
contentIdstringContent identifier
interactionTypestringType of interaction (see below)
valueanyInteraction-specific value
Interaction types:
  • share - User tapped share button
  • like - User tapped like/bookmark
  • caption_toggle - User enabled/disabled captions
  • speed_change - User changed playback speed (value: new speed)
  • mute_toggle - User muted/unmuted
  • seek - User scrubbed to a new position (value: seek target)
  • fullscreen_toggle - User entered/exited fullscreen
  • pip_toggle - User entered/exited Picture-in-Picture

Ad impression

Fired when an ad unit renders in the feed.
FieldTypeDescription
adIdstringAd identifier
adNetworkIdstringAd network identifier
contentIdBeforestringContent before the ad
positionnumberPosition in feed

Ad completion

Fired when an ad finishes or is skipped.
FieldTypeDescription
adIdstringAd identifier
watchTimenumberSeconds watched
didCompletebooleanWhether ad played to completion
didSkipbooleanWhether user skipped

Feed entry

Fired when user enters the feed.
FieldTypeDescription
entryPointstringHow user entered: “tab”, “widget”, “deepLink”
timestampdatetimeEntry timestamp
sessionIdstringNew session identifier

Feed exit

Fired when user leaves the feed.
FieldTypeDescription
totalSessionTimenumberSession duration (seconds)
videosWatchednumberNumber of videos viewed
adsWatchednumberNumber of ads viewed
exitMethodstringHow user exited: “tabSwitch”, “background”, “navigation”

How signals affect ranking

The Feed Service uses engagement signals to personalize content ranking:
SignalRanking Effect
Watch timeContent with high average watch time ranks higher
Completion rateContent users finish ranks higher
Swipe velocityContent users quickly swipe away from ranks lower
Topic engagementContent matching user’s topic preferences ranks higher
RecencyFresh signals weighted more than historical

Topic affinity

The User Signal Service builds topic affinity profiles based on viewing patterns:
// Conceptual example of how topic affinity is calculated
userProfile.topicAffinities = {
  "politics": 0.72,    // High engagement with politics content
  "sports": 0.45,      // Moderate engagement
  "entertainment": 0.23 // Lower engagement
};
When computing feed rankings, content tagged with higher-affinity topics receives a boost.

Data retention

Data TypeRetention
Real-time signals7 days (for ranking)
Aggregated analytics2 years
Raw event logs90 days
User profilesUntil deletion requested

Privacy considerations

Anonymous mode

Before setUserId is called, all signals are attributed to an anonymous device ID:
  • Personalization works at the device level
  • No PII is stored
  • Cross-device tracking is not possible

Identified mode

After setUserId is called:
  • Signals merge with the identified profile
  • Cross-device personalization becomes possible
  • The host app’s user ID is stored (but not validated or enriched)
Use an opaque identifier (database ID, UUID) rather than email addresses or other PII for user IDs.

Data subject requests

For GDPR/CCPA compliance, use the API to export or delete user data:
# Export user data
curl -X POST https://api.shortkit.dev/v1/users/{userId}/export \
  -H "Authorization: Bearer sk_live_your_secret_key"

# Delete user data
curl -X DELETE https://api.shortkit.dev/v1/users/{userId} \
  -H "Authorization: Bearer sk_live_your_secret_key"

Accessing signal data

Admin Portal

View aggregate signals in Analytics → Content Performance:
  • Views and unique viewers
  • Average watch time and completion rate
  • Engagement by time of day
  • Geographic distribution

Analytics API

Query signals programmatically:
curl https://api.shortkit.dev/v1/analytics/content/cnt_abc123 \
  -H "Authorization: Bearer sk_live_your_secret_key"
Response:
{
  "data": {
    "contentId": "cnt_abc123",
    "views": 15420,
    "uniqueViewers": 12350,
    "totalWatchTime": 289500,
    "avgWatchTime": 18.7,
    "completionRate": 0.42,
    "shares": 234,
    "rebufferRate": 0.012
  }
}

Raw data export

Export raw event data for custom analysis:
curl -X POST https://api.shortkit.dev/v1/analytics/export \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "startDate": "2024-01-01",
    "endDate": "2024-01-31",
    "format": "jsonl"
  }'
See Data export guide for details.

Next steps