One of the rejections on Speech Route came down to the privacy policy. The client had sent me an HTML file.
I had a document. App Store Connect wanted an address.
Guideline 5.1.1, Data Collection and Storage, is the one that shows up when this is wrong. Apps that collect user data need a privacy policy a reviewer can actually open. App Store Connect's field is labelled Privacy Policy URL for a reason. It is a URL. A file sitting on my machine, or attached to an email, is not that.
It had to be hosted somewhere public before the field would take it. That sounds obvious written down. It is not obvious when you are in the middle of a first submission, holding a file the client considers done, and the rejection doesn't say "host this."
A file is not a link, twice
The same document is required in two places, and doing one is not enough.
On the paywall, Guideline 3.1.2 wants functional links to the privacy policy and the Terms of Use. Functional means they open a real page. A link to a 404, or to an empty in-app browser, counts as missing. So does a paywall that has no link because you never had a URL to point it at.
In App Store Connect, the Privacy Policy field on the app record has to be that same public URL. Terms of Use need to exist in the listing too — either a link in the description, or a custom EULA uploaded in App Store Connect. Apple's standard EULA is accepted if you link it from the description.
I had neither a hosted policy nor a working paywall link, because I had a file. One missing URL blocked both checks. A full review cycle, for something that never appeared in any StoreKit tutorial.
What I do with it now
Before I submit anything that collects data or sells a subscription, I ask for the hosted URL first — not the document. If the client only has a file, hosting it is part of the submission work, not a leftover.
Then I put that URL in three places: the paywall, the Privacy Policy field, and wherever the terms live in the listing. I open each link on a phone, not only in a desktop browser, because that is closer to what a reviewer does.
The empty paywall that started this submission is Guideline 2.1. The full sequence is in the eight-rejections post.