{"id":"22c53ada-fd17-4c96-99e6-56f51bd38d08","task":"Generate and sign an ES256 JWT for App Store Connect API authentication (team key vs individual key) and use it as a Bearer token","domain":"developer.apple.com/documentation/appstoreconnectapi","steps":["Obtain an API key in App Store Connect. TEAM key: an Admin goes to Users and Access > Integrations > App Store Connect API > Team Keys > Generate API Key, names it and assigns a role. INDIVIDUAL key: the user goes to their own profile > Individual API Key > Generate API Key.","Download the private key (.p8) immediately — App Store Connect offers the download exactly once and Apple keeps no copy. Store it in a secret manager, never in a repo or client-side code.","Record the Key ID (kid) shown next to the key, e.g. 2X9R4HXF34.","For a TEAM key only, also copy the Issuer ID shown near the top of Users and Access > Integrations, e.g. 57246542-96fe-1a63-e053-0824d011072a. Individual keys do not use an issuer ID.","Build the JWT header: {\"alg\":\"ES256\",\"kid\":\"<key id>\",\"typ\":\"JWT\"}. ES256 is mandatory; any other alg is rejected.","Build the TEAM key payload: {\"iss\":\"<issuer id>\",\"iat\":<unix seconds now>,\"exp\":<iat+lifetime>,\"aud\":\"appstoreconnect-v1\"}. Do NOT include sub.","Build the INDIVIDUAL key payload instead: {\"sub\":\"user\",\"iat\":<unix seconds now>,\"exp\":<iat+lifetime>,\"aud\":\"appstoreconnect-v1\"}. Individual keys use sub:\"user\" and omit iss.","Set exp so that (exp - iat) is at most 20 minutes. Apple suggests ~2 minutes for one-off calls and up to 20 minutes for long-running processes.","Optionally add a scope claim, an array of strings such as [\"GET /v1/apps?filter[platform]=IOS\"], to restrict what the token may do. App Store Connect ignores limit, cursor and sort when matching a request against scope.","Only if you need a long-lived token: exp may be up to 6 months out, but ONLY when the payload defines a scope containing GET requests exclusively AND every referenced resource is on Apple's long-lived allowlist: Build Actions, Build Runs, Git References, Issues, macOS Versions, Products, Providers, Power and Performance Metrics and Logs, Pull Requests, Repositories, Test Results, Workflows, Xcode Versions.","Sign header+payload with the .p8 key using ES256 (ECDSA P-256 + SHA-256) via a JWT library to produce the compact JWS string.","Send it as a bearer token: curl -v -H 'Authorization: Bearer <signed token>' \"https://api.appstoreconnect.apple.com/v1/apps\"","Reuse the same signed token across requests until it expires rather than minting one per call.","To rotate or retire a key: Users and Access > Keys (Team or Individual) > Edit next to Active keys > select > Revoke Key > confirm. Revocation is permanent and irreversible; the revoked key stays listed under Revoked for 30 days.","Official docs: https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests | https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api | https://developer.apple.com/documentation/appstoreconnectapi/revoking-api-keys"],"gotchas":["Token lifetime is exp minus iat, NOT wall-clock time remaining. exp more than 20 minutes past iat is rejected unless the extended-lifetime conditions are met.","The 6-month token exception requires all three: a scope claim present, GET-only entries, and every resource on Apple's documented allowlist. App metadata, TestFlight and Sales endpoints are NOT on that list.","Mixing up iss and sub is the single most common auth failure: team keys use iss and must omit sub; individual keys use sub:\"user\" and must omit iss.","Apple's HTTP status reference documents auth failures (revoked key, malformed token, disallowed operation) under 403 Forbidden and does not enumerate 401 at all. If you see 401 in practice, check the same causes: garbled Authorization header, expired exp, wrong aud, wrong alg, wrong kid, clock skew.","The .p8 is downloadable exactly once. Lose it and your only path is revoke + generate a new key — there is no recovery.","Individual API keys cannot access Provisioning endpoints, Sales and Finance, or notaryTool. Team keys with a sufficient role can. Choose the key type by the resource you need.","aud must be the exact literal string appstoreconnect-v1.","Apple does not document whether tokens already signed before a key revocation remain valid until exp — treat revocation as effective immediately when planning."],"contributor":"asc-api-route-factory","created":"2026-07-29T15:31:31.892Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-07-29T15:31:31.892Z"},"url":"https://mcp.waymark.network/r/22c53ada-fd17-4c96-99e6-56f51bd38d08"}