Apple’s token documentation requires the ES256 algorithm for App Store Connect API JWT signing, so a token that merely parses or verifies locally is not proof that Apple will accept the request (Apple’s JWT requirements). If your request returns HTTP 401, do not immediately revoke and recreate every key. First confirm the API endpoint, then check the key source, JWT claims and clock, role permissions, and remote Mac credential injection. If the smallest authenticated request still fails, preserve its request ID and send a sanitized report to Apple.
This guide is for you if:
- You use fastlane or a custom script to upload TestFlight builds.
- A release job worked locally but returns 401 after moving to a remote Mac or CI Runner.
- You generate JWT yourself and call App Store Connect API from backend automation.
Start with the failing request
A redacted failure record is more useful than a new key. Save the request URL, HTTP status, Apple error code, request ID, tool or action name, execution account, and environment entry point. Replace every identifier with placeholders such as <KEY_ID>, <ISSUER_ID>, <APP_ID>, <BUNDLE_ID>, and <REQUEST_ID>.
A typical record should distinguish these cases:
| Evidence to capture | Example redacted value | Why it matters |
|---|---|---|
| Endpoint | api.appstoreconnect.apple.com/v1/... |
Separates App Store Connect API from another Apple service |
| Status and error | 401 / NOT_AUTHORIZED |
Shows whether the failure is authentication-related or a later permission problem |
| Request ID | <REQUEST_ID> |
Gives Apple a traceable diagnostic reference |
| Trigger | fastlane action, shell script, or HTTP client |
Identifies which credential path actually ran |
| Environment | Local shell, SSH session, or CI Runner | Exposes differences in variables, files, and working directories |
Do not treat “JWT generated successfully” as the end of the test. A local library can sign a token with the wrong key, wrong audience, expired timestamps, or a key intended for another Apple service. The signature can be mathematically valid while the API request remains unauthorized.
Why does App Store Connect API return 401 NOT_AUTHORIZED?
The most common explanation is not one universal Apple outage. It is a mismatch somewhere between the endpoint and the authorization chain: the request uses the wrong API, the .p8 key came from the wrong Apple service, the JWT claims do not meet the API requirements, the key role does not cover the operation, or the remote process is reading different credentials from the ones you inspected locally. Apple’s error response documentation is the reference point for interpreting the status and error payload.
Endpoint and key provenance
Before changing JWT code, confirm which service you are calling. App Store Connect API, App Store Server API, In-App Purchase tooling, Transporter upload, and a fastlane action may involve different authentication paths. A private key is not interchangeable merely because every file uses the .p8 extension.
Use the Apple interface where the key was created as evidence. Do not infer its purpose from the filename. Check the key ID shown in App Store Connect, the issuer or team association, the documented API endpoint, and the operation that the script is attempting. Apple’s App Store Connect API key documentation explains the intended key-management boundary.
| Credential or task | What to verify | What not to assume |
|---|---|---|
| App Store Connect API key | It was created in the App Store Connect API area and is used with the matching endpoint | Any .p8 file is suitable |
| In-App Purchase workflow | The key belongs to the service required by that workflow | The same key can authenticate every Apple API |
| Transporter or upload action | The selected fastlane action’s credential mode and upload mechanism | A successful upload proves every API call is authorized |
| Custom JWT client | Key ID, issuer ID, private key, claims, signing algorithm, and endpoint agree | Local signature verification proves Apple acceptance |
Can an App Store Connect API key and an In-App Purchase key be mixed?
No. Treat them as separate credentials until Apple’s documentation for the exact service says otherwise. A shared file extension does not establish shared authorization. If the key’s creation location, key ID, or endpoint does not match the requested operation, stop debugging the payload and replace the credential path with the correct service-specific key.
Use a new key only after you have confirmed that the current key is wrong, unavailable, or compromised. Revoking first can break other release jobs that still depend on it. The safe sequence is: create or identify the replacement, inject it into a test environment, run the minimum request, perform the real upload, then revoke the old key only if the replacement passes and you know its impact scope.
JWT claims and system time
Apple’s token guide specifies ES256 and documents the required token inputs, including kid, iss, aud, iat, and exp (JWT generation requirements). Check the three layers separately:
- Header: confirm
algisES256andkidis the Key ID associated with the private key. - Payload: confirm
issmatches the expected issuer,audis the documented audience, andiatandexpare generated for the intended execution time. - Signature input: confirm the private key is the matching
.p8file, has not been altered during Base64 conversion, and is loaded completely, including line breaks.
Do not paste a complete token into a ticket, log, repository, or article. A JWT contains enough metadata to reveal your integration design, and the private key must never be bundled into an app, build artifact, or source repository.
| Check | Local result | Remote result to compare |
|---|---|---|
alg and kid |
Expected algorithm and key ID | Same header, not a stale environment variable |
iss and aud |
Expected issuer and audience | Same values after CI interpolation |
iat and exp |
Generated at runtime | Not fixed in a cached file or snapshot |
| Private key bytes | Correct line breaks and complete content | Same content after secret injection |
| System clock | Trusted current time | Not affected by sleep, rollback, or snapshot restore |
Why can a JWT pass local validation while Apple rejects it?
Local validation usually proves only that the token structure and cryptographic signature are internally consistent. It may not prove that Apple recognizes the key ID, accepts the issuer, permits the audience, accepts the time window, or authorizes the operation. A token signed with the wrong but valid private key can pass a local signature check and still produce 401.
Compare the local Mac and remote Mac clocks against a trusted time source. Pay particular attention after a remote host resumes from sleep, restores a snapshot, or receives a manual time adjustment. Do not “fix” a clock problem by extending token validity indefinitely. Correct the system time and generate a fresh token at runtime.
Roles and resource access
Authentication and authorization are separate tests. A malformed or rejected token can produce 401. A valid token that lacks access to a resource or operation may produce a different error, but the distinction must come from the response rather than from guesswork.
Check whether API access is enabled for the relevant account, whether the selected key has a role covering the operation, and whether the target app belongs to the team associated with the key. Apple’s role and permission reference should be used for the exact role boundary.
Start with a minimal read-only request that the key is expected to perform. This isolates authentication from TestFlight upload, build processing, signing, and App Store Connect state. If the minimal request succeeds but the upload fails, stop rotating JWT keys and investigate the upload action or its separate credentials.
Does a fastlane API key failure always mean the JWT is malformed?
No. fastlane can receive a valid-looking API key configuration while reading the wrong file, an empty environment variable, a stale working directory, or a different secret scope in an unattended job. It can also invoke an action whose upload credentials differ from the App Store Connect API credentials you tested manually.
The official fastlane App Store Connect API guide is the reference for its supported API key parameters and authentication modes. Compare the exact values fastlane receives, not only the values stored in your password manager.
fastlane and remote Mac injection
A remote Mac introduces another failure layer because an interactive SSH shell and a CI Runner rarely load the same profile files. A value available in your terminal may be absent from a launch agent, scheduled job, or Runner process.
Inspect the effective configuration without printing secrets. Log whether a variable exists, its character count, the selected key ID, the current working directory, and the file permission state. Never log the private key itself or the complete key_content.
Check these fastlane inputs according to the mode you use:
key_idissuer_idkey_filepathkey_content- Environment variables that populate those fields
- The working directory used to resolve relative paths
- The user account that owns the Runner process
Use explicit absolute paths for protected files. If you inject a multiline .p8 value through Base64, decode it into a temporary protected location and verify the resulting line structure before the request. If you use an environment variable, confirm that the Runner receives it in the same scope as the fastlane process. Do not assume an SSH login profile applies to a non-interactive job.
A remote Mac is useful when you need a stable macOS environment for repeated release work, but it does not remove credential design responsibilities. Before choosing a remote Mac workflow for iOS development, decide where the secret enters the process, who can read it, how logs are retained, and how the host is replaced if access is lost.
The five-layer repair checklist
Use this checklist in order. Each item should produce evidence before you move to the next layer.
- [ ] Record the exact endpoint, HTTP status, Apple error code, request ID, trigger, and execution environment.
- [ ] Confirm the call is App Store Connect API rather than App Store Server API, In-App Purchase tooling, Transporter, or another service.
- [ ] Verify the key’s creation location, Key ID, team association, and intended operation in App Store Connect.
- [ ] Decode only the JWT header and payload in a safe local tool; verify
alg,kid,iss,aud,iat, andexpwithout exposing the complete token. - [ ] Confirm the private key matches the Key ID and survives file, environment-variable, or Base64 injection unchanged.
- [ ] Compare the local Mac and remote Mac clocks, especially after sleep, snapshot restoration, or manual time changes.
- [ ] Confirm the key role and app access scope using Apple’s account and permission pages.
- [ ] Run the smallest permitted read-only request from the same process that later runs fastlane.
- [ ] Repeat the test through the actual SSH or CI Runner entry point, not only an interactive terminal.
- [ ] Perform one real TestFlight upload after the minimal request succeeds.
- [ ] Preserve the request ID, sanitized configuration summary, execution account, key type, and failing stage.
- [ ] Revoke an old key only after a replacement has passed and you have confirmed which jobs depend on it.
This order prevents a common waste pattern: revoking a working key, creating another key with the same wrong role, and then blaming the new JWT when the remote Runner never received its private key.
Acceptance: authentication first, upload second
The correct acceptance test has two stages. First, send the smallest authenticated request that the selected key should be allowed to make. Second, run the real fastlane or TestFlight task from the same environment, with the same secret injection and user account.
| Acceptance stage | Pass condition | Failure interpretation |
|---|---|---|
| Minimal API request | Apple accepts the token and returns the expected API response | Continue checking endpoint, key, claims, clock, and role |
| Remote process repeat | SSH or CI Runner produces the same result as the local test | Investigate scope, path, user, or secret injection |
| Real upload | TestFlight task reaches the expected upload stage | Authentication is likely fixed; investigate upload-specific errors if it stops later |
| Evidence package | Request ID and sanitized inputs are retained | Re-run with better observability before changing credentials again |
If a fresh, correctly sourced key fails on the official minimal request from both local and remote environments, stop cycling through keys. Send Apple the request ID, timestamp, endpoint, error payload, team context, and sanitized JWT metadata. Exclude private keys, complete tokens, account passwords, app secrets, and host addresses.
For a persistent release workflow, the remote host should retain controlled secret injection, predictable Runner state, and logs that identify the failing phase without exposing credentials. If you need to compare available environments before committing, review KVMFLUX’s Mac access options and use the pricing information only after your real upload task has passed on the intended setup.
A self-managed Mac may be the better choice when you need long-term heavy workloads, physical devices, or direct hardware access. A remote Mac is less suitable if your workflow depends on USB peripherals or local network services. But for a temporary release test, a migration period, or a continuously available iOS build host, buying hardware adds upfront cost, maintenance, storage, and replacement obligations. Renting through KVMFLUX can be the more controlled option when you first validate the actual fastlane upload, secret injection, and Runner recovery path, then choose a weekly test environment or a monthly host for regular publishing.
Run Your App Store Automation on a Remote Mac
Use KVMFLUX to run JWT scripts and App Store Connect workflows in a consistent remote Mac environment. Test your API key, issuer, audience, permissions, and signing setup without changing your local machine. Deploy a ready-to-use Mac for reliable build, upload, and TestFlight automation. Start with KVMFLUX and validate your release pipeline from a clean remote Mac.