Browse docsOverview
Unity SDK · iOS

SKAN & AdAttributionKit

Two Apple-mediated attribution frameworks on iOS. The SDK writes SKAdNetwork (SKAN 2/3/4) conversion values at meaningful in-app moments — SKAN keeps working on every supported iOS version, including 17.4+. AdAttributionKit (AAK) is handled entirely server-side: Reflect receives, verifies, and decodes AAK postbacks whenever Apple sends them.

What is SKAN?

SKAdNetwork (SKAN) is Apple's privacy-preserving install attribution framework. Ad networks receive postbacks from iOS with a conversion value (0-63) that encodes post-install behavior (revenue tier, level reached, etc.). Reflect handles postback receipt and decoding server-side — the SDK's job is to update the conversion value at the right moments in your app.

AdAttributionKit (iOS 17.4+)

AdAttributionKit (AAK) is Apple's successor to SKAN, available on iOS 17.4+. AAK extends SKAN with four conversion windows, signed JWT postbacks, re-engagement support, and a new source-domain field for web-to-app attribution. On the client, the Reflect SDK deliberately does not link AdAttributionKit — ReflectSDK.UpdateConversionValue(…) writes SKAdNetwork conversion values, and SKAN postbacks continue to arrive on iOS 17.4+ devices. Server-side, Reflect fully accepts AAK postbacks at /aak-postback for the networks and campaigns that produce them; client-side AAK conversion updates are on the roadmap, not shipped.

How AAK differs from SKAN

AspectSKAN 4.0AdAttributionKit
iOS version15.0+ (4.0 features on 16.1+)17.4+
Postback signatureApple-signed JSON (attribution-signature field)JWS (ES256-signed JWT compact form)
Conversion windows3 (0-2h, 2-72h, 72-360h)4 (configurable, plus an explicit lock-window flag)
Re-engagementNot supportedSupported (re-engagement: true in payload)
Web-to-appLimitedFirst-class via source-domain
Postback URL fieldNSAdvertisingAttributionReportEndpoint in Info.plistSame Info.plist key, separate registration on Apple side

Postback endpoint

Reflect receives AAK postbacks at:

POST https://api.reflect.cloud/aak-postback Content-Type: application/jwt Body: <compact JWS — header.payload.signature>

Reflect verifies the JWS against Apple's published ES256 keys (JWKS fetched + cached in edge memory for 1h), persists the full raw JWT plus the decoded payload, and resolves the install destination via publisher-app-id → apps.store_id. Postbacks for unresolved apps are still saved to the immutable audit lake for later backfill.

How to register Reflect for your app's AAK postbacks

  1. In your Info.plist, set NSAdvertisingAttributionReportEndpoint to https://api.reflect.cloud (the path /aak-postback is appended by Apple).
  2. Keep the existing SKAN postback URL registered alongside — SKAdNetwork postbacks keep arriving on every supported iOS version, including 17.4+, and remain the primary Apple-mediated signal for Reflect-integrated apps. Reflect handles both at /skan-postback and /aak-postback.
  3. In your app's SKAN → CV Schema page in the admin panel, define how to decode the 0-63 fine-conversion-value back into revenue tiers / engagement levels. The schema applies to both SKAN and AAK postbacks (the fine value semantics are identical).
  4. Within ~24 hours of your first AAK install, postbacks land in the SKAN/AAK dashboard under your app. The reporting UI distinguishes SKAN rows from AAK rows so you can compare windows.
SKAN keeps working on iOS 17.4+
AdAttributionKit does not switch SKAdNetwork off — SKAN postbacks continue to arrive for iOS 17.4+ installs, so an app on Reflect's SKAN integration is not losing attribution data by staying on it. Registering the endpoint above additionally lets Reflect capture any AAK postbacks (with their extra conversion windows and re-engagement data) from networks that run AAK campaigns.

API

// Simple — just a fine value (0-63)
ReflectSDK.UpdateConversionValue(12);

// Full — fine value + coarse + lock window + callback
ReflectSDK.UpdateConversionValue(
    fineValue:   42,
    coarseValue: "high",        // "low", "medium", "high", or null
    lockWindow:  false,         // true = lock the current postback window
    onComplete:  (ok, err) => {
        if (!ok) Debug.LogWarning("SKAN update failed: " + err);
    }
);

Platform behavior

PlatformBehavior
iOS 17.4+Still SKAdNetwork 4.0 — the SDK deliberately does not link AdAttributionKit (AAK postbacks are received server-side; client-side AAK is roadmap)
iOS 16.1+Uses SKAdNetwork 4.0 — supports fine value, coarse value, and lock window
iOS 15.xLegacy SKAN — fine value only, coarse value ignored
Android / EditorNo-op (Editor logs the call for debugging)

When to call it

Call UpdateConversionValue whenever the user crosses a meaningful threshold:

// Example: encode revenue tiers
void OnPurchase(double revenueUsd) {
    int cv = revenueUsd >= 50 ? 63
           : revenueUsd >= 20 ? 48
           : revenueUsd >= 10 ? 32
           : revenueUsd >= 5  ? 16
           : revenueUsd >= 1  ? 8
           : 4;
    ReflectSDK.UpdateConversionValue(cv, coarseValue: cv >= 32 ? "high" : "medium");
}

// Example: encode level progress
void OnLevelComplete(int level) {
    int cv = Math.Min(level, 63);
    ReflectSDK.UpdateConversionValue(cv);
}

Conversion value schema (admin)

In the admin panel, go to SKAN → CV Schema to define what each 0–63 value means (revenue ranges, engagement tiers). The schema does two jobs, and the second one is easy to miss:

  • It decodes SKAN reports. Postbacks arrive carrying a bare number; the schema turns that number back into a revenue band or tier in the console and in CSV exports.
  • It drives the SDK’s automatic conversion values. The iOS core underneath every Reflect SDK fetches this schema from GET /skan/cv-schema and caches it for 24 hours. Post-install revenue is bucketed against it, and Apple is called only when the bucket goes up. With no schema published, no automatic value is ever sent — the SDK will not invent buckets you never defined.

Your own UpdateConversionValue calls, like the ones above, are passed straight through and need no schema. But if you rely on automatic revenue-based conversion values, publishing the schema is a prerequisite rather than a reporting nicety — and editing it changes what devices send once their cached copy refreshes, within 24 hours.

SKAN is complementary to Reflect's own attribution
Reflect attributes installs using deterministic (Play Referrer / AdServices) and fingerprint methods. SKAN postbacks provide an independent, Apple-verified signal. Use both: Reflect for real-time granular data, SKAN for privacy-safe validation and networks that only support SKAN.