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

# Revenue

> Record payments, refunds, and disputes from Stripe or your server, see net revenue and MRR, and link revenue to the people in product analytics.

<Note>
  Revenue is part of product analytics, which is coming soon. A project can use it only when product
  analytics is available for the project. See [Product analytics](/features/product-analytics).
</Note>

Revenue records each money movement of your business: payments, refunds, and disputes. It shows
gross and net revenue, refunds, paying and new customers, and monthly recurring revenue (MRR) in one
reporting currency. When a payment carries the user id of the buyer, the revenue links to the same
person in product analytics.

## Send revenue to Squasher

Use one of two sources, or both for different payments:

* **Stripe.** Connect a restricted read-only key and add a webhook endpoint. Squasher reads the
  payments of the last 13 months, then records new ones as Stripe sends them. See
  [Stripe](/integrations/stripe).
* **Your server.** Send payments, refunds, and subscription states from your billing system with
  the API or the CLI. See [Send revenue from your server](#send-revenue-from-your-server).

Do not send the same payment from both sources: each source counts its own payments.

## How Squasher counts revenue

* Each payment, refund, dispute withdrawal, and dispute reversal is one row. A second delivery of
  the same Stripe event, or a second send of the same id, changes the row instead of adding one.
* Refunds and disputes are their own negative rows, on the date they were processed, not the date
  of the payment. A refund that fails or is canceled does not count.
* **Gross** is the sum of payments. **Net** is gross minus refunds and disputes, plus dispute
  reversals.
* A **paying customer** made a payment in the range. A **new customer** made the first payment in
  the range.
* A Stripe subscription payment counts once: Squasher reads the charge, and the invoice only adds
  the plan, the subscription, and whether the payment is a renewal.

## MRR

MRR comes from subscription states. For each subscription, Squasher keeps the monthly amount from
each time it changed: the price times the quantity, scaled to a month (a yearly price is divided by
12\), minus a percent discount. A subscription that is trialing, paused, unpaid, or canceled has an
MRR of 0. When Squasher reads past Stripe subscriptions, a canceled subscription gets MRR for its
past only when its latest invoice shows a paid period, so a trial canceled before it paid never
counts as MRR or churn.

For a range, Squasher compares each customer's MRR at the start and at the end:

| Movement | Meaning |
| - | - |
| New | No MRR at the start, and never before. MRR at the end. |
| Reactivation | No MRR at the start, but MRR before that. MRR at the end. |
| Expansion | MRR at the start, and more MRR at the end. |
| Contraction | MRR at the start, and less (but not zero) MRR at the end. |
| Churn | MRR at the start, and no MRR at the end. |

Net new MRR is new plus expansion plus reactivation, minus contraction and churn.

## Reporting currency

Squasher keeps each amount in its own currency. Reports convert amounts to the project's reporting
currency (USD by default) with the euro foreign exchange reference rates that the European Central
Bank publishes each working day. A payment uses the rate of its own date; a weekend or holiday uses
the rate of the working day before. MRR uses the rate of the last day of the range.

A currency without a reference rate stays in the breakdown by currency in its own amounts, and the
report lists it under `missing_currencies`. It is not counted in converted totals.

To change the reporting currency:

```bash theme={null}
squasher revenue set-currency --project "$SQUASHER_PROJECT_ID" EUR
```

## Link revenue to people

Revenue links to a person in product analytics when the payment carries the user id that you pass
to `identify()` in the [Browser SDK](/sdks/browser#product-analytics-and-consent). With Stripe, use
one of these:

* Set `client_reference_id` of the Checkout Session to the user id.
* Set the metadata key `squasher_distinct_id` to the user id on the Checkout Session, the customer,
  the subscription, or the payment. Use `squasher_anonymous_id` for a visitor who has not signed
  in.

A link on the customer also applies to the customer's earlier and later payments. Refunds and
disputes take the person of their payment. The setup check shows how much revenue has a link.

Squasher never links revenue by email, IP address, name, or device fingerprint.

## Send revenue from your server

Send up to 100 money movements in one request. `amount` is positive, in the smallest unit of the
currency (cents for USD, yen for JPY); `type` sets the sign. Use your own id for each movement:
sending the same id again replaces it.

```bash theme={null}
curl -X POST "https://api.squasher.ai/v1/projects/$SQUASHER_PROJECT_ID/revenue/events" \
  -H "x-squasher-key: $SQUASHER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      { "id": "order_1001", "type": "payment", "amount": 4900, "currency": "USD",
        "occurred_at": "2026-10-12T09:30:00Z", "customer_id": "cust_42",
        "distinct_id": "user_42", "plan": "Pro" },
      { "id": "refund_77", "type": "refund", "amount": 900, "currency": "USD",
        "occurred_at": "2026-10-12T11:00:00Z", "parent_id": "order_1001" }
    ]
  }'
```

For MRR, send subscription states with the monthly amount from a time on:

```bash theme={null}
squasher revenue send-subscriptions --project "$SQUASHER_PROJECT_ID" --file subscriptions.json
```

The API key needs the `revenue:write` permission. See [Revenue API](/api-reference/revenue).

## Revenue impact

Issue, incident, and release pages show the revenue at risk, and the revenue API returns it for a
slow page too. It is an estimate, shown as a range, never an exact loss.

* **Issues and slow pages (affected people).** Squasher finds the browser sessions with the error
  (or with a poor LCP, INP, or CLS on the page) in the last 30 days. It links those sessions to
  identified people through product analytics and adds the net revenue of those people in the same
  30 days. That is the low end. The high end scales the low end to all affected sessions, at most 5
  times, and never above the project's net revenue.
* **Incidents and releases (baseline).** Squasher compares the net revenue in the first 7 days
  (or less) of the incident or release with the same time in each of the four weeks before. The
  range runs from the shortfall against the lowest week to the shortfall against the highest week.
  Revenue that is higher than before shows as zero.

The estimate needs identified people for issues and pages: call `identify` with your user id, and
put the same id on the payment. See [Link revenue to people](#link-revenue-to-people).

```bash theme={null}
squasher revenue impact --project "$SQUASHER_PROJECT_ID" --target page /checkout
```

## Revenue attribution

The Revenue page shows net revenue by the source, medium, or campaign that brought each paying
person. The revenue API returns the same table.

A **touch** is a consented page view or event whose page address has UTM parameters or an ad click
ID (`gclid`, `fbclid`, `msclkid`, `ttclid`, `li_fat_id`, `twclid`), or that comes from another site.
Squasher reads touches from 90 days before the range to its end, and links them to people in the
same way as product analytics: the user id of `identify`, else the visitor. An `identify` call up
to 400 days before the range still links a later visit of the same visitor.

* **Source**: `utm_source`; else the ad network of the click ID (for example `google` for `gclid`);
  else the referrer host.
* **Medium**: `utm_medium`; else `cpc` for a click ID; else `referral` for another site.
* **Campaign**: `utm_campaign`.

A value that a touch does not have shows as `(not set)`. Links from other hosts of your own site,
and returns from a hosted checkout (Stripe, PayPal, Paddle), are not touches.

| Model | Credit |
| - | - |
| Last touch (default) | Each payment goes to the person's newest touch before it. |
| First touch | Each payment goes to the person's first touch, if it was before the payment. |

Revenue of a person with no touch before the payment shows as **direct**. A payment without an
analytics id (no `distinct_id` or `anonymous_id`) shows as **not linked**. Refunds and disputes
count against the touch of their own date, so the net of each row is correct.

```bash theme={null}
squasher revenue attribution --project "$SQUASHER_PROJECT_ID" --model first_touch --dimension campaign
```

Touches come only from consented product analytics. Without consent the SDK sends nothing. An
event that the SDK holds while consent is pending is sent after the grant without its UTM values,
click IDs, referrer, and query parameters, so a visit before consent is never a touch. That revenue shows as direct
or not linked.

## Check the setup

```bash theme={null}
squasher revenue status --project "$SQUASHER_PROJECT_ID"
```

The check shows whether the Stripe key works, whether webhook events arrive, whether past payments
are read, how many rows are recorded, whether reports are up to date, and how much revenue links to
people. Each check says what to do next.

## Privacy

Squasher receives amounts, currencies, dates, plan names, and the provider's customer and object
ids. It does not receive card data, names, emails, or addresses from Stripe. When you delete a
person in product analytics, Squasher removes the person's link from their revenue rows and keeps
the amounts, so totals stay correct.

## Plans and usage

Each revenue row counts as one product event. A repeated delivery of the same row does not count
again. See [Plans and billing](/features/plans-and-billing).

## Next steps

<CardGroup cols={2}>
  <Card title="Connect Stripe" icon="https://mintcdn.com/nextbigthingllc/Fg5aclMlZNSrg_Y3/icons/entity/connection.svg?fit=max&auto=format&n=Fg5aclMlZNSrg_Y3&q=85&s=d0b3ee40d988fcded3129f8427d8e582" href="/integrations/stripe" width="24" height="24" data-path="icons/entity/connection.svg">
    Read payments, refunds, disputes, and subscriptions from Stripe.
  </Card>

  <Card title="Revenue API" icon="https://mintcdn.com/nextbigthingllc/Fg5aclMlZNSrg_Y3/icons/docs/api.svg?fit=max&auto=format&n=Fg5aclMlZNSrg_Y3&q=85&s=ad72f5da1d92bd1c152340d2b13ad254" href="/api-reference/revenue" width="24" height="24" data-path="icons/docs/api.svg">
    Read revenue and MRR, and send revenue from your server.
  </Card>
</CardGroup>


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