VoidhashDocs
DocumentationAPI reference
Dashboard
Get started
Introduction
Quickstart
Setup
Setup overview
Create a project
Environments
Products and perks
Paywall locations
CLI
Paywall components
Components
Develop a component
Editor panels
Component definition
Publishing
Installing and capabilities
SDKs
Swift
Kotlin
React Native
Web
Store setup
Connect the App Store
Connect Google Play
Connect Stripe
Record payments from other sources
API libraries
Node.js
PHP
Rust
Go
Resources
MCP server
Ad measurement
Meta ads
Migrate in observer mode
Delete a person's data

Ad measurement

How Voidhash measures the visits, signups, installs, trials, payers and revenue your ads bring in, on the web and in your apps.

Voidhash measures what the people an ad or tracked link brought in went on to do, from your own data: visits, signups, app installs, trials, payments and revenue. You can compare these numbers with what your ad platform reports.

Web-only, app-only and web-to-app journeys are measured the same way. Voidhash never assumes an install or a trial: if your product is a web app, the report has no install step, and if you sell without trials, it has no trial step.

What you need

  • The web SDK on your landing pages and web app. It records where each visitor came from and sends page views.
  • identify with your own user ID wherever people sign up or log in: on the web and in your apps. The same user ID links a person's web visit, app install and purchases.
  • Campaign parameters on your ad links, such as the recommended Meta URL parameters, so visits carry the campaign, ad set and ad IDs.

Definitions

TermMeaning
SurfaceWhere something happened: web, ios or android, from the SDK that reported it.
TouchA visit through a link with campaign parameters, such as an ad click, or the store or ad attribution your app reads when it is installed. It carries an ad network when it came from an ad; otherwise it is organic.
Attribution windowHow many days before a signup a touch may come and still get credit for it: 1 to 90 days, 7 by default, set per project.
VisitA web session whose first page view came through a link with campaign parameters.
SignupThe first time a person is identified with your user ID, on any surface. A person who buys or starts a trial before creating an account counts as a signup from that moment.
People acquiredPeople who signed up and whose signup is credited to a touch: their last ad touch before the signup within the attribution window, else their last other touch in that time. Other anonymous visitors count only as visits.
Returning peoplePeople a touch reached after they had already signed up, such as customers reached by retargeting. A touch never gets credit for an earlier signup.
Returning revenueWhat returning people bought within the attribution window after that touch, and trial conversions, less their refunds. Renewals of what they already had do not count. It is reported on its own, never as part of revenue or return on ad spend, since the same sale can count for the ad that first acquired them.
UnattributedPeople who signed up in the range without a credited touch, such as app users who never visited your website, or visitors whose touches all came too long before their signup.
App installAn acquired person your iOS or Android app reported after they arrived from an ad, including people who signed up on the web and then opened the app. This is measured by Voidhash, not reported by the ad platform.
PayerA person who made a paid charge with any payment provider. A free trial is not a charge.
Payment surfaceStripe and other web payment sources are web, the App Store is ios, Google Play is android.
Cross-surfaceA person who signed up in an app after a web visit, signed up or paid on the web after arriving through an app store's or ad platform's install attribution, or paid on a different surface than the one they signed up on.
Key conversionThe conversion your ads are judged by: signup, trial or payer (the default), set per project.

A person's acquisition date is the day of the touch their signup is credited to. If a visitor clicks one ad, then another one two days later and signs up, the second ad gets the signup. An ad wins over a later newsletter or other link without an ad network within the window, so the newsletter gets the signup only if no ad was clicked in that time. If none of their touches falls in the attribution window before the signup, the signup is unattributed. Once a signup is credited, everything the person pays later, renewals included, belongs to that ad, wherever they pay: a person who clicks an ad on the web and subscribes in your iOS app counts toward that ad.

Set the attribution window in Studio, next to the key conversion on the Ads overview, or with the project settings API. Reports read it when they run, so a change applies to past days too. A report request can also set attributionWindowDays to compare windows.

Reports

  • The acquisition report groups people by network, campaign, ad set, ad or surface. It includes a funnel from visits to payers by surface, where payers paid, and how much of your revenue can be traced to a touch.
  • Acquisition cohorts show, for each week of acquisitions, the cumulative signups, payers and revenue by day 0, 1, 3, 7, 14, 30, 60, 90, 180, 365 and 730. A cell is maturing while some people of the week have not reached that day yet. Divide a cell's net revenue by the week's spend for return on ad spend.
  • Subscription revenue keeps arriving long after day 90, so cohorts also show projected revenue for the days their people have not all reached, and Ads cohorts a projected return on ad spend and the chance that each campaign pays back by day 365. See projected revenue.
  • Revenue that can't be traced to a touch is split by why: the person has no touch at all (no_touch), their touches were too long before they signed up, or came after (outside_window), or the revenue has no person (no_person).
  • Tracking health lists the websites that sent page views in the last seven days, the app SDK versions that sent events, and how many of the people your apps saw for the first time that week signed up or arrived on the web first.

Ask people how they heard about you

Ads and links can't explain everyone: a podcast, a friend or a billboard leaves no click. To learn where your unattributed people came from, ask them, for example with a "How did you hear about us?" question at signup, and save the answer as the person trait $acquisition_source:

voidhash.setPersonAttributes({ $acquisition_source: "Podcast" });

The acquisition report's selfReported lists the 20 most common answers among the unattributed people of the range, lowercased and trimmed, with their people and net revenue. An answer fewer than 2 people gave is left out, so one-off free text can't identify someone through this report. People already credited to a touch are left out, and the list is absent from reports filtered to a network, campaign, ad set or ad, and from reports by surface.

Projected revenue

A projection is what a cohort has earned so far plus what its people are expected to pay by that day, learned from your project's own history:

  • Subscribers whose subscription renews are expected to keep renewing at their own price, as often as subscribers of the same plan (billing period and store) have renewed before. Subscribers who turned off renewal are not expected to pay again.
  • People on a trial are expected to convert as often as earlier trials of the plan did, more often the closer the trial is to its end. Trials that won't renew are not expected to convert.
  • People who have not paid yet are expected to make a first payment as often as earlier people did at the same time after signing up.

Each projection comes with a range: the actual revenue is expected to fall inside it 8 times out of 10. Projections need history. They appear once a plan, or all plans with the same billing period together, have 50 renewals or cancellations to learn from. A plan with too few renewals learns from the other plans with the same billing period. If neither has enough, its renewals are left out and the projection is marked partial: it is likely too low. The cohorts report lists which plans projections learned from.

Check lift with a geographic holdout

Attribution says which ad gets credit for a sale, not whether the sale needed the ad. To check that, stop showing ads somewhere and see whether revenue there falls:

Choose regions to pause

Pick countries or regions that normally behave alike, and keep some running as the comparison. Spend by country is in the Ads breakdown, for the last 28 days: Ads → Campaigns → Breakdown → Country, or "breakdown": "country" in the performance report.

Pause spend there

Exclude those regions from your campaigns in your ad platform for a few weeks, long enough to cover your usual time from click to purchase. Voidhash only reads your ads, so this happens in the platform.

Compare total revenue

Compare each group's total revenue before and during the pause, not the revenue attributed to ads: the question is what happens without them. If paused regions fall by less than the spend you saved would have earned, the ads were getting credit for sales that would have happened anyway.

For revenue by country, the transactions table in VoidQL has each App Store and Google Play charge's storefront country. The stores write it differently, DEU on the App Store and DE on Google Play, so name both, such as country IN ('DEU', 'DE', 'FRA', 'FR'). Stripe charges have none, so for web revenue use your payment provider's own reports. It's a comparison between regions, so a promotion or a seasonal change in one of them can look like lift.

Things to know

  • Revenue counts from production purchases unless you ask for another environment (sandbox or test), and is reported net of refunds where the report says so.
  • Recent cohorts look smaller: people acquired recently have had less time to pay. Use a revenue window, such as 7 days, to compare cohorts fairly.
  • A person who used your app before logging in is linked from their first login, so an install is dated by that login.
  • A person who buys without an account and creates one later counts from their first purchase, so the ad that led to the purchase keeps the signup and their revenue, however much later the account comes. A person who signed up before paying counts from the signup.
  • Web touch times come from the device, so Voidhash puts them on its own clock when they are recorded: a phone set to the wrong time still gets its signup credited to the right touch. A store or ad platform's touch keeps the time that store or platform gave.
  • People who signed up before October 2026 have their signup dated by the first time they were identified, with the surface unknown, and their app use is recorded from their next visit to your app.
  • The funnel shows the steps your project has had in the last 90 days, such as installs once your app reports users and trials once anyone starts one, even when nobody in the selected days reached them.
PreviousMCP serverNextMeta ads

On this page

What you needDefinitionsReportsAsk people how they heard about youProjected revenueCheck lift with a geographic holdoutChoose regions to pausePause spend thereCompare total revenueThings to know