SKAN & AdAttributionKit
Two Apple-mediated attribution frameworks on iOS. SKAdNetwork (SKAN) on iOS 15-17.3, AdAttributionKit (AAK) on iOS 17.4+. The SDK's job is the same for both — update the conversion value at meaningful in-app moments. Reflect handles the postback receipt, signature verification, and decoding server-side.
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, default on iOS 17.4+ and now the dominant attribution path for >50% of iOS installs. AAK extends SKAN with four conversion windows, signed JWT postbacks, re-engagement support, and a new source-domain field for web-to-app attribution. The SDK API is unchanged — ReflectSDK.UpdateConversionValue(…) writes to whichever framework iOS exposes — but the postback path differs server-side.
How AAK differs from SKAN
| Aspect | SKAN 4.0 | AdAttributionKit |
|---|---|---|
| iOS version | 15.0+ (4.0 features on 16.1+) | 17.4+ |
| Postback signature | Apple-signed JSON (attribution-signature field) | JWS (ES256-signed JWT compact form) |
| Conversion windows | 3 (0-2h, 2-72h, 72-360h) | 4 (configurable, plus an explicit lock-window flag) |
| Re-engagement | Not supported | Supported (re-engagement: true in payload) |
| Web-to-app | Limited | First-class via source-domain |
| Postback URL field | NSAdvertisingAttributionReportEndpoint in Info.plist | Same 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 + KV-cached 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 R2 audit for later backfill.
How to register Reflect for your app's AAK postbacks
- In your
Info.plist, setNSAdvertisingAttributionReportEndpointtohttps://api.reflect.cloud(the path/aak-postbackis appended by Apple). - If you also use SKAN (iOS 15-17.3 devices in your install base), keep the existing SKAN postback URL registered alongside — Apple sends to both endpoints based on device version. Reflect handles both at
/skan-postbackand/aak-postback. - 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).
- 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.
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
| Platform | Behavior |
|---|---|
| iOS 17.4+ | Uses AdAttributionKit (Apple's successor to SKAN) |
| iOS 16.1+ | Uses SKAdNetwork 4.0 — supports fine value, coarse value, and lock window |
| iOS 15.x | Legacy SKAN — fine value only, coarse value ignored |
| Android / Editor | No-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 how Reflect decodes the 0-63 value back into human-readable metrics (revenue ranges, engagement tiers). This mapping is used when displaying SKAN reports and doesn't affect the SDK.