← Blog

Upload an IPA to TestFlight from Windows, safely

Apple's Transporter command-line tool runs on Windows and uploads an .ipa with an App Store Connect API key. How to use it, the other routes from Windows, and why no uploader needs your Apple ID.

· 8 min read

Need a Mac for this? NoMac gives your agent full macOS with Xcode for $0.90 an hour, billed per minute. Start now →


You can upload an .ipa to TestFlight from a Windows PC with Apple's own tool. The Transporter command-line tool runs on Windows 11, takes an App Store Connect API key, and Apple documents app uploads with it. What Windows cannot do is build the .ipa, and Transporter on Windows needs one more file, which Xcode writes when it exports the app.

This page covers the official routes, what the popular uploader tools ask for instead, and how to build, sign and upload from Windows through a cloud Mac.

The short version:

  • ·You have the .ipa and the AppStoreInfo.plist Xcode exported with it: use Transporter for Windows with an API key.
  • ·You have only the .ipa: upload it through the App Store Connect API (the open-source asc CLI does this), or with Expo Submit for an Expo app.
  • ·You have no .ipa yet: it has to be built on a Mac, which can also upload it.
  • ·In every case, give a tool an API key, never your Apple ID and an app-specific password.

What runs on Windows

Xcode, the Transporter app and altool run only on macOS. Apple's upload page calls Transporter “a macOS app”, and the altool guide says altool needs a Mac. The command-line Transporter is a separate, Java-based tool that Apple also uses for book, music and video deliveries. Its user guide (version 4.2, revised December 2025) lists macOS, Windows 11 or later and Red Hat Enterprise Linux, and says: “App upload supports Linux and Windows in addition to macOS.”

Tool for WindowsSigns in withNeeds
Transporter command-line toolAPI key, or Apple ID and app-specific passwordThe .ipa and its AppStoreInfo.plist
App Store Connect APIAPI keyThe .ipa, its version and build number
Expo SubmitApple ID, or your API keyA signed .ipa and an Expo account

What the uploader tools ask for

Search for this and most results are guides from uploader tools. AppUploader's guide asks for your Apple ID and an app-specific password, and says not to use your account password. RoamUpload's says to sign in to your Apple Developer account in its browser uploader. Neither page says where those credentials are kept.

An app-specific password is safer than your real password, but it is not limited to uploads. Apple's support page describes it as the way apps from other developers sign in to your Apple Account, for things like the mail, contacts and calendars you keep in iCloud. With Apple's own altool, the same Apple ID and password can also list your apps and upload new App Store text (its --list-apps and --app-store-text commands). The password works until you revoke it, or until you change your Apple Account password, which revokes every app-specific password at once.

An App Store Connect API key is the better thing to hand out. It is not a way into your Apple Account or iCloud: it works with the App Store Connect API and Apple's upload tools, it has a role, and you revoke it on its own in App Store Connect. If you have already given an uploader an app-specific password, remove it at account.apple.com, under Sign-In and Security, App-Specific Passwords. To create a key, see the App Store Connect API key explained.

Route 1: Apple's Transporter on Windows

Install it from the Windows link in Apple's guide, under Install Transporter (on November 4, 2026 it served iTMSTransporterToolInstaller_4.3.0.4.exe). It installs to C:\Program Files\itms by default. Then:

  • ·Put the key's file, AuthKey_ABCD123456.p8, in C:\Users\you\private_keys. Transporter looks there for the key you name.
  • ·Have the .ipa and the AppStoreInfo.plist from the same export.
  • ·Run the upload in PowerShell, from the install folder:
cd "C:\Program Files\itms"
.\iTMSTransporter.cmd -m upload `
  -apiIssuer 69a6de70-0000-0000-0000-000000000000 `
  -apiKey ABCD123456 `
  -assetFile C:\builds\MyApp.ipa `
  -assetDescription C:\builds\AppStoreInfo.plist `
  -v eXtreme

The catch is -assetDescription. The guide says it is “required for Linux and Windows App uploads, but not for macOS App uploads”, and that it is “the file generated by exporting from Xcode”. With xcodebuild, the export writes it when its options turn on generateAppStoreInformation, which xcodebuild -help describes as App Store Information “for uploading with iTMSTransporter” and which defaults to off. So whoever exports the .ipa on a Mac has to turn it on and send you both files. With only the .ipa, use route 2.

Keep the -v eXtreme log: the guide says Apple needs it if you ask for delivery help. Windows uploads have not worked for everyone; a developer on Apple's forums reported failed attempts in December 2025.

Route 2: the App Store Connect API

Since version 4.1, the App Store Connect API can take the upload itself, through its build uploads. It is plain HTTPS with an API key, so it works from any system and needs no AppStoreInfo.plist. There are three calls:

  • ·POST /v1/buildUploads with the app, the version, the build number and the platform.
  • ·POST /v1/buildUploadFiles with the file's name, size and type. The answer lists upload operations: addresses and the byte range to send to each.
  • ·Send each part, then PATCH /v1/buildUploadFiles/{id} with uploaded: true. Apple then processes the build like any other upload.

You do not have to write this yourself. The open-source asc CLI (MIT license) uses these calls for asc builds upload, and its README points Windows users to the binaries on its releases page. After asc auth login with your key ID, issuer ID and .p8 file:

asc builds upload --app "123456789" --ipa "C:\builds\MyApp.ipa"

--app is the app's Apple ID, the number under App Information in App Store Connect.

Route 3: Expo Submit

For an Expo or React Native app, Expo Submit (the eas submit command) “works on macOS, Linux, and Windows”. Expo's docs say it accepts any valid, signed .ipa, including one not built with Expo:

eas submit --platform ios --path ./my-app.ipa

It needs an Expo account and the bundle identifier in app.json. By default it asks for your Apple ID on the first run. To give it a key instead, set ascApiKeyPath, ascApiKeyIssuerId and ascApiKeyId in eas.json, and keep the .p8 out of git.

No .ipa yet: it is built on a Mac

All three routes start from a signed .ipa, and making one takes Xcode, which runs only on macOS. Apple also sets a floor: from April 2027, iOS apps uploaded to App Store Connect must be built with the iOS 27 SDK or later. The Mac can be your own, a CI service's, Expo's cloud build for an Expo app, or a cloud Mac. The Mac that builds the .ipa can upload it too, and then none of the Windows workarounds above matter.

The NoMac route from Windows

NoMac gives you a cloud Mac with Xcode 27.0, run from your Windows terminal. It is $0.90an hour, billed per minute from prepaid credit, with no subscription. Your App Store Connect API key stays on NoMac's server: NoMac creates the upload at Apple, and the Mac gets only Apple's upload addresses for this one file, which need no key. It is still a key you give to a service, so the same rules apply: no wider role than you need, and revoke it in App Store Connect when you stop using NoMac.

Once

npm install -g @nomac/cli
nomac login          # approve the code in your browser
nomac credits buy    # a payment link, if the account has no credit
nomac apple          # whether your App Store Connect key is connected
  • ·The CLI needs Node 20 or later, and works in PowerShell and cmd.
  • ·Connect your App Store Connect API key at nomac.app/dash/apple.
  • ·Create the app in App Store Connect (Apps, +, New App). Apple's API cannot create it, so a person does this once; NoMac answers with these steps if it is missing.

Build, sign and upload

cd C:\code\MyApp
nomac start          # a Mac, ready in about a minute
nomac sync .         # the project, into ~/drive/src/MyApp on the Mac
nomac sign           # profiles, a build settings file and the build commands
nomac ssh            # a shell in the project: paste the lines sign printed, then exit
nomac testflight build/export/MyApp.ipa --encryption no
nomac stop

nomac sign reads the project on the Mac, registers its bundle IDs if needed, makes the App Store profiles and installs them with your account's distribution certificate. It leaves the project unchanged and prints an xcodebuild archive line and an xcodebuild -exportArchive line, which write the .ipa to build/export. It also prints Flutter and fastlane versions of the same build.

A native Xcode project builds as it is. Sync leaves node_modules and Pods behind, so a React Native app needs its install steps run on the Mac before nomac sign, and an Expo app also needs npx expo prebuild -p ios for its ios folder.

nomac testflight waits until the build is on TestFlight. Once it says Apple is processing the build, the Mac is no longer needed: press Ctrl-C, run nomac stop and check later with nomac testflight --id tfu_…. NoMac then adds the build to the app's internal TestFlight group, so the team's testers can install it. nomac stop deletes the Mac; the synced project stays on your 20 GB drive for the next session.

An .ipa built somewhere else

If the .ipa is already signed for the App Store, for an app in the Apple account you connected, the Mac only sends it. Sync skips folders named build and folders your .gitignore lists, so put the file in a folder of its own:

mkdir C:\upload
copy C:\builds\MyApp.ipa C:\upload\
cd C:\upload
nomac start
nomac sync .
nomac testflight MyApp.ipa --encryption no
nomac stop

Billing has a one-minute minimum. Five minutes of Mac time cost 7.5¢; a session that runs 15 minutes costs 22.5¢.

The agent route

A coding agent can do the same through NoMac's MCP tools: start_mac, then set_up_signing, the build with exec_mac, and send_to_testflight with the .ipa's path on the Mac. Calling send_to_testflight again with the request_key it returned follows the upload through sending, processing and on_testflight. A step only a person can take, such as creating the app record, comes back as needs_human with its link.

# Claude Code
claude mcp add nomac -- npx @nomac/cli mcp

# or hosted: https://mcp.nomac.app/all (the Mac, signing and TestFlight)
Start a NoMac Mac, put this project on it and set up signing. Archive and
export with the commands set_up_signing returns, then send the .ipa to
TestFlight. Ask me the export-compliance question; do not answer it yourself.
Stop the Mac once Apple is processing the build.

For an .ipa you already have, the agent can make an upload link (create_mac_link on the hosted server, create_mac_upload_link in nomac mcp): a page where you pick the file, up to 2 GiB, and it lands on the Mac. More on this setup in give your agent TestFlight.

Two things each build needs

Export compliance

Apple asks whether the app uses encryption that is not exempt. Typically, the system's own encryption, such as HTTPS through URLSession, is exempt and proprietary encryption is not (Apple's rules). Without the ITSAppUsesNonExemptEncryption key in the app's Info.plist, App Store Connect asks for every new version. Setting the key answers it in the app itself.

With NoMac, --encryption no or --encryption yes (over MCP, uses_non_exempt_encryption) answers it once per app: NoMac remembers the answer and sets it on each build it sends, and does not ask when the Info.plist has the key. The answer falls under U.S. export law, so it is yours to give, not an agent's guess.

A new build number

Apple identifies a build by its version (CFBundleShortVersionString) and build number (CFBundleVersion). Apple's technote on the two (archived, no longer updated) says build numbers must be unique within a version, each one higher than the last. Once Apple has a build number for a version, it cannot be used again for that version.

nomac sign reserves the next free number for the app and its commands set it through CURRENT_PROJECT_VERSION. If the app's Info.plist has a fixed CFBundleVersion, set the number there instead. nomac testflight checks before it sends: a number Apple already has comes back with the next free one, before any time goes into the upload.

Which one to use

  • ·Both files from a Mac export: Transporter for Windows with an API key.
  • ·Only the .ipa: the App Store Connect API through asc, or Expo Submit for an Expo app.
  • ·No .ipa, or a build you want to fix and send again: a Mac that builds and uploads, your own or a cloud Mac.
  • ·Your Apple ID and an app-specific password: none of these routes needs them.

Every route here works with the same key. If you do not have one yet, the App Store Connect API key explained covers creating it, the role to pick and revoking it. The commands are in the CLI reference.

Give your agent a Mac in a minute.

Full macOS with Xcode and root, over MCP, CLI, API or SSH. $0.90 an hour, billed per minute. The Mac is deleted at stop; your 20 GB drive is kept for the next one. No Apple account needed to start.

Get started from $10

7-day money-back guarantee on your first purchase. Credit never expires.

Connect your agent →