Capture analytics
Track product events and customer traits with the Voidhash SDK.
The SDK captures product events and sends them to Voidhash for you. Every event is attributed to the current person and delivered in background batches. You do not need a separate analytics package.
Capture an event
Call capture with an event name and any properties you want to attach:
voidhash.capture(
"upgrade_button_clicked",
mapOf("source" to "settings", "plan" to "monthly"),
)Use clear, stable event names. Names that begin with $ are reserved for Voidhash's own events.
The SDK trims the name. An event with a blank name is not captured; the SDK reports it through the
ANALYTICS_EVENT_INVALID diagnostic instead.
Events you capture before initialize() are kept and sent after it. Each one keeps the time it
was captured.
Automatic events
The SDK records common lifecycle events for you, so you do not need to capture them yourself:
$app_installed: the first launch of this install of the app. A reinstall, or a new device restored from a backup, is a new install.$app_updated: the first launch after the app's version or build changed.$app_opened: the first time one of the app's activities comes to the foreground in a process. Android also starts your app's process in the background, for example for a push message, a scheduled job or a broadcast. Such a start records no$app_openeduntil the user brings up an activity.$app_backgrounded: the app's last visible activity stopped.$app_became_active: an activity came back to the foreground after$app_backgrounded, in the same process.$sign_out$screen, see Track screens
$app_installed and $app_updated are always the first events of a launch. Configure Voidhash in
Application.onCreate so that the SDK sees your first activity start.
When the process starts in the background, initialize() does not connect to Google Play or
refresh the project, customer and flag data. The first activity start does both.
Sessions
Every event carries a session id, so screen paths and funnels can tell one visit from the next. A session starts with the first event and ends after 30 minutes without any event, including time spent in the background. The next event then opens a new session. Signing out also starts a new session, and the session survives app restarts within the same 30 minutes. Read the current id when you need to correlate your own data with it:
val sessionId = voidhash.sessionIdFlush before a boundary
Events flush automatically in batches. A batch is sent when it reaches 20 events or every 5
seconds while the app is in the foreground, whichever comes first, and once more when the app goes
to the background. Failed requests are retried with exponential backoff (each retry waits longer
than the last), and the SDK honors the server's Retry-After header. If Voidhash refuses a batch
because one of its events is invalid, the SDK still delivers the other events and drops only the
invalid one. Force delivery when the app is about to cross an important boundary, such as a
screen the customer may not return from:
voidhash.flush()This sends everything that is due: attribute and sign-in updates first, then purchase reports,
then analytics events. If the SDK stopped contacting Voidhash after repeated failures, flush()
tries again at once, and it returns once each queue was tried.
Update customer traits
Trait updates travel through the same pipeline as analytics events. See Set customer attributes for how to set them.
Analytics consent
When an end user declines usage analytics, or has not decided yet, tell the SDK:
voidhash.setAnalyticsConsent(AnalyticsConsent.DENIED) // GRANTED, DENIED or PENDING
val consent = voidhash.analyticsConsentdenied and pending behave the same. While consent is either of them:
- The SDK captures no custom events, screens, app lifecycle events,
$sign_out, or paywall impressions and interactions. Nothing is queued, stored on the device, or sent, and no analytics session is kept.$app_installedand$app_updatedare recorded once consent is granted. - Person attributes are neither sent nor queued, and
setPersonAttributesWithStatusreturnsWriteStatus.SKIPPED. - The first analytics call the SDK skips is reported once as the
ANALYTICS_CONSENT_DENIEDdiagnostic. - Purchases, restores, entitlements, feature flags, and paywalls work as usual.
identifystill links the customer and sends the email and name you pass, because purchases are attributed to them. - Requests to Voidhash report the missing consent in the
x-analytics-consentheader, so Voidhash records no paywall exposure for them and discards any analytics that still arrive.
Withdrawing consent also discards the events still waiting on the device, including a batch that is being sent, and ends the analytics session. Granting it collects from that moment on; nothing skipped in the meantime is sent later.
The analyticsConsent option sets the consent at launch and defaults to AnalyticsConsent.GRANTED,
so existing apps keep collecting analytics. Set it to AnalyticsConsent.PENDING when your app asks
for consent first. setAnalyticsConsent is not a suspending function and applies before it returns.
The choice you set at runtime is stored on the device and takes precedence over the option on later
launches. Signing out and resetting keep it, so ask again only when your own consent flow requires
it.
Revenue reporting comes from purchases and is unaffected. Metrics built from analytics events, such as paywall impressions and conversion rates, count only end users who granted consent.