Develop

In-App Analytics

Updated: Mar 25, 2026
This is a Platform SDK feature requiring Data Use Checkup
To use this or any other Platform SDK feature, you must complete a Data Use Checkup (DUC). The DUC ensures that you comply with Developer Policies. It requires an administrator from your team to certify that your use of user data aligns with platform guidelines. Until the app review team reviews and approves your DUC, platform features are only available for test users.
In-App Analytics lets you track user behavior and app performance within your Meta Quest application. Use it to record timed segments (such as gameplay sessions, menu visits, or tutorials), fire one-off metric events, and aggregate high-frequency counters. All data is sent to the Meta analytics backend for analysis.
The API is organized around three concepts:
  • Segments — Named time intervals that represent a phase of user activity, for example a match, a tutorial, or a menu visit. You open a segment when the phase begins and close it when the phase ends. The platform records the duration automatically.
  • Metric events — Individual data points with a name and numeric value. Use these for discrete measurements such as scores, distances, or action counts.
  • Event counters — Lightweight accumulators for high-frequency metrics. Create a counter, increment it as events occur, and send the aggregated total when you are ready. Counters support auto-flush so you do not have to manage send timing manually.

Prerequisites

Before using In-App Analytics:
  1. Register your app on the Developer Dashboard.
  2. Complete the Platform SDK setup for Kotlin development.

Getting started

Create an instance of the InAppAnalytics class. All methods are Kotlin suspend functions and must be called from a coroutine scope.
import horizon.platform.inappanalytics.InAppAnalytics
import horizon.platform.inappanalytics.InAppAnalyticsException

val inAppAnalytics = InAppAnalytics()

Segments

Segments represent timed phases of user activity. Open a segment at the start of an activity and close it when the activity ends.

Open a segment

try {
    val segment = inAppAnalytics.openSegment("gameplay_round")
    Log.d(TAG, "Segment opened: segId=${segment.segId}, segName=${segment.segName}")
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to open segment: ${e.displayableMessage}")
}

Close a segment

Closing a segment records the elapsed duration automatically.
try {
    val segment = inAppAnalytics.closeSegment("gameplay_round")
    Log.d(TAG, "Segment closed: duration=${segment.durationS}s")
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to close segment: ${e.displayableMessage}")
}

Close all open segments

try {
    val closedSegments = inAppAnalytics.closeAllOpenSegments()
    Log.d(TAG, "Closed ${closedSegments.size} segment(s)")
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to close segments: ${e.displayableMessage}")
}

List active segments

try {
    val segments = inAppAnalytics.getAllSegments()
    for (segment in segments) {
        Log.d(TAG, "Active segment: ${segment.segName}")
    }
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to get segments: ${e.displayableMessage}")
}

Metric events

Send a one-off metric event

For simple measurements, use sendEvent with a metric name and a numeric value:
try {
    inAppAnalytics.sendEvent("player_score", 1500.0f)
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to send event: ${e.displayableMessage}")
}

Queue metric events for batch processing

For events that should be batched before sending, use queueMetricEvent with a MetricEventInput:
import horizon.platform.inappanalytics.enums.MetricType
import horizon.platform.inappanalytics.options.MetricEventInput

try {
    val event = MetricEventInput.builder()
        .withMetricName("lap_time")
        .withValue(62.5f)
        .withMetricType(MetricType.Action)
        .build()
    inAppAnalytics.queueMetricEvent(event)
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to queue event: ${e.displayableMessage}")
}

Queue segment events for batch processing

You can queue detailed segment events with custom metadata using SegmentEventInput:
import horizon.platform.inappanalytics.enums.SegmentEventType
import horizon.platform.inappanalytics.enums.SegmentType
import horizon.platform.inappanalytics.options.SegmentEventInput

try {
    val event = SegmentEventInput.builder()
        .withSegName("tutorial_step_3")
        .withSegType(SegmentType.Tutorial)
        .withEventType(SegmentEventType.Start)
        .build()
    inAppAnalytics.queueSegmentEvent(event)
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to queue segment event: ${e.displayableMessage}")
}

Event counters

Event counters are ideal for high-frequency metrics where you want to accumulate a total before sending. By default, counters auto-flush after a timeout so you do not need to manage send timing.

Create and use a counter

try {
    // Create a counter (starts at value 1)
    inAppAnalytics.createEventCounter("enemies_defeated")

    // Increment as events occur
    inAppAnalytics.incrementEventCounter("enemies_defeated", 1.0f)
    inAppAnalytics.incrementEventCounter("enemies_defeated", 3.0f)

    // Check the current value
    val counter = inAppAnalytics.getEventCounter("enemies_defeated")
    Log.d(TAG, "Current count: ${counter.value}")

    // Send the counter as a metric event (removes it from tracking)
    inAppAnalytics.sendEventCounter("enemies_defeated")
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Counter error: ${e.displayableMessage}")
}

Manual flush mode

Pass manualFlush = true when creating a counter to disable auto-flush. You are then responsible for calling sendEventCounter explicitly:
inAppAnalytics.createEventCounter("coins_collected", manualFlush = true)

List tracked counters

try {
    val counterNames = inAppAnalytics.getAllEventCounterNames()
    for (name in counterNames.names) {
        Log.d(TAG, "Tracking counter: $name")
    }
} catch (e: InAppAnalyticsException) {
    Log.e(TAG, "Failed to get counter names: ${e.displayableMessage}")
}

API reference

MethodDescriptionReturns
openSegment(segName)
Opens a named segment and records a START event.
SegmentEvent
closeSegment(segName)
Closes a named segment and records an END event with duration.
SegmentEvent
closeAllOpenSegments()
Closes all active segments.
List<SegmentEvent>
getAllSegments()
Returns all currently active segments without closing them.
List<SegmentEvent>
sendEvent(metricName, value)
Sends a single metric event immediately.
Void
queueMetricEvent(event)
Queues a metric event for batch processing.
Void
queueSegmentEvent(event)
Queues a segment event for batch processing.
Void
createEventCounter(counterName, manualFlush?)
Creates a counter with an initial value of 1.
MetricEvent
incrementEventCounter(counterName, amount)
Increments an existing counter by the given amount.
MetricEvent
sendEventCounter(counterName)
Sends the counter value and removes the counter.
MetricEvent
getEventCounter(counterName)
Gets the current counter value without sending.
MetricEvent
getAllEventCounterNames()
Returns the names of all tracked counters.
EventCounterNames
All methods are suspend functions and throw InAppAnalyticsException on failure.