Skip to main content
Your platform usage is billed on MTPU — monthly tracked paying users: the number of distinct users who were in a paid state in your app during a calendar month, counted per app. The word that matters is distinct. A user who buys through an AppDNA paywall, renews through RevenueCat and is also reported by your own server is one paying user, counted once.

What already counts, with no code from you

Purchases the SDK can see. If you sell through an AppDNA paywall, or through AppDNA.billing.purchase(...), or you have connected RevenueCat or Adapty, those purchases and renewals already count. There is nothing to switch on and nothing to call — and no way to switch it off, because it is what the meter has always counted.
A connected billing provider is enough on its own: RevenueCat’s INITIAL_PURCHASE and RENEWAL webhooks become purchases in your account, and if the SDK and RevenueCat both report the same purchase, the duplicate is dropped rather than billed twice.

Revenue AppDNA cannot see

Some revenue leaves no trace in the app: a subscription sold on your website, billing run entirely by your own server, a seat a salesperson sold, an existing payments stack you do not route through us. Those users are paying you, and by default they are invisible to the meter. There are two ways to make them visible. Both are agreed with us first — they change what you are billed, so they are part of your agreement rather than a switch in the dashboard.

Option 1 — call reportPayingUser()

Supported on: iOS 1.0.83+ · Android 1.0.55+ · Flutter 1.0.21+ · React Native 1.0.20+. On an older SDK the method does not exist — use option 2, or update.
The documented path. Call it whenever you know the current user is in a paid state:
Every argument is optional — reportPayingUser() with no arguments is a valid assertion that this user pays. productId, priceCents and currency are analytics only: MTPU is a count of users, so the price never changes what you are billed.
Call it on every launch, not on the transition. The meter counts distinct users, so calling it a hundred times in a month costs exactly the same as calling it once — and you cannot miss a user by failing to notice the moment they upgraded. Hosts that try to report only the transition get it wrong; hosts that report the current state cannot.
Call identify() first. The SDK attaches the identity it already holds — there is deliberately no user-id argument, so nobody can report users who are not their own. If you report before identifying, the assertion lands on the anonymous id, and an identify() more than 8 days later will not re-link it: that user counts twice. Before configure() it does nothing. It logs at debug level and returns. A paying-user assertion made before the SDK knows which app it belongs to cannot be attributed to one, and a silently queued bill is worse than a missed one.

Option 2 — have one of your own events count

If your app already emits an event that means “this user pays” — say you have been tracking subscription_activated from your own billing code for years — you do not have to re-instrument anything. We can name that event as a paying-user signal for your app.
Two rules apply, and both exist to keep your invoice defensible:
  • We name the event, not your code. A billing input that depended on whatever string an app happened to send would have no way to tell a typo from a quiet month. The event names that count are recorded against your app, and you can see them on your billing page.
  • It cannot be a platform event. Names AppDNA itself defines — session_start, screen_view, feature_used, purchase_completed and the rest — are refused. Counting session_start as a paying user would bill you for every active user, which is not what MTPU is.

The rules we hold ourselves to

When a new signal starts counting, it counts from that moment. The earlier events are still in your analytics, and they are deliberately not billed: switching something on must never produce a charge for a period you had not agreed to.
Metering stops from that moment. Users already counted that month stay counted, and the month bills what it measured while the signal was on — so your invoice agrees with the usage page you were reading all month.
MTPU is a count of users who were in a paid state during the month, not a sum of money. A mid-month refund does not retract a month you already served. For a user you report yourself, only you can end the assertion — by ceasing to report them.
Your billing page breaks the month’s MTPU into purchases in the app, users your app reported, and your own events — and lists, per app, exactly which signals count and from what date. The parts overlap, because a user seen by two of them is billed once; the total is what you pay.

Which to choose

Both options are part of your commercial terms. Talk to us and we will switch them on for the apps they apply to — and tell you the date they start counting from.