RevenueHog

// guides

Create an App Store Connect API key (.p8, Issuer ID, Key ID)

Every App Store integration (CI, sales reports, server notifications tooling, revenue telemetry) starts with the same three values. Here's exactly where they live, which role to pick, and how to prove the key works before wiring it anywhere.

last updated 2026-07-11 · by revenuehog

The three values, and what each is

valuewhat it iswhere you find it
AuthKey_XXXXXXXXXX.p8Your private key: an EC (P-256) key in PKCS#8 PEM format. It signs the ES256 JWTs that authenticate every API request. Downloadable exactly once.The download button next to a freshly created key.
Key IDA 10-character identifier for this specific key (also embedded in the .p8 filename). Goes in the JWT header as kid.The key's row on the Integrations page.
Issuer IDA UUID identifying your team as the token issuer, shared by all of your team's keys. Goes in the JWT payload as iss.The top of Users and Access → Integrations.

Pick the least-privileged role

The role you assign at creation is the key's permission ceiling forever. Choose deliberately:

  • Finance or Sales is enough to read sales and finance reports: everything a revenue dashboard, analytics pipeline, or tool like RevenueHog needs to read your numbers. Prefer these for anything that only consumes data.
  • App Manager / Developer add app-metadata and build management: what CI needs to upload builds and edit listings.
  • Admincan do nearly everything, including updating App Store Server Notification URLs. Only hand an Admin key to something that actually manages configuration. For example, RevenueHog's optional one-click S2S wiring needs Admin to set notification URLs for you; with a read-only key you paste the URL into App Store Connect yourself and lose nothing.

One more distinction: team keys (what this guide creates) act with their own role, while individual keysinherit the permissions (and the fate) of the user who made them. Server integrations should use team keys so they don't break when someone leaves the team.

Step by step

  • Sign in to App Store Connect and open Users and Access → Integrations → App Store Connect API → Team Keys. First time here? An Account Holder must click Request Access once before keys can be created.
  • Click +, name the key after the thing that will hold it (revenue-telemetry-readonly beats key2when you're auditing later), and pick the role from the section above.
  • Click Generate, then Download API Key, immediately. This is the one-time download (next section).
  • Copy the Key ID from the key's row and the Issuer ID from the top of the page.

The one-time download

Apple lets you download the .p8exactly once; there is no "download again". If the file is lost, the only path is: revoke the key in App Store Connect, create a new one, and update every integration that used it. That's not a disaster (revocation is instant and free), but it's a deploy-day annoyance you can avoid by putting the file straight into a secret manager, not your Downloads folder.

Storing and rotating keys

  • Treat the .p8 like a password: secret manager or encrypted store, never committed to a repo, never pasted into chat tools.
  • ASC API keys don't expire on their own; rotation is on you. Rotate by creating a new key, switching integrations over, then revoking the old one (zero-downtime, since both work during the swap).
  • One key per integration. When each consumer has its own key, revoking one doesn't take down the others, and access is auditable.
  • Revoke immediately if a key may have leaked: Users and Access → Integrations → the key's row → Revoke.

Prove the key works: a minimal JWT

App Store Connect API auth is an ES256 JWT signed with the .p8. The claims Apple checks: iss (Issuer ID), aud (appstoreconnect-v1), and exp at most 20 minutes out; the header carries kid (Key ID) and alg: ES256.

mint a token
// asc-token.ts: mint a short-lived App Store Connect API token.
// Node 18+, npm i jose
import { readFileSync } from "node:fs";
import { SignJWT, importPKCS8 } from "jose";

const ISSUER_ID = "12345678-abcd-1234-abcd-1234567890ab"; // top of the Integrations page
const KEY_ID = "ABC123DEFG"; // shown next to your key

const privateKey = await importPKCS8(
  readFileSync("AuthKey_ABC123DEFG.p8", "utf8"),
  "ES256",
);

const token = await new SignJWT({})
  .setProtectedHeader({ alg: "ES256", kid: KEY_ID, typ: "JWT" })
  .setIssuer(ISSUER_ID)
  .setAudience("appstoreconnect-v1")
  .setIssuedAt()
  .setExpirationTime("20m") // Apple rejects tokens valid longer than 20 minutes
  .sign(privateKey);

console.log(token);
call the API
curl -s \
  -H "Authorization: Bearer $TOKEN" \
  "https://api.appstoreconnect.apple.com/v1/apps" | head

# 200 + a JSON list of your apps → the key works.
# 401 NOT_AUTHORIZED → usually a wrong Issuer ID / Key ID pairing,
#   an expired token, or server clock skew.
# 403 FORBIDDEN → the key's role can't access that resource.

What RevenueHog does with a key

RevenueHog is revenue telemetry built on exactly this key: you paste the .p8, Key ID and Issuer ID once, and it discovers every app on the key, imports about a year of daily history (Apple's daily-report limit), and turns Apple's reports plus Server Notifications V2 into a live feed, MRR and churn metrics. The key is encrypted at rest and used read-only: a Finance or Sales role is all it needs (Admin only if you opt into one-click notification wiring).

sources

Apple: creating API keys for App Store Connect API — developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api.

Apple: generating tokens for API requests — developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests.

Checked 2026-07-11.

// related