Your pipeline was green yesterday. Today the deploy job, on GitHub Actions or any other CI, dies with "Your session cookie has been expired" (fastlane's exact, slightly odd wording) and asks for a six digit code that nobody is there to type. Nothing in your Fastfile changed. What changed is on Apple's side: fastlane was authenticating with a stored Apple ID web session, and that session ran out.
Answer capsule: The durable fix is an App Store Connect API key. Keys skip two-factor prompts entirely and are not on a monthly clock, and most fastlane tools now accept them. Sessions minted with
fastlane spaceauthlast anywhere from one day to one month, are tied to the region where they were created, and fail without warning, so keep one around only for the few tools that still require an Apple ID login, mainlyproduceandpem.
Switch to an App Store Connect API key
fastlane's documentation recommends the API key as the primary way to authenticate. It talks to Apple's official App Store Connect API instead of the cookie session, so there is no two-factor prompt, no region sensitivity, and nothing that quietly expires next month.
Create the key in App Store Connect under Users and Access, in the Integrations tab. Pick a Team key rather than an Individual one; Individual keys cannot use the provisioning endpoints that match and sigh depend on. Download the .p8 file immediately, because App Store Connect will not offer it again after the page refreshes. Then reference it in your lane:
lane :beta do
app_store_connect_api_key(
key_id: "D383SF739",
issuer_id: "6053b7fe-68a8-4acb-89be-165aa6465141",
key_filepath: "./AuthKey_D383SF739.p8"
)
upload_to_testflight
end
The key can also come from environment variables or a JSON file, which tends to be cleaner in CI: store the .p8 content as a secret like any other credential. Enterprise teams set in_house: true; Apple also now offers a separate Enterprise Program API for certificate and profile automation on accounts that historically had no API option at all.
Support is broad but not total. The fastlane docs maintain a status table, and as of July 2026 it reads:
| Tool | API key support |
|---|---|
pilot, deliver, sigh, cert, match | Yes |
download_dsyms, app_store_build_number | Yes |
precheck | Yes, except in-app purchase checks (unsupported with Apple ID too) |
produce | No (Apple ID only, and listed as partial) |
pem | No (Apple ID only) |
If your pipeline builds, signs, uploads to TestFlight, and pushes metadata through deliver, a key covers all of it. Delete FASTLANE_SESSION from your CI secrets and move on.
Why the session keeps expiring
fastlane predates the official API, so its spaceship layer signs in the way a browser does: username, password, two-factor code, cookie. The authentication docs are blunt about the two limits Apple puts on that cookie. Validity varies between one day and one month depending on factors like geolocation, and a session is only valid within the region where it was created. Mint one on your laptop in Europe and consume it from a runner in the US, and it can be dead within a day. There is no expiry warning; the first signal is a red build. And since March 2021, Apple developer accounts cannot sign in at all without two-factor registered, so there is no going back to a plain password.
If you still need an Apple ID session
For produce and pem, generate a session ahead of time:
fastlane spaceauth -u ci@yourcompany.com
spaceauth walks through the two-factor prompt interactively, then prints a cookie string to store in the FASTLANE_SESSION environment variable on your CI. Two practical notes. Run spaceauth on the CI machine itself, or at least in the same region, or the session will expire much faster than a month. And keep FASTLANE_USER and FASTLANE_PASSWORD set alongside it; teams running this at scale found that the session alone is not enough. Then accept the ritual: a fresh session at least once a month, with a calendar reminder, because the only notification Apple gives you is a failing pipeline.
What app-specific passwords do and do not cover
An app-specific password passed through FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD is narrower than it sounds. fastlane hands it to iTMSTransporter, the tool that performs the physical binary upload in upload_to_testflight and upload_to_app_store. That upload is all it authenticates. The moment the same action needs anything more, setting release notes or distributing to testers, fastlane requires a real session and fails. It is a reasonable stopgap when the whole job is pushing an .ipa, and no help beyond that.
The agent path: no Apple credentials in CI
If an AI agent drives your releases, there is a way to hold this problem that does not involve CI secrets at all. Lance's App Manager is an autonomous App Store Connect operator that keeps an organization's credentials, both API keys and a live Apple session, on its own Mac fleet. An agent connected to the Lance MCP server delegates the operation in plain language through ask_app_manager: upload this build to TestFlight, update the listing metadata, create the in-app purchase. The work runs where the credentials already live, so there is no FASTLANE_SESSION to rotate and nothing in your pipeline to expire.
Apple sessions still expire on the operator's side; the difference is that the agent can see it coming. Before a long task, it calls get_account_health, which reports whether the Apple session is available or unavailable and returns a connect link for restoring it when it is down. A dead session becomes something the agent knows before committing to a twenty minute build and upload, not something it learns from a stack trace.
Setup is one command, with OAuth instead of exported keys:
npx add-mcp https://api.lance.app/mcp
The install guide covers Claude Code, Cursor, and other MCP clients.
Picking an approach
For a standard developer account, create an API key today and delete the session from CI; that ends the monthly breakage for nearly every pipeline. Keep a spaceauth session only for lanes that call produce or pem, mint it in the same region as your runners, and plan on refreshing it monthly. Reach for an app-specific password only when the entire job is uploading a binary. And if agents do the releasing, let an operator with a managed session handle App Store Connect instead of teaching your CI to babysit cookies.