> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload iOS builds to TestFlight with Devin

> Set up App Store Connect, an API key, and Devin secrets so Devin can archive an iOS app on a macOS VM and upload the build to TestFlight.

Devin can archive an iOS app, sign it, upload it to TestFlight, and add the build to a beta group. It runs `xcodebuild` on a [macOS VM](/onboard-devin/environment/macos-support) and authenticates with an App Store Connect API key that you store as Devin secrets.

Before Devin can upload a build, you need to:

1. Set up your app in App Store Connect.
2. Create an App Store Connect API key.
3. Add the key and your Team ID to Devin as secrets.
4. Make sure Devin can reach Apple's servers.

<Note>
  Uploading to TestFlight requires macOS sessions. If you're on a Dedicated SaaS deployment, contact your account team to enable macOS VMs.
</Note>

## Requirements

| Requirement                        | Detail                                                                 |
| ---------------------------------- | ---------------------------------------------------------------------- |
| Apple Developer Program membership | A paid membership for the team that owns the app.                      |
| App record in App Store Connect    | An app with the same bundle ID as the Xcode project.                   |
| App Store Connect API key          | A team key with the **App Manager** role.                              |
| Devin secrets                      | `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_PRIVATE_KEY`, and `APPLE_TEAM_ID`. |
| Network access                     | Devin's macOS VM can reach Apple's servers.                            |

## Set up App Store Connect

Do these steps in the Apple Developer portal and App Store Connect. Devin can't do them for you: most require an Account Holder or Admin, and some need two-factor authentication on a person's Apple Account.

<Steps>
  <Step title="Accept the latest agreements">
    The Account Holder signs in to [App Store Connect](https://appstoreconnect.apple.com) and accepts any pending agreements in **Business**. Uploads fail while a required agreement is pending.
  </Step>

  <Step title="Register the bundle ID">
    In the [Apple Developer portal](https://developer.apple.com/account/resources/identifiers/list), go to **Certificates, Identifiers & Profiles → Identifiers** and register an App ID that matches the bundle ID of your app target. Skip this step if the identifier already exists.
  </Step>

  <Step title="Create the app record">
    In App Store Connect, go to **Apps**, click **+**, and select **New App**. Choose the platform, name, primary language, the bundle ID from the previous step, and a SKU. TestFlight uploads fail if no app record exists for the bundle ID.
  </Step>

  <Step title="Create a beta group">
    Open the app, go to **TestFlight**, and create a group:

    * **Internal testing**: testers must be users on your App Store Connect team. Builds are available as soon as processing finishes.
    * **External testing**: testers can be anyone with an email address or a public link. Fill in **Test Information** (beta app description, feedback email, and review contact details) first. The first build for each version goes through Beta App Review.
  </Step>

  <Step title="Answer export compliance">
    Every build must answer the export compliance question before testers can install it. To skip the question for each build, set `ITSAppUsesNonExemptEncryption` in your app's `Info.plist`. Set it to `NO` if the app uses only exempt encryption, such as HTTPS.
  </Step>
</Steps>

## Create an App Store Connect API key

Devin authenticates with an App Store Connect API key, not an Apple Account. The key doesn't need two-factor authentication.

<Steps>
  <Step title="Enable API access">
    In App Store Connect, go to **Users and Access → Integrations → App Store Connect API**. If API access isn't enabled yet, the Account Holder clicks **Request Access** and accepts the terms.
  </Step>

  <Step title="Generate a team key">
    Under **Team Keys**, click **+**. Enter a name, such as `Devin TestFlight`, and select the **App Manager** role. You need the Admin role to generate a key. If Xcode can't create a distribution certificate with an App Manager key, generate a key with the **Admin** role instead.
  </Step>

  <Step title="Download the private key">
    Click **Download** next to the new key to save `AuthKey_<KEY_ID>.p8`. Apple lets you download the file only once. If you lose it, revoke the key and generate a new one.
  </Step>

  <Step title="Copy the Issuer ID and Key ID">
    The **Issuer ID** is shown above the keys table. The **Key ID** is in the key's row.
  </Step>

  <Step title="Find your Team ID">
    In the [Apple Developer portal](https://developer.apple.com/account), go to **Membership details** and copy the **Team ID**. It's a 10-character string, such as `A1B2C3D4E5`.
  </Step>
</Steps>

<Warning>
  Anyone who has the `.p8` file can upload builds and manage TestFlight for every app on your team. Store it only in Devin secrets, and don't commit it to a repository.
</Warning>

## Add secrets to Devin

Add the following values as raw secrets on the [Secrets page](https://app.devin.ai/settings/secrets):

| Secret name       | Value                                                                                                                     | Where to find it                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `ASC_KEY_ID`      | Key ID, such as `2X9R4HXF34`                                                                                              | App Store Connect → Users and Access → Integrations → App Store Connect API |
| `ASC_ISSUER_ID`   | Issuer ID, a UUID                                                                                                         | Same page, above the keys table                                             |
| `ASC_PRIVATE_KEY` | Full contents of `AuthKey_<KEY_ID>.p8`, including the `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` lines | The file you downloaded when you created the key                            |
| `APPLE_TEAM_ID`   | Team ID, such as `A1B2C3D4E5`                                                                                             | Apple Developer portal → Membership details                                 |

To copy the private key on a Mac, run:

```bash theme={null}
pbcopy < AuthKey_<KEY_ID>.p8
```

Choose a scope for the secrets:

* **Organization**: every session in your organization can use the key. Use this scope when the team ships builds with Devin.
* **Personal**: only sessions you start can use the key.

Secrets are only available in sessions started after you add them. For more on scopes, see [Secrets](/product-guides/secrets).

## Allow network access

Devin's macOS VM needs to reach Apple's servers to sign and upload builds. If your organization uses a restricted network policy, add `api.appstoreconnect.apple.com` and the other Apple hosts that `xcodebuild` uses for signing and uploads. Allowing `*.apple.com` covers them.

A blocked host usually shows up as an authentication error, not a network error. See [Troubleshooting](#troubleshooting).

## Upload a build

Start a macOS session and ask Devin to upload a build:

```text theme={null}
Archive the MyApp scheme and upload it to TestFlight. Use a build number
higher than the latest build in App Store Connect, add the build to the
"QA" group, and send me the build number when it's done.
```

Devin uses the secrets to do the following:

1. Write `ASC_PRIVATE_KEY` to `~/.appstoreconnect/private_keys/AuthKey_$ASC_KEY_ID.p8` with `0600` permissions.
2. Check the latest build number with the App Store Connect API and pick a higher one.
3. Archive the app with `xcodebuild archive`.
4. Sign and upload the build with `xcodebuild -exportArchive`.
5. Wait for processing to finish, then add the build to the beta group with the App Store Connect API.

The export step uses an export options plist with `destination` set to `upload`:

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>method</key><string>app-store-connect</string>
  <key>destination</key><string>upload</string>
  <key>teamID</key><string>A1B2C3D4E5</string>
  <key>signingStyle</key><string>automatic</string>
</dict>
</plist>
```

And these commands:

```bash theme={null}
xcodebuild -project MyApp.xcodeproj -scheme MyApp \
  -configuration Release -destination 'generic/platform=iOS' \
  -archivePath build/MyApp.xcarchive \
  CURRENT_PROJECT_VERSION=<build-number> archive

xcodebuild -exportArchive \
  -archivePath build/MyApp.xcarchive \
  -exportOptionsPlist ExportOptions.plist \
  -exportPath build/export \
  -allowProvisioningUpdates \
  -authenticationKeyID "$ASC_KEY_ID" \
  -authenticationKeyIssuerID "$ASC_ISSUER_ID" \
  -authenticationKeyPath ~/.appstoreconnect/private_keys/AuthKey_$ASC_KEY_ID.p8
```

`-allowProvisioningUpdates` lets Xcode create the distribution certificate and provisioning profile with the API key, so the VM doesn't need signing assets installed.

If your repository already has a release script, such as a `fastlane` lane or a `make` target, tell Devin to use it. `fastlane` accepts the same key through `app_store_connect_api_key`.

### Save the steps in your blueprint

To avoid repeating the instructions in every prompt, add them to the `knowledge` section of your repository's [blueprint](/onboard-devin/environment/blueprints):

```yaml theme={null}
runs-on: macos

knowledge:
  - name: testflight
    contents: |
      To upload a TestFlight build:
      1. Write $ASC_PRIVATE_KEY to ~/.appstoreconnect/private_keys/AuthKey_$ASC_KEY_ID.p8 (chmod 600).
      2. Use a build number higher than the latest build in App Store Connect.
      3. Archive the MyApp scheme, then run xcodebuild -exportArchive with
         ExportOptions.plist (destination: upload, teamID: $APPLE_TEAM_ID),
         -allowProvisioningUpdates, and the -authenticationKey* flags.
      4. Add the build to the "QA" beta group.
```

## Troubleshooting

**`No Accounts with App Store Connect Access` or `Failed to Use Accounts` during export.** Check network access first. If the VM can't reach Apple's servers, `xcodebuild` reports this error even when the key is valid. Look for `ITunesConnectFoundationErrorDomain Code=-1003` in the lines above it. If the network is fine, check that the key has the App Manager role.

**`No profiles for '<bundle ID>' were found`.** The bundle ID isn't registered for the team in `APPLE_TEAM_ID`, or no app record exists for it in App Store Connect.

**`The provided entity includes an attribute with a value that has already been used`.** The build number was already used for this version. Upload again with a higher build number.

**The build uploads but testers can't install it.** Check the build in App Store Connect → TestFlight. It may still be processing, be missing export compliance information, or be waiting for Beta App Review.

**Xcode can't create a distribution certificate.** The key's role doesn't allow managing certificates. Generate a key with the Admin role, or have an Admin create the distribution certificate in the Apple Developer portal.

**Environment variables are empty in the session.** Secrets are only available in sessions started after you add them. Start a new session.
