Events
What Voidhash records for analytics, which events are built in, and how to choose which ones a project stores.
An event is one thing a person did or that happened to them, recorded for analytics, such as opening the app, viewing a paywall or finishing a checkout. Analytics reports, funnels, A/B test results and alerts are all built from events.
Every event has:
| Field | Meaning |
|---|---|
event | The name, such as $app_opened or checkout_started. |
properties | Flat key-value data about this event. |
distinct_id | The distinct ID of the person it belongs to. |
timestamp | When it happened, by the device clock. |
uuid | A unique ID. An event resent with the same uuid is stored once. |
session_id | The session it happened in, when the SDK knows it. |
Voidhash corrects each timestamp for the device's clock offset and clamps it to at most 24 hours before and 5 minutes after the event arrives.
Built-in events
Names starting with $ are reserved for events the SDKs and Voidhash record on their own.
| Event | Recorded when | Sent by |
|---|---|---|
$app_installed | The app launches for the first time after install | Mobile SDKs |
$app_updated | The app launches for the first time after a version change | Mobile SDKs |
$app_opened | The app cold-starts | Mobile SDKs |
$app_backgrounded | The app moves to the background | Mobile SDKs |
$app_became_active | The app returns to the foreground | Mobile SDKs |
$app_crashed | The app crashed; sent at the next launch, without a stack trace | Mobile SDKs |
$screen | A screen is viewed | Mobile SDKs |
$pageview | A page is viewed | Web SDK |
$pageleave | A page is left | Web SDK |
$outbound_click | A link to another site is clicked | Web SDK |
$sign_out | The person signs out | All SDKs |
$push_opened | A push notification sent through Voidhash is opened | Mobile SDKs |
$paywall_viewed | A paywall is shown | Paywalls |
$paywall_purchase_started | A purchase starts from a paywall | Paywalls |
$paywall_close_intercepted | A paywall's close was intercepted | Paywalls |
$experiment.exposed | A person is assigned an A/B test variant | Voidhash |
$purchase.*, $subscription.* | Revenue: purchases, renewals, cancellations, refunds and more | Voidhash |
$set | Person attributes change | All SDKs |
$identify | An anonymous ID is linked to a user ID; needs $previous_distinct_id | Event capture API |
Mobile SDKs are Swift, Kotlin and React Native. Revenue events come only from Voidhash's own records of store events; the same names sent from a client are dropped.
Custom events
Record your own events for anything the built-in ones don't cover:
await voidhash.capture("checkout_started", properties: ["plan": .string("pro_monthly")])voidhash.capture("checkout_started", mapOf("plan" to "pro_monthly"))voidhash.client.capture("checkout_started", { plan: "pro_monthly" });voidhash.track("checkout_started", { plan: "pro_monthly" });- Use
snake_casenames in the form object-action, such ascheckout_started, and keep them stable: reports match names exactly. - Don't start a name with
$. The SDKs send it, but Voidhash drops it. - Property values are strings, numbers, booleans or
null. - Never put personal data you don't need, such as passwords or payment details, in properties.
Sessions
A session groups a person's events from one visit. It ends after 30 minutes without activity, and a new one starts on sign-out. On the web, tabs of the same site share a session.
How events reach Voidhash
- Client SDKs queue events and send them in batches of 20, every 5 seconds, holding at most 1,000 events; the oldest are dropped first when the queue is full. See the analytics page of Swift, Kotlin, React Native or Web.
- Your backend sends events with a server library or the
event capture API, up to 100 events and 5 MB per request. Reuse an event's
uuidwhen you retry, so it is stored once.
Each event lands in the environment of the key that sent it:
a test key records test events, and a production key records production events, including those
from store sandbox builds.
Consent
While analytics consent isn't granted, the SDKs collect no events, screens or attributes and drop
any still queued. Purchases, access checks, identify, feature flags and paywalls keep working. Set
consent with setAnalyticsConsent; see your SDK's analytics page and
Web privacy.
Choose which events to store
Each project decides which events it stores, under Settings → Events in Studio:
- Built-in events can be turned off one by one or by group, such as Revenue. Turning off Revenue stops all revenue, subscription and churn reporting, and turning off experiment exposure breaks A/B test results.
- Custom events are all stored unless you block a name, such as
checkout_started.
On Voidhash Cloud every built-in event is on by default. Self-hosted projects start with only
$app_installed, Revenue and $set on. Changing the policy needs an admin or owner; from the
terminal, use voidhash events policy (view, enable, disable, block, unblock), or the MCP
tools get_ingest_policy and set_event_ingest.
Voidhash drops a turned-off or blocked event when it arrives and counts it in the request's
rejected total; the request still succeeds, so SDKs don't retry it. An event already in
processing when you change the policy is rejected with the reason policy_rejected. Dropped
events aren't stored and can't be recovered.
Check that events arrive
voidhash events tail follows a project's events live from the terminal, and Studio lists the
latest ones under Activity → Analytics events. Studio's Data health
page shows missing and rejected events; Event capture
lists the limits and every rejection reason.
Next steps
- Check your data: find missing and rejected events.
- People and identity: how events join a person.
- Track events from your SDK: Swift, Kotlin, React Native or Web.
- Event capture API: send events from your backend.
Something wrong or missing on this page? Tell us by email or on Discord.