> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appdna.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Who counts as a paying user

> What MTPU counts, how to report revenue AppDNA cannot see, and what each option costs you

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

<Check>
  **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.
</Check>

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()`

<Info>
  **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.
</Info>

The documented path. Call it whenever you know the current user is in a paid state:

<CodeGroup>
  ```swift iOS theme={null}
  AppDNA.reportPayingUser(productId: "pro_monthly", priceCents: 999, currency: "USD")
  ```

  ```kotlin Android theme={null}
  AppDNA.reportPayingUser(productId = "pro_monthly", priceCents = 999, currency = "USD")
  ```

  ```dart Flutter theme={null}
  await AppDNA.reportPayingUser(
    productId: 'pro_monthly',
    priceCents: 999,
    currency: 'USD',
  );
  ```

  ```typescript React Native theme={null}
  await AppDNA.reportPayingUser('pro_monthly', 999, 'USD');
  ```
</CodeGroup>

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.

<Tip>
  **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.
</Tip>

**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.

```swift theme={null}
// Nothing new to call. Your existing event:
AppDNA.track(event: "subscription_activated", properties: ["plan": "pro"])
```

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

<AccordionGroup>
  <Accordion title="Nothing is ever backfilled">
    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.
  </Accordion>

  <Accordion title="Switching something off does not rewrite the month">
    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.
  </Accordion>

  <Accordion title="A refund does not un-count the 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.
  </Accordion>

  <Accordion title="You can always see what counts">
    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.
  </Accordion>
</AccordionGroup>

## Which to choose

| Your situation | Use |
| - | - |
| You sell through an AppDNA paywall | Nothing — it already counts |
| RevenueCat or Adapty is connected | Nothing — purchases and renewals already count |
| Your server owns billing, or you sell on the web | `reportPayingUser()` |
| You already emit a paid-state event and would rather not change the app | Have that event count (option 2) |

<Note>
  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.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.