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:
- iOS 14.2 and below: iAd framework
- iOS 14.3 and above: AdServices framework (recommended)
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
#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:
- Setting few-second delay before retrieving attribution data
- 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
#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:
- Facebook data in User-Level Exports
- Sharing with Data Destinations
- Sending postbacks
More info: Google Install Referrer Documentation
Implementation Steps:
- Retrieve install referrer on first app open using Play Install Referrer API
-
Report session via
SESSION endpoint
including
install_refJSON 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:
- Retrieve Meta install referrer on first app open per Meta's documentation
-
Report session via
SESSION endpoint
including
meta_refJSON 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:
- Follow platform-specific uninstall tracking setup:
-
Append platform-specific token to SESSION request:
-
iOS:
apns_token(APNs device token) -
Android:
fcm(FCM device token)
-
iOS:
iOS Install Receipt
Receipt Validation
Pass iOS install receipt in
install_receipt
parameter when reporting iOS session.
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
}
}
}
}