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.client.capture("upgrade_button_clicked", {
source: "settings",
plan: "monthly",
});capture() is synchronous. It queues the event and returns immediately. This is true even while
the provider is still initializing, so you can capture events from your first render.
Property values are copied when you call capture(), so changing the object afterwards does not
change the event. Values JSON cannot hold are converted: a bigint is sent as a decimal string, a
Date as an ISO string and NaN or Infinity as null. Functions, undefined and circular
references are left out. An event with more than 10,000 property values is dropped and reported
through onDiagnostic as ANALYTICS_EVENT_DROPPED, so pass the fields you need rather than whole
framework objects such as press events.
Each event also records the app version, OS version and locale at the moment you capture it. An event that waits offline across an app update still reports the version it happened in.
Use clear, stable event names. Names that begin with $ are reserved for Voidhash's own events.
Automatic events
The SDK records common lifecycle events for you, so you do not need to capture them yourself:
$app_installed$app_updated$app_opened$app_backgrounded$app_became_active$sign_out$screen, see Track screens
$app_opened marks a launch the customer sees. When the system starts your app in the background,
for example for a silent push or a background fetch, the SDK records nothing for the launch itself
and records $app_opened (with $app_installed or $app_updated for a new release) when the
customer first opens the app, instead of $app_became_active.
To record the app events and $sign_out yourself, set automaticLifecycleEvents to false in
the client options. The SDK then stops capturing them.
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:
const sessionId = voidhash.client.getSessionId();Flush before a boundary
Events flush automatically in batches. A batch is sent when it reaches 20 events or every 5 seconds, whichever comes first, and failed requests are retried with exponential backoff (each retry waits longer than the last). Force delivery when the app is about to cross an important boundary, such as a screen the customer may not return from:
await voidhash.client.flush();You do not need to flush around identity changes. The identity methods flush at the correct time
on their own, so events captured before identify() or signOut() stay attached to the previous
identity.
Update customer traits
Trait updates are not analytics events. They go to the customer's profile through a queue of their own that is never trimmed to make room for events. See Set customer attributes for how to set them.
Event capture API
Your backend, and any client that is not a mobile app, can send single events or batches through the Event Capture API. Authenticate those requests with the project's publishable token in the request body. Never send a secret key to the ingestion endpoint.
Analytics consent
When an end user declines usage analytics, or has not decided yet, tell the SDK:
await voidhash.client.setAnalyticsConsent("denied"); // "granted", "denied" or "pending"
const consent = voidhash.client.getAnalyticsConsent();denied 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
setPersonAttributesSync()answers with theskippedstatus. - 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 "granted", so existing
apps keep collecting analytics. Set it to "pending" when your app asks for consent first.
setAnalyticsConsent() returns an error result with FAILED_TO_SET_ANALYTICS_CONSENT when the
choice applies but could not be stored on the device. 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.