You post the launch to Hacker News, submit it to a dozen directories, pitch a few Apple blogs and drop it in three subreddits. Over the next two days, downloads go up. That is the entire result: a graph that went up. Nothing in it tells you whether the movement was the Show HN post, the one subreddit that liked it, or a single link somebody put in a newsletter.
Apple's answer to this costs nothing, needs no SDK and sends nothing to a third party: give every venue its own tagged App Store URL. App Analytics then reports each tag as a separate campaign. The whole mechanism is one query parameter — and the two ways it goes wrong are that the parameter gets quietly renamed in transit, or that two venues end up sharing one.
What Apple's campaign link actually is
A campaign link is your normal App Store product page URL with tokens attached. Apple's own example on the campaign links help page is:
https://apps.apple.com/app/apple-store/id123456789?pt=123456&ct=test1234&mt=8
Two of those three parameters are documented. The campaign token, ct, identifies one campaign, and Apple says you create a new one for each campaign you run. The provider token, pt, identifies your developer account — Apple is explicit that it is not specific to an ad network or an external provider. You generate it the first time you create a campaign link and then reuse it forever; only the campaign token changes between venues.
Apple's troubleshooting section is direct about needing both: a campaign that is not appearing should include "both a provider token and a campaign token". That is why a link builder has to ask you for the provider token rather than deriving it — it belongs to your account and is not exposed anywhere on a public App Store listing. It is a team identifier, not a credential.
The third parameter, mt=8, appears in Apple's example and is not explained anywhere on that page. So the honest description is the one used in Appstro's own code: mt=8 is the media type in Apple's documented example. Anything more specific than that is inference dressed up as documentation.
One detail worth noting before you plan around it: campaign links are created inside App Store Connect, under Analytics, and Apple's troubleshooting answers the obvious launch-week question with a flat no — the Campaigns tab and its add button appear only once your app has generated analytics data. If your app is brand new, that tab may not be there on day one.
The rules Apple publishes, and the ones that circulate
Three rules govern a campaign token, and all three are on Apple's page rather than inferred:
- It can be up to 30 alphanumeric characters and spaces.
- Alongside those, this punctuation is allowed:
[ ] / \ - ~ + = <> : ; , . _ ' " * & $ % # @ ? ! | { } ( ) - "A space can't be used as the first or last character in the campaign token."
The campaign name you type into App Store Connect is the campaign token, so those limits are limits on what you can call a campaign, not on some hidden encoding underneath it.
Now compare that against what gets repeated elsewhere. Searching for guidance on pt and ct turns up third-party documentation asserting a 40-character cap on the campaign token, and asserting that ?, ! and & cannot be used — three characters that appear explicitly in Apple's allowed list. Neither claim cites Apple.
| Rule | Apple's help page | What circulates elsewhere |
|---|---|---|
| Campaign token length | 30 characters | 40 characters |
?, ! and & in a token | listed as allowed | "cannot include" |
| Space as first or last character | not allowed | rarely mentioned at all |
The divergence matters less because 40 is wrong than because of what it signals: a rule nobody checked at the source gets copied forward, and you cannot tell from the page which of its other rules were checked either. Every constraint in this post came off Apple's page during the writing of it.
Trap one: the plus sign that renames a campaign
Apple permits spaces inside a campaign token, which means the obvious campaign name — spring sale — is legal. Building the URL is where it breaks.
The default tool for assembling a query string in a browser is URLSearchParams, and it serialises a space as +. That is correct for HTML form encoding, and it is the reason + in a query string so often means a space. But it is a convention of one media type, not of URLs. Under strict RFC 3986 query parsing, + is a literal plus character with no special meaning at all.
So spring sale becomes spring+sale, and whether your campaign is named correctly now depends on which of those two readings the receiving system applies. %20 has no such ambiguity: it decodes to a space under both.
This was caught in Appstro's own testing rather than reasoned about in the abstract, and the fix is one line — the link builder in src/lib/campaignLinks.ts uses encodeURIComponent on each token and joins the parameters itself, never URLSearchParams.
There is a second, blunter version of the same problem. Apple's allowlist includes &, #, ? and % — characters that terminate or corrupt a query string unless they are percent-encoded, and that survive being pasted into a forum post, a chat client or a QR generator far less reliably than letters and hyphens do. A token can be entirely legal by Apple's rules and still arrive somewhere as something else. That is why Appstro's generated tokens are deliberately stricter than Apple's allowlist: they emit only URL-safe characters, so the link is byte-identical before and after encoding. Tokens you type yourself are validated against Apple's full set, with a warning rather than an error when they are legal but need encoding.
Trap two: two venues, one row
The second failure needs no encoding bug at all. It happens when two links carry the same campaign token.
Apple describes the reporting side plainly: you view results by adding a filter in Analytics and picking a campaign from a drop-down. The campaign token is the only thing distinguishing one link from another. So if you tag two subreddits reddit, there is no filter, date range or dimension that can pull them apart afterwards — not because of undocumented backend behaviour, but because the information was never in the link. Apple does not document what happens when two links share a token, and this post will not guess; what is certain is that nothing downstream can separate them.
This is the exact failure that made /campaign-links worth building, and the check is trivial: generate one row per venue, then refuse to let two rows share a token. It is much easier to catch before you post thirty links than after.
Apple documents one more thing that changes how you read the results: if a user clicks more than one campaign link within a given timeframe, "only the most recent link receives credit" for subsequent sales. During a launch week, when the same person may well see your app on three of the thirty places you posted it, that is last-touch attribution — the final click takes the credit, not the one that introduced them.
Why the dashboard is empty the next morning
Three separate thresholds keep campaign data invisible early, and knowing them is the difference between waiting and rebuilding links that were fine all along.
Apple states that campaigns appear in Analytics only after "a minimum of 24 hours since their launch". Separately, a specific campaign's data appears once it has produced first-time downloads from "at least five individual users". And separately again, each individual metric appears only when it meets a minimum threshold of 5 within the date range you have selected — so a campaign that is genuinely reporting can still show an empty metric because the window you are looking at is too narrow. Apple's own advice for that case is to widen the date range.
Note that there are two different 24-hour periods on that page and they are easy to conflate. One is how long before campaigns show up in Analytics. The other is the attribution window: a user who downloads for the first time within 24 hours of using your campaign link counts as a first-time download for it. Those are unrelated numbers that happen to match.
If the link becomes a QR code
Launch links end up on slides, posters and stickers, and two details there fail silently in the same way the plus sign does.
The first is the quiet zone — the white margin the QR specification requires around the code. A code cropped tight scans badly in the physical world and gives no hint of why, because decoding the same image file on a computer works perfectly. Appstro renders campaign QR codes with a margin of 4 modules for that reason, and verified the codes by round-tripping real campaign links, including percent-encoded tokens and one at the full 30 characters, through an independent decoder.
The second is colour. The codes stay black on white whatever theme the page is in, because not all scanners handle an inverted code and the failure — again — is silent.
What this post cannot tell you
Several things, and they are worth stating rather than implying.
Nothing here can read your App Analytics. Campaign results live in App Store Connect, visible only to your team. No public API and no third-party tool exposes another developer's campaign data, and any tool claiming to show you your numbers without your account is not doing what it says.
Apple does not document everything. The meaning of mt=8, whether campaign tokens are case sensitive, whether there is a maximum number of campaigns, whether links expire, how duplicate tokens are handled, and how the parameters should be URL-encoded are all absent from the help page. Where this post is silent on those, the silence is the finding.
Tagging does not tell you whether a venue was worth it. A campaign token tells you which link produced downloads. It cannot tell you whether those users stayed, whether the same people would have found you anyway, or whether a venue that sent nothing this time sends something next time. That judgement is yours, and it needs more than one launch.
Bulk export is deliberately lossy. Apple notes that individual campaign data appears only in the detailed Analytics reports, and that rows representing very small groups of users may be withheld or combined to avoid identifying anyone. A small launch is exactly the case where that applies.
The work itself is quick: /launch-directories is the list of thirty places to submit, and /campaign-links builds one validated, duplicate-checked link per venue, with QR codes and a CSV export, before you post a single one of them.