Server-to-Server - Mobile App S2S Guide

Overview

This guide covers the mobile-specific attribution signals that layer on top of the shared S2S mechanics: Apple Search Ads, Google Play and Meta install referrers, uninstall tracking, and the iOS install receipt. It applies to iOS and Android app integrations that report sessions and events server-to-server.

These features assume you already have the shared S2S request flow in place. For the endpoints, authentication, and common parameters they build on, start with the fundamentals hub and the two endpoint references.

Before you start: These features build on the shared S2S request flow. See the S2S Fundamentals hub for endpoints and common parameters, the SESSION endpoint reference , and the EVENT endpoint reference .


Apple Search Ads Attribution

iOS Search Ads Integration

Apple Search Ads is a Self-Attributing Network (SAN) requiring platform-specific attribution implementation via Apple frameworks.

Framework Support:

Dual Implementation: Implement both iAd and AdServices frameworks until iAd deprecated. Singular prioritizes AdServices over iAd signals for attribution and reporting.

Complete guide: Apple Search Ads Integration Documentation


iAd Framework Implementation

Retrieve and send Apple Search Ads attribution data via iAd framework for iOS 14.2 and below.

Step 1: Retrieve Attribution Data

Objective-C
#import <iAd/iAd.h>

Class ADClientClass = NSClassFromString(@"ADClient");

if (ADClientClass) {
    id sharedClient = [ADClientClass performSelector:@selector(sharedClient)];

    if ([sharedClient respondsToSelector:@selector(requestAttributionDetailsWithBlock:)]) {
        [sharedClient requestAttributionDetailsWithBlock:^(NSDictionary *attributionDetails, NSError *error) {
            if (attributionDetails && attributionDetails.count > 0) {
                // REPORT attributionDetails FROM YOUR APP TO YOUR SERVER
            }
        }];
    }
}

Latency Handling: Users clicking Search Ads may download and open app immediately. Prevent attribution timing issues by:

  1. Setting few-second delay before retrieving attribution data
  2. Implementing retry logic if response False or error code (0, 2, 3). Retry 2 seconds later

Step 2: Send to Singular

Report event with reserved name __iAd_Attribution__ via EVENT endpoint , passing attribution JSON as e parameter.

Timing Requirements:

  • iOS 13+: Send __iAd_Attribution__ event immediately after first session post-install/reinstall. Otherwise Apple Search Ads data not considered for attribution
  • iOS 14+: Attribution responses only available under certain conditions and unavailable if ATT status is ATTrackingManager.AuthorizationStatus.denied

AdServices Framework Implementation

Retrieve and send attribution token via AdServices framework for iOS 14.3 and above.

Step 1: Retrieve Attribution Token

Objective-C
#import <AdServices/AdServices.h>

NSError *error = nil;
Class AAAttributionClass = NSClassFromString(@"AAAttribution");
if (AAAttributionClass) {
    NSString *attributionToken = [AAAttributionClass attributionTokenWithError:&error];
    if (!error && attributionToken) {
        // Handle attributionToken
    }
}

Token Characteristics:

  • Generated on device
  • Cached for 5 minutes—new token generated after expiry if attributionToken() called
  • Valid for 24 hours

Step 2: Send to Singular

URL-encode token and append to SESSION endpoint as attribution_token parameter on first session after every install and reinstall.


Google Play Install Referrer

Android Install Attribution

Google Play Install Referrer provides most accurate Android install attribution, containing information about user origin before Play Store arrival.

Required For:

More info: Google Install Referrer Documentation

Implementation Steps:

  1. Retrieve install referrer on first app open using Play Install Referrer API
  2. Report session via SESSION endpoint including install_ref JSON parameter with required attributes

install_ref Attributes:

Parameter Details
referrer

Referrer value from Play Install Referrer API (JSON object—encode as string)

referrer_source

Specify "service"

clickTimestampSeconds

Click timestamp from API (e.g., "1550420123")

installBeginTimestampSeconds

Install start time from API

current_device_time

Current device time in milliseconds (e.g., "1550420454906")


Meta Install Referrer

Facebook Attribution Enhancement

As of June 18, 2025: Meta's Advanced Mobile Measurement (AMM) removes need for Meta Install Referrer implementation. Not recommended if AMM enabled.

Meta Referrer is Android-specific measurement solution providing granular user-level attribution data for app installs, combining Google Play Install Referrer and Meta Install Referrer technologies.

Learn more: Meta Referrer FAQ

Implementation Steps:

  1. Retrieve Meta install referrer on first app open per Meta's documentation
  2. Report session via SESSION endpoint including meta_ref JSON parameter with required attributes

meta_ref Attributes:

Parameter Details
is_ct

is_ct from Meta Install Referrer (0 or 1)

install_referrer

install_referrer from Meta Install Referrer

actual_timestamp

actual_timestamp from Meta Install Referrer (e.g., 1693978124)


Uninstall Tracking

Silent Push Notification Setup

Track uninstalls using device silent push notifications by sending push token with every session notification.

Setup Requirements:

  1. Follow platform-specific uninstall tracking setup:
  2. Append platform-specific token to SESSION request:

iOS Install Receipt

Receipt Validation

Pass iOS install receipt in install_receipt parameter when reporting iOS session.

Swift
import Foundation
import StoreKit

class ReceiptManager {
    static func getInstallReceipt() -> String? {
        if #available(iOS 18.0, *) {
            let semaphore = DispatchSemaphore(value: 0)
            var result: String?

            Task {
                do {
                    let transaction = try await AppTransaction.shared
                    result = transaction.jwsRepresentation
                    semaphore.signal()
                } catch {
                    debugPrint("Failed to get app transaction: \(error.localizedDescription)")
                    semaphore.signal()
                }
            }

            semaphore.wait()
            return result

        } else {
            guard let receiptURL = Bundle.main.appStoreReceiptURL else {
                debugPrint("Receipt URL not found")
                return nil
            }

            do {
                let receiptData = try Data(contentsOf: receiptURL, options: .uncached)
                return receiptData.base64EncodedString(options: [])
            } catch {
                debugPrint("Failed to read receipt: \(error.localizedDescription)")
                return nil
            }
        }
    }
}