FunnelHound / Guides / The .p8 file
The App Store Connect .p8 file: what it is and where it goes
The .p8 file is the private half of an App Store Connect API key: a small PEM text file named AuthKey_<KEYID>.p8, handed to you exactly once when the key is created. Every tool that talks to Apple on your behalf, from fastlane to a Python script to FunnelHound, signs a short-lived token with it. Lose it and there is no second download, only a new key.
What is inside
Open it in any text editor: a BEGIN PRIVATE KEY line, four lines of base64, an END PRIVATE KEY line. That is a PKCS#8 container holding an elliptic-curve private key on the P-256 curve, the curve the ES256 signature algorithm uses. There is no password on it, which is why the file itself is the secret. The matching public key stays with Apple; when a tool signs a token with your .p8, Apple checks the signature against that public key and trusts the request up to the key's role.
The number in the filename is the Key ID. Tools that take a folder instead of a path find the right key by that name, so renaming the file is the fastest way to break altool and notarytool. The Issuer ID is not in the file anywhere; it is read off the API page and passed alongside.
One download, ever
App Store Connect shows a Download API Key link once, in the row of the freshly created key, with a warning that it will not be offered again. Click it, store it, and only then leave the page. Losing the file means revoking the key and creating another, then rotating the Key ID in every tool. Apple does this on purpose: a private key that can be re-downloaded is a private key that Apple holds, and the point of the scheme is that it does not. The step-by-step for creating the key, including which role to pick, is in the API key setup guide.
Where each tool expects it
| Tool | How it takes the .p8 | Notes |
|---|---|---|
| fastlane | app_store_connect_api_key(key_id:, issuer_id:, key_filepath:), or key_content: with is_key_content_base64: true; env vars APP_STORE_CONNECT_API_KEY_KEY_ID, _ISSUER_ID, _KEY | The returned hash feeds pilot, deliver, match and sigh. Prefer the base64 content form on CI. |
| xcodebuild | -allowProvisioningUpdates -authenticationKeyPath ./AuthKey.p8 -authenticationKeyID KEYID -authenticationKeyIssuerID UUID | Lets archive and export talk to Apple without a logged-in Xcode account. |
| altool, notarytool | --apiKey KEYID --apiIssuer UUID, file looked up in ./private_keys, ~/private_keys, ~/.private_keys or ~/.appstoreconnect/private_keys; notarytool also accepts --key path --key-id --issuer | Filename must stay AuthKey_KEYID.p8 for the folder lookup to work. |
| Transporter app | Sign in with API key: paste Issuer ID, Key ID, select the file | Once per Mac. |
| Xcode Cloud | Nothing | Apple's own service signs internally; no .p8 to manage. |
| Scripts (Python, Node, Swift) | Read the file, sign an ES256 JWT with kid = Key ID, iss = Issuer ID, aud = appstoreconnect-v1, expiry under 20 minutes | Worked example in the Python guide. |
| FunnelHound | Import once from Files or the clipboard; stored in the iOS Keychain | Signs on the device, never uploads the key anywhere. |
Storing it without regretting it
- Password manager as the source of truth, attached to an entry that also holds the Key ID and Issuer ID. One item, three values.
- CI: base64 in a secret. On macOS: base64 -i AuthKey_KEYID.p8 | tr -d '\n' | pbcopy. In the job: echo "$ASC_KEY_B64" | base64 --decode > AuthKey.p8, then delete it at the end. Base64 survives secret stores that mangle newlines; raw PEM often does not.
- Never in the repository. Add *.p8 to the global gitignore today. A key in git history is a key you have to revoke, even in a private repo.
- Never in chat. The file plus the two public IDs is full access up to the key's role. Treat the three together as a password.
- One key per purpose. A CI key with App Manager rights, a read-only Sales key for dashboards, a Developer key for scripts. Revoking one does not stop the others.
The other .p8: push keys
developer.apple.com → Certificates, Identifiers & Profiles → Keys also produces a file called AuthKey_KEYID.p8, in the same PEM format, for Apple Push Notification service and for Sign in with Apple. Same shape, different key, different companion: the push key travels with your ten-character Team ID, the App Store Connect key with a UUID Issuer ID. A push provider asking for a Team ID will reject an API key, and fastlane will reject a push key, each with an error that mentions neither fact. When two .p8 files sit in the same Downloads folder, the companion ID is how you tell them apart.
Did it work? Thirty seconds
Sign a token and call GET https://api.appstoreconnect.apple.com/v1/apps. A JSON list of your apps means the file, Key ID and Issuer ID agree. A 401 means one of the three is wrong or the token expired; a 403 means the key's role is too low for that endpoint. The setup guide has the curl one-liner.
Import the key once, read the funnel forever
FunnelHound takes the .p8, Key ID and Issuer ID, keeps them in the iOS Keychain, and signs every request on your iPhone. Impressions, page views, installs, deletions and proceeds for all your apps, with no server holding your key.
Get FunnelHoundData notes: tool flags per fastlane, Xcode and notarytool documentation as of 2026; folder lookup paths per Apple's altool and notarytool manuals. 2X9R4HXF34 and the Issuer ID fragment are placeholders. Token lifetime must stay under 20 minutes or Apple rejects it. Verify flag spellings against the tool version you run; fastlane in particular renames options between majors.