Skip to content
Tolinku
Tolinku
Sign In Start Free
Analytics & Attribution · · 5 min read

Attribution for Subscription Apps: Revenue Tracking

By Tolinku Staff
|
Tolinku mobile attribution dashboard screenshot for analytics blog posts

Subscription apps have a unique attribution challenge: the install is free, the value comes from ongoing payments. A user acquired from a Facebook ad who installs, starts a free trial, converts to paid, and stays subscribed for 18 months is worth far more than one who installs, starts a trial, and cancels. Attribution needs to track this entire lifecycle, not just the install.

This guide covers attribution for subscription apps. For gaming app attribution, see attribution for mobile gaming apps. For attribution models, see last-click vs multi-touch attribution.

The Subscription Attribution Funnel

Ad Click → Install → Onboarding → Trial Start → Trial Active → Trial-to-Paid → Renewal → Churn

Each step needs to be tracked and attributed back to the acquisition source:

Event Attribution Question
Install Which campaign drove the install?
Trial start Which channel's users start trials at the highest rate?
Trial-to-paid Which source has the best trial conversion rate?
Renewal (Month 2+) Which source retains subscribers longest?
Churn Which source has the highest churn rate?
LTV (12 months) Which channel delivers the highest lifetime value?

Key Metrics by Acquisition Source

Funnel Metrics

Metric Definition Why It Matters
Install-to-trial rate Trial starts / installs Onboarding effectiveness by source
Trial-to-paid rate Paid conversions / trial starts Quality of acquired users
Day 0 paid conversion Users who paid immediately (no trial) High-intent user percentage
Free-to-paid rate Paid conversions / free installs Overall conversion by source

Revenue Metrics

Metric Definition Why It Matters
ARPPU (Average Revenue Per Paying User) Revenue / paying users Revenue quality
LTV (Lifetime Value) Total revenue per user over lifetime True user value
LTV:CAC ratio LTV / cost per acquisition Campaign sustainability
Payback period Months until CAC is recovered Cash flow
ROAS (30/60/90/365 day) Revenue / ad spend at each window Time-boxed ROI

Retention Metrics

Metric Definition Why It Matters
Month 1 retention % still subscribed after 1 month Early churn indicator
Month 3 retention % still subscribed after 3 months Medium-term quality
Month 12 retention % still subscribed after 12 months Long-term value
Churn rate by source Monthly churn rate per acquisition channel Channel quality

Tracking Subscription Events

Server-Side Event Tracking

Track subscription events on your server using App Store and Google Play server notifications:

// Apple App Store Server Notifications V2
app.post('/webhooks/apple', async (req, res) => {
  const notification = decodeAppleNotification(req.body);

  switch (notification.notificationType) {
    case 'SUBSCRIBED':
      await trackEvent(notification.originalTransactionId, 'trial_start', {
        productId: notification.productId,
        price: notification.price
      });
      break;

    case 'DID_RENEW':
      await trackEvent(notification.originalTransactionId, 'renewal', {
        productId: notification.productId,
        price: notification.price,
        renewalNumber: notification.renewalCount
      });
      break;

    case 'EXPIRED':
    case 'DID_FAIL_TO_RENEW':
      await trackEvent(notification.originalTransactionId, 'churn', {
        reason: notification.expirationReason
      });
      break;

    case 'DID_CHANGE_RENEWAL_STATUS':
      if (!notification.autoRenewStatus) {
        await trackEvent(notification.originalTransactionId, 'cancellation_intent', {});
      }
      break;
  }

  res.sendStatus(200);
});

// Google Play Real-Time Developer Notifications
app.post('/webhooks/google', async (req, res) => {
  const notification = decodeGoogleNotification(req.body);

  switch (notification.subscriptionNotification.notificationType) {
    case 4: // SUBSCRIPTION_PURCHASED
      await trackEvent(notification.purchaseToken, 'subscription_start', {
        productId: notification.subscriptionId
      });
      break;

    case 2: // SUBSCRIPTION_RENEWED
      await trackEvent(notification.purchaseToken, 'renewal', {
        productId: notification.subscriptionId
      });
      break;

    case 13: // SUBSCRIPTION_EXPIRED
      await trackEvent(notification.purchaseToken, 'churn', {});
      break;
  }

  res.sendStatus(200);
});

Linking Subscriptions to Attribution

Connect subscription events to the original attribution source:

interface UserAttribution {
  userId: string;
  installSource: string;       // 'facebook', 'google', 'organic'
  campaignId?: string;
  installDate: Date;
  subscriptionEvents: SubscriptionEvent[];
}

async function getAttributedRevenue(
  source: string,
  dateRange: DateRange
): Promise<RevenueReport> {
  const users = await db.query(`
    SELECT
      u.install_source,
      u.campaign_id,
      COUNT(DISTINCT u.user_id) AS users,
      COUNT(DISTINCT CASE WHEN s.event = 'trial_start' THEN u.user_id END) AS trials,
      COUNT(DISTINCT CASE WHEN s.event = 'subscription_start' THEN u.user_id END) AS paid,
      SUM(CASE WHEN s.event IN ('subscription_start', 'renewal') THEN s.revenue ELSE 0 END) AS total_revenue
    FROM users u
    LEFT JOIN subscription_events s ON u.user_id = s.user_id
    WHERE u.install_source = ?
      AND u.install_date BETWEEN ? AND ?
    GROUP BY u.install_source, u.campaign_id
  `, [source, dateRange.start, dateRange.end]);

  return users;
}

LTV Calculation by Source

Cohort-Based LTV

Calculate LTV by acquisition cohort and source:

async function calculateLTV(
  source: string,
  cohortMonth: string,
  daysAfterInstall: number
): Promise<number> {
  const result = await db.query(`
    SELECT
      AVG(cohort_revenue) AS avg_ltv
    FROM (
      SELECT
        u.user_id,
        SUM(CASE
          WHEN s.event_date <= u.install_date + INTERVAL ? DAY
          THEN s.revenue
          ELSE 0
        END) AS cohort_revenue
      FROM users u
      LEFT JOIN subscription_events s ON u.user_id = s.user_id
      WHERE u.install_source = ?
        AND DATE_FORMAT(u.install_date, '%Y-%m') = ?
      GROUP BY u.user_id
    ) cohort
  `, [daysAfterInstall, source, cohortMonth]);

  return result[0].avg_ltv;
}

LTV Curves

Plot LTV over time for each acquisition source:

Source Day 0 LTV Day 30 LTV Day 90 LTV Day 365 LTV
Facebook $0.00 $4.50 $12.00 $38.00
Google Search $0.00 $5.20 $14.50 $45.00
TikTok $0.00 $3.80 $9.50 $28.00
Organic $0.00 $6.00 $16.00 $52.00

Google Search users have the highest paid LTV (organic is higher but has no acquisition cost). TikTok users have the lowest LTV, suggesting lower-quality traffic for this app type.

Trial Optimization by Source

Trial Configuration Testing

Different acquisition sources may respond differently to trial configurations:

Source 7-Day Trial Conversion 14-Day Trial Conversion No Trial Conversion
Branded search 45% 42% 30%
Social (Meta) 25% 30% 12%
Display 18% 22% 8%

Social users benefit more from longer trials (they need more time to experience the product), while search users already have intent and convert well with shorter trials or no trial at all.

SKAdNetwork and Subscription Events

On iOS, SKAdNetwork limits what you can report back to ad networks. For subscription apps, use the conversion value to encode subscription events:

// SKAN 4.0 conversion value scheme for subscription apps
func updateSKANConversion(event: SubscriptionEvent) {
    var fineValue: Int = 0

    switch event {
    case .install:
        fineValue = 0
    case .trialStart:
        fineValue = 10
    case .trialDay3Active:
        fineValue = 20
    case .trialDay7Active:
        fineValue = 30
    case .paidConversion(let tier):
        switch tier {
        case .monthly: fineValue = 40
        case .annual: fineValue = 50
        case .premium: fineValue = 60
        }
    }

    SKAdNetwork.updatePostbackConversionValue(fineValue, coarseValue: .high) { error in
        if let error = error {
            print("SKAN update failed: \(error)")
        }
    }
}

Tolinku for Subscription Attribution

Tolinku's analytics track deep link clicks and attribute conversions, including subscription events. When a user clicks a Tolinku deep link and later subscribes, the conversion is attributed to the original click source. Configure subscription event tracking in the Tolinku dashboard.

For mobile attribution, see mobile attribution: a developer's guide. For gaming app attribution, see attribution for mobile gaming apps.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.