Deeplinkly

Glossary/iOS platform and privacy

SKAN Postback

Definition

A SKAN postback is a signed JSON report that iOS sends from the device to an ad network's server after an advertised app is installed, containing the campaign identifier and any conversion value the install's privacy tier permits.

It is the only output of SKAdNetwork, which makes its shape the whole contract. There is no query API, no backfill, and no way to request a postback that never came — if your endpoint drops one, that install is unmeasured forever. Postbacks are also delayed by design, arrive out of order, and routinely omit fields you were counting on.

Who receives one, and how you get a copy

The device posts to the ad network registered in the advertised app's SKAdNetworkItems. You, the advertiser, are not a recipient by default — you receive copies only of the postbacks your app *wins*, and only if you declare an endpoint.

Info.plist — request your own copies
<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://postbacks.example.com</string>
What each party receives.
RecipientEndpointGets
Ad network/.well-known/skadnetwork/report-attribution/Wins and losses (did-win: false)
AdvertiserThe host in NSAdvertisingAttributionReportEndpointWinning postbacks only

The copy is worth having even when an MMP already parses the network's feed: it is the one version of the record that did not pass through a party with an interest in the numbers, which makes it the tiebreaker in a discrepancy argument.

Every field, and what it means when it is missing

A complete 4.0 postback
{
  "version": "4.0",
  "ad-network-id": "example123.skadnetwork",
  "source-identifier": "5239",
  "app-id": 525463029,
  "transaction-id": "6aafb7a5-0170-41b5-bbe4-fe71dedf1e28",
  "redownload": false,
  "source-app-id": 1234567891,
  "fidelity-type": 1,
  "did-win": true,
  "postback-sequence-index": 0,
  "conversion-value": 20,
  "coarse-conversion-value": "high",
  "attribution-signature": "MEQCIQD..."
}
Postback fields and their failure modes.
FieldAlways presentIf absent, it means
versionYes
ad-network-idYes
transaction-idYes— use it as the idempotency key
app-idYes
source-identifierYes— but 2, 3 or 4 digits by tier
conversion-valueNoTier below 3, or index > 0. Not zero
coarse-conversion-valueNoTier 0 — nothing was measurable
source-app-id / source-domainNoThe publisher cohort was too small to name
fidelity-typeNoTreat as unknown, not as a click
did-winNoOlder versions; absent implies a win
redownloadYes

Absent is not zero

The most expensive parsing bug in SKAN is defaulting a missing conversion-value to 0. Zero means "installed and did nothing"; absent means "the cohort was too small to tell you". Conflating them creates a phantom population of worthless users, which then teaches your bidding to avoid the campaigns that were merely small.

The three timers

Every delay in SKAN is deliberate: arrival time must not leak conversion time. Three separate clocks stack up between the install and your dashboard.

Timers between install and postback arrival.
PostbackMeasurement windowRandomised delayRealistic arrival
Index 0Days 0–224–48 hoursDay 3 to day 5
Index 1Days 3–724–144 hoursDay 8 to day 13
Index 2Days 8–3524–144 hoursDay 36 to day 41
  • Postbacks for one install are not correlatable across indices — there is no shared install ID, only the campaign dimensions. Aggregate at the source-identifier level, never at the user level.
  • lockWindow: true closes a window early but does not shorten the randomised delay.
  • Indices arrive out of order across a cohort. A day-13 report is not late; it is index 1.
  • Cohort reporting must stay open for six weeks. A campaign judged at day 7 has seen one third of its postbacks.

Verifying the signature

The endpoint is a public URL that accepts unauthenticated POSTs, so anyone who finds it can send you installs. attribution-signature is an ECDSA signature over specific fields concatenated in a fixed order, verifiable against Apple's public key.

Verifying a postback signature
# Fields are joined with U+2063 (INVISIBLE SEPARATOR) in the order
# Apple documents for the postback's version — the order differs
# between 3.0 and 4.0, so branch on the version field.

printf '4.0⁣example123.skadnetwork⁣5239⁣525463029...' > payload.txt

# Apple publishes the verification key; the signature is base64 DER ECDSA.
openssl dgst -sha256 \
  -verify apple-skan-public-key.pem \
  -signature <(base64 -d <<< "$SIGNATURE") \
  payload.txt

Reject before you count

Verify the signature, then deduplicate on transaction-id, then count. Skipping either step means your install numbers are writable by anyone on the internet, and inflated SKAN volume that reconciles against nothing is indistinguishable from a fraud problem in your own reporting.

The exact field order is version-specific and Apple's documentation is the only authority on it; validate against a known-good live postback rather than trusting a copied implementation.

SKAN conversion value builder

The postback's conversion value is only useful if the schema behind it was designed for six bits and one window. Our builder produces that mapping, including what your dashboard should show when the field is absent rather than zero.

Open the skan conversion value builder

Frequently asked questions

What is a SKAN postback?
It is the signed JSON attribution report iOS sends from the device to an ad network after a user installs an advertised app. It contains the ad network ID, a hierarchical source identifier for the campaign, the app ID, a transaction ID, and — when the install cohort's privacy tier allows it — a conversion value describing post-install behaviour.
How many postbacks does one install generate?
Up to three in SKAdNetwork 4.0, one per conversion window, identified by a postback-sequence-index of 0, 1 or 2. They cannot be linked back to a single install because no shared identifier is included, so they must be aggregated at the campaign level rather than counted as three separate installs.
Why does my postback have no source-app-id?
Because the cohort of installs from that publisher app was too small for Apple to name the source without risking re-identification. The same crowd anonymity thresholds that strip the conversion value also strip the publisher identity, which is why granular publisher-level reporting is unavailable on low-volume campaigns regardless of instrumentation.
How do I verify a SKAdNetwork postback is genuine?
Verify the attribution-signature field, which is an ECDSA signature over a version-specific concatenation of postback fields joined by the U+2063 invisible separator character, checked against Apple's published public key. Because the endpoint accepts unauthenticated POST requests, skipping verification means anyone who discovers the URL can inject fabricated installs into your reporting.
Can I get a postback resent if my server was down?
No. There is no retry guarantee, no query API and no backfill, so a postback your endpoint fails to accept is lost permanently along with that install's measurement. Return a 200 quickly and do the parsing asynchronously, and keep the raw body so a parsing bug can be replayed without needing the postback again.

Related terms

  • SKAdNetworkSKAdNetwork is Apple's StoreKit framework that attributes app installs to advertising campaigns without exposing a device identifier, by having the operating system send a delayed, aggregated postback to the ad network that won the install.
  • Conversion ValueA conversion value is a 6-bit integer between 0 and 63 that an iOS app sets after install to describe post-install behaviour, and which the operating system reports to the ad network in an attribution postback.
  • Crowd AnonymityCrowd anonymity is Apple's privacy mechanism that decides how much campaign and conversion detail an attribution postback may contain, based on whether the install cohort is large enough that the report cannot identify an individual.
  • WebhookA webhook is an HTTP request a service sends to a URL you control when an event occurs, delivering the event's data without your system having to poll for it.