Trial, paying and subscription value
How Atune knows whether an account is a trial or a paying customer, what it pays, and how downgrades become churn signals.
Atune separates trials from paying customers because they need different help: a trial needs to reach value before it ends, a paying customer needs to keep getting value. Several signals, such as an account going quiet or missing a feature that keeps customers, only apply to paying accounts.
Atune doesn't guess who pays
Only your app knows who pays. Atune does not infer it from behavior.
- An account is shown as a paying customer only when your app sends
accountStage: "paying"for it. - Until then, every account is shown as a trial. This is a default, not a conclusion. The account page says Assumed next to the stage, and the Accounts list shows a notice when your app has not sent a stage for any account.
Send the stage with your events
Add the optional lifecycle fields to the events you already send to
POST /api/v1/events. Send them on every event, or at
least whenever they change. Invalid values are ignored and never reject the
event.
{
"accountId": "acme",
"userId": "u_42",
"event": "report.generated",
"accountStage": "paying",
"accountPlan": "Pro",
"accountSeats": 10,
"accountMrr": 490,
"accountCurrency": "NOK"
}| Field | Values | Meaning |
|---|---|---|
accountStage | "trial" or "paying" | Where the account is in its lifecycle. |
trialEndsAt | ISO 8601 date | The real end of the trial. Enables the trial-expiring signal. |
accountPlan | Text, up to 100 characters | The plan name in your product, e.g. "Pro". |
accountSeats | Positive integer | Seats the account pays for. Enables the underseated (upsell) signal. |
accountMrr | Number ≥ 0 | What the account pays per month, in major units (49.5 = 49.50). 0 is kept, e.g. a free plan. |
accountCurrency | ISO 4217 code | Currency of accountMrr, e.g. "NOK" or "USD". |
When the account page says Reported by your app, the stage came from your app on that date.
With the SDK middleware
Return a subscription from getAccount and the middleware sends it with
every captured request:
createAtuneMiddleware({
getAccount: async (request) => {
const session = await getSession(request);
if (!session) return null;
const billing = await getBilling(session.orgId);
return {
accountId: session.orgId,
userId: session.userId,
subscription: {
stage: billing.active ? "paying" : "trial",
plan: billing.planName,
seats: billing.seats,
mrr: billing.monthlyAmount,
currency: billing.currency,
},
};
},
});Downgrades are churn signals
Atune keeps a history of these fields. Each time your app sends a stage, plan, seat count or MRR that differs from the last one, Atune records the change. The first value it receives is the starting point, not a change.
A paying account that lowers its MRR, drops seats, moves to a cheaper plan or goes from paying back to trial gets a Downgraded action in the Inbox for 14 days. Lower spend often comes before churn, so this is the moment to ask what changed. While it is open, Atune does not suggest selling that account more seats.
A plan name has no order, so a plan change counts as a downgrade only when MRR fell in the same update. Amounts in different currencies are not compared.
MRR also raises the priority of an account's actions, so bigger customers come first. The account page shows MRR and the subscription history, and the Accounts list can be sorted by MRR.