
How to Start an Affiliate Program for an iOS App in 14 Days
There is no click id in the App Store. That single fact is why an affiliate program takes an afternoon on the web and two weeks on iOS. A creator posts a link, the user leaves your domain, Apple serves the product page, the install happens there, and your app opens for the first time having never heard of the click. How to start an affiliate program for an iOS app is therefore four jobs: capture the click on your own domain, recover it after the install, bind it to the user id your billing provider reports, and pay on the events that move money. Every Apple claim below was checked against Apple's pages on 2026-09-14.
#Why an affiliate link that works on the web breaks on iOS
Because the web keeps state in the URL and the App Store does not. On the web a click id rides the query string from the creator's post to your checkout, in one browser. On iOS the user taps apps.apple.com/app/..., installs from a page Apple controls, and opens the app days later from the home screen. That launch is not a Universal Link open, and there is nothing in it to read.
Apple ships three install attribution surfaces. None hands your app a per-user creator id.
- App Analytics campaign links report campaign totals inside App Store Connect. Counts, not people.
- AdServices exists to "Attribute app-download campaigns that originate from the App Store on iOS devices", returning Apple Ads metadata:
orgId,campaignId,clickDate. Your creator is not an Apple Ads campaign. - SKAdNetwork and AdAttributionKit post back to ad networks registered with Apple, coarsened by crowd anonymity data tiers.
SKAdNetwork is not deprecated, whatever you have read. Apple's documentation showed no deprecation on iOS, iPadOS or Mac Catalyst when we pulled it on 2026-09-14. It does carry an Important note: "Use AdAttributionKit for app ad campaigns on the App Store and alternative marketplaces." A steer is not a removal, and neither was built for a person with a podcast.
Our iOS SDK works around the gap rather than closing it. start asks the API for a deferred match on a fresh install because, as our docs put it, the App Store drops the claim token. Android has a deterministic answer in the Play Install Referrer; iOS has none.
#What App Store Connect campaign links actually give you
A campaign token in a URL and a row in Analytics. They report that a campaign produced downloads and subscriptions, never which subscriber came from which creator, and three documented limits stop them short of a payout ledger.
The link shape, from Apple's campaign links help page on 2026-09-14, is https://apps.apple.com/app/apple-store/id123456789?pt=123456&ct=test1234&mt=8. The campaign token ct is the campaign name you typed, up to 30 characters. The provider token pt identifies your developer account.
- Small creators report as nothing. A metric shows only above a threshold of five in the selected range, and campaign data appears once it has first-time downloads from five individual users.
- The window is 24 hours and the last click takes it. Apple: "When a user downloads your app for the first time within 24 hours of using your campaign link or campaign token, they're counted as a First-Time Download." Only the most recent link gets credit for later sales.
- Rows can disappear. Per-campaign data lives only in the detailed reports, where "Rows representing very small groups of users might be withheld or combined with other rows to prevent identifying individuals."
Twenty four hours is the number that should worry you, because creator traffic converts late, the argument behind how attribution windows work. Create the links anyway: they cost a minute and give you a second count when a creator disputes a payout. Do not pay from them. A ledger allowed to withhold a row cannot answer why the number is $40 and not $60.
#Where the affiliate id has to be by the time the purchase happens
On your server, already bound to the user id your billing provider will send. Not the app bundle, not the pasteboard, not App Store Connect. The purchase arrives as a webhook carrying a user id and a price, and an unattributed id can never be joined to a creator afterwards.
The join is one string. Your app calls identify(userId:), RevenueCat gets the same string through Purchases.logIn(userId), and the webhook arrives with app_user_id set to it. Our webhook reference is blunt: two different strings means "everything looks healthy and no commission is ever created".
This is the whole app-side integration, copied from the iOS SDK docs for version 0.3.0:
import MyAppAffiliate
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView().myAppAffiliate(apiKey: "pk_live_…")
}
}
}The modifier starts the SDK and forwards Universal Links, so first launch either claims a link or asks for a deferred match. Then MyAppAffiliate.identify(userId: yourUserId) binds it to your id.
Six routes can carry a creator id to your server. Two are exact.
| Route | How the creator id reaches your server | Exactness | Works pre-install | Where it stops |
|---|---|---|---|---|
| Universal Link into an installed app | Claim token in the link, read by `attribute(url:)` | Exact | No | Needs Associated Domains and the app already on the device |
| Creator code typed in the app | `MyAppAffiliate.applyCode("LUMI")` | Exact | Yes | The user has to see the code and type it |
| Deferred match on first launch | `start` asks the API, which matches a hashed IP inside a time window | Probabilistic | Yes | Shared IPs, and it declines rather than guesses |
| App Store Connect campaign link | It does not. Counts land in Analytics | Aggregate only | Yes | 24 hour window, five user threshold, rows can be withheld |
| Apple Ads via AdServices | Apple Ads campaign metadata, not your creator | Exact for Apple Ads | Yes | Apple Ads campaigns only |
| SKAdNetwork or AdAttributionKit | Postbacks to an ad network registered with Apple | Coarse by design | Yes | Built for ad networks, with crowd anonymity tiers |
#How accurate is deferred matching, and does it need ATT?
It is probabilistic and we will not pretend otherwise. The link Worker hashes the click IP at the edge with a salt and never stores the raw address. On first launch the SDK posts an install with no token and no code, and the server looks for unattributed clicks on that IP hash inside a short window. One distinct creator means attribute, recorded as deferred_ip. Two or more means nothing.
Three cases it cannot separate:
- Two colleagues behind one office NAT who clicked different creators this morning.
- Anyone sharing a carrier-grade NAT range, thousands of subscribers wide on mobile.
- The person who clicked and the different person who installed on the same cafe Wi-Fi.
It also misses installs after the window closes. Ambiguity resolves to nothing rather than a guess, every row records its match method, and your dashboard should show the split.
A creator code is exact by comparison, needing no Associated Domains, link domain or store cooperation:
MyAppAffiliate.applyCode("LUMI")It attributes the device immediately, and MyAppAffiliate.attributedAffiliateId() reads it back. Use links for reach and codes where money is at stake: a code survives a screenshot, a repost without your URL, and a viewer installing on a second device.
Apple's ATT page requires the framework when your app "collects data about end users and shares it with other companies for purposes of tracking across apps and web sites". User Privacy and Data Use defines tracking as linking your app's data with data from other companies' apps, websites or offline properties, for advertising.
That page's FAQ asks whether third-party deep-linking or deferred deep-linking tools need ATT. Apple answers yes, then states the condition: "If your application uses any third-party services that pass unique identifiers or create a shared identity of the user between applications from different companies" for ad targeting, ad measurement or a data broker. Read that against what your tool does, and ask App Review if you are unsure. Our SDK stores a Keychain device id and the attributed affiliate id, reads no IDFA, and joins nothing to another company's app. That describes our SDK, not a ruling on yours, and none of it is legal advice.
Apple also says you may not derive data from a device "for the purpose of uniquely identifying it", listing the user's network connection among the examples. Our reading is that a salted hash plus a short window, declining whenever ambiguous, is not an attempt to identify a device uniquely. If your risk tolerance is lower, ship codes only.
#What commission structure a first program should use
Pay a percentage of what you actually receive, for a fixed number of renewals, behind a hold period. Percentage of gross reads as generous and costs more than the number on the page. A flat bounty per install is the easiest deal to explain and to farm.
Start from Apple's cut, because "20 percent of revenue" means nothing until you say whose revenue. From Apple's subscriptions page: you receive 70 percent during a subscriber's first year, and "After a subscriber accumulates one year of paid service, your net revenue increases to 85% of the subscription price, minus applicable taxes." The Small Business Program sets 15 percent for accounts under 1 million USD in prior-year proceeds, whose members get 85 percent at every cycle whatever the subscriber's age. On EU alternative terms Apple lists a further reduced 10 percent for those developers and for subscriptions after their first year.
These differ less in what they cost than in what happens when a subscriber refunds or renews for three years.
| Structure | What you owe per conversion | Predictable | What it attracts | Where it breaks |
|---|---|---|---|---|
| Flat bounty per paying install | A fixed amount, once | Yes | Volume, including low intent volume | Refunds, and incentivized installs that guideline 5.6.3 treats as manipulating referrals |
| Percentage of the first payment | Rate applied to the first invoice | Yes | Creators whose audience buys now | Undervalues an audience that renews for two years |
| Percentage of every payment for N months | Rate applied to each invoice in the window | Mostly | Creators who care about retention | Needs a renewal-aware ledger and a cap |
| Percentage for the subscriber's lifetime | Rate applied to every invoice, forever | No | Everyone, permanently | An open-ended liability you cannot forecast |
| Tiered by volume | Rate rises at thresholds | No | Larger creators | A retroactive tier change rewrites a ledger you already paid |
We default to 20 percent of net, capped at 12 billed periods, with a 30 day hold. The cap makes the liability a number you can write down; the hold makes a first-month refund a status change, not a debt collection email. The argument in full is in affiliate commissions and payouts, and the base question in net versus gross on the App Store.
Which events move the balance is a short list. A first payment and a renewal create commission. A trial start is worth zero and belongs in the funnel, the subject of trials, intro offers and renewals. A refund reverses. Cancellations and expirations change nothing, because access continues and past charges stand. The RevenueCat trap: no event is named REFUND, so a mapping keyed on one never fires. Refunds arrive as CANCELLATION with cancel_reason: CUSTOMER_SUPPORT.
#A worked example on a $9.99 plan, with one refund
One subscriber, US storefront, standard first year rate, 20 percent of net, 30 day hold. Four charges land, one is refunded, the creator ends with $4.20.
Apple retains 30 percent of a $9.99 charge in the first year, so you receive $6.99 (9.99 x 0.70 = 6.993, truncated to the cent). Twenty percent of that net is $1.40, computed as our engine does it, in cents: 699 x 2000 bps / 10000 = 139.8. Twenty percent of gross is 999 x 2000 / 10000 = 199.8, or $2.00, which is 28.6 percent of the $6.99 you keep. Under the Small Business Program you receive $8.49 and 20 percent of net is $1.70.
| Date | Event | Gross | Your net | Commission | Creator balance |
|---|---|---|---|---|---|
| 1 Mar | `INITIAL_PURCHASE` | $9.99 | $6.99 | +$1.40 pending | $1.40 |
| 31 Mar | Hold expires, no webhook | Status only | $1.40 | ||
| 1 Apr | `RENEWAL` | $9.99 | $6.99 | +$1.40 pending | $2.80 |
| 1 May | `RENEWAL` | $9.99 | $6.99 | +$1.40 pending | $4.20 |
| 12 May | `CANCELLATION`, `cancel_reason: CUSTOMER_SUPPORT` | -$9.99 | -$6.99 | -$1.40 | $2.80 |
| 1 Jun | `RENEWAL` | $9.99 | $6.99 | +$1.40 pending | $4.20 |
Three charges survive, so three commissions do: 3 x $1.40 = $4.20. You keep 3 x $6.99 = $20.97, of which $4.20 goes to the creator and $16.77 stays with you. The 12 May refund lands 11 days after the commission it reverses, inside the hold, so it is a status change against a pending row rather than a clawback.
Run the subscriber past their first anniversary and the point of a net-based rate shows up on its own. Apple's rate moves to 85 percent, your net rises to $8.49, and the commission rises to $1.70 with nobody renegotiating. A gross-based commission stays at $2.00 while the creator's share of your proceeds falls from 28.6 to 23.6 percent.
Two field names make this computable rather than assumed. RevenueCat's webhook carries commission_percentage, documented as "Estimated store commission percentage", and tax_percentage. Both are estimates and both can be null. Fall back to your configured store rate on a null, or the accidental default is paying on gross.
#Before money moves: what a creator program needs
Six things, none of them code: written terms, an approval step, a hold period, a statement the creator can read unaided, a payout rail, and a record of what you sent. Self-referral and paid search on your brand name are the two exclusions worth naming on day one. Programs that skip this fail at month six, when 40 creators are owed money and nobody agreed what a refund does.
Payout rails are solved and uninteresting. Our customers use PayPal and Wise for cross-border creators and a bank transfer when everyone is in one country. We do not move money: the export matures the due commissions, records a payout, marks them paid, and returns a CSV that reconciles to the total. Automating around that is affiliate payout automation.
Then the part outside our lane. Creators are usually engaged as independent contractors rather than employees, and what a payer must collect or report depends on your country, the creator's country and the amounts. Programs commonly collect a tax form before the first payment, and a US payer typically asks a US person for a W-9 and a non-US individual for a W-8BEN. That is common practice, not a statement of your obligations, and nothing here is tax or legal advice. Ask an accountant before the first payout.
Three mistakes account for most unpayable ledgers we have seen on iOS:
- No hold period. Refunds become money you already sent, and the recovery conversation costs you the creator.
- Rates stored on the creator rather than snapshotted on the commission, so a rate change rewrites history.
- Attributing on an id your billing provider never reports. Both halves look healthy and you find out at the first payout.
#How to start an affiliate program for an iOS app in 14 days
Two weeks, assuming the app ships and bills through RevenueCat, Adapty, Superwall, Stripe or Paddle. Steps 9 and 13 catch the earlier errors.
- Decide the offer first. Rate, base, window, hold, cap. Five numbers, before you touch Xcode.
- Write the terms on one page, including the two exclusions.
- Add the SDK with Swift Package Manager,
from: "0.3.0", started by the SwiftUI modifier. - Call
identify(userId:)with the exact string your billing provider reports back. - Register the billing webhook at
https://api.myappaffiliate.com/webhooks/<provider>/<appId>and paste the signing secret. - Add the Associated Domains capability so Universal Links resolve for users who already have the app.
- Ship a "Got a code?" field wired to
applyCode(_:), which needs no link setup. - Create one campaign link per creator in App Store Connect for a second count.
- Run a sandbox purchase end to end and confirm one commission row, at the rate and creator you expect.
- Recruit five creators, not fifty. Five finds every bug and is payable by hand.
- Send each a link, a code and a one-page brief with the terms attached.
- Watch the match method split for a week. Mostly
deferred_ipmeans your codes are not visible enough. - Run the first payout manually and reconcile the CSV against your bank export.
- Only then revisit the rate. Cheap to change after two weeks of data, expensive after two hundred creators.
#Where MyAppAffiliate fits
We are the layer between the click and the payout, and we say which parts of it are exact. The iOS SDK is three calls: start, identify, and optionally a creator code. Revenue from RevenueCat, Adapty, Superwall, Stripe or Paddle normalizes to one event vocabulary and becomes a commission with the rate snapshotted on the row. Pricing is a flat monthly fee with no percentage of tracked revenue, the point of flat pricing.
One limit is better known before you start, and it is not the same on every plan. Free stops crediting the moment you pass its band. Growth and Scale carry you 50 percent past theirs before they do, which is meant to be enough room to hear from a person first. Either way the attribution is still recorded, just held back, and an upgrade releases what was held. A program that outgrows its plan loses no data, but it does stop crediting until you act.
Choosing between tools rather than building? The mobile affiliate software comparison puts us in the same table.
Does Apple give me an affiliate program for my app?
No. App Store Connect gives you campaign links, which count downloads and subscriptions per campaign token in Analytics. They carry no per-user identifier, apply a five user reporting threshold, and can withhold small rows. Nothing there computes or pays a creator commission.
Do I need the IDFA or an App Tracking Transparency prompt to run a creator program?
It depends on what your attribution tool does. Apple defines tracking as linking your app's data with other companies' data for advertising or ad measurement, and its FAQ answers yes for deep-linking tools that pass identifiers between companies. Read that condition against your own stack.
Is SKAdNetwork deprecated in favour of AdAttributionKit?
No. Apple's [SKAdNetwork documentation](https://developer.apple.com/documentation/storekit/skadnetwork) showed no deprecation on iOS, iPadOS or Mac Catalyst when we checked on 2026-09-14, alongside an Important note directing app ad campaigns to AdAttributionKit. Both are live, and both target registered ad networks rather than creators.
How accurate is deferred attribution on iOS, honestly?
It is probabilistic. The match is a hashed IP and a short time window, so two people on one office network who clicked different creators are indistinguishable. Our engine attributes nothing in that case rather than guessing. A typed creator code is the exact alternative.
Can a creator link straight to my web checkout instead of the App Store?
Outside the app, yes. [Guideline 3.1.3](https://developer.apple.com/app-store/review/guidelines/) says "Developers can send communications outside of the app to their user base about purchasing methods other than in-app purchase." Inside the app the rules differ by storefront, and 3.1.1(a) carves out United States storefront apps. Guidelines last updated 8 June 2026.
#Ship the code field this week
Add the "Got a code?" field and MyAppAffiliate.applyCode(_:) before anything else on the list. It is the one exact iOS route, needs no Associated Domains, and will carry your first five creators while you get the link path working. The five minute version is in the iOS SDK documentation, and the revenue side in the full RevenueCat setup.