If your app uses Expo, you can build and submit it to TestFlight without owning a Mac. Other stacks can use a compatible hosted macOS runner, but their setup depends on the project and service. Every route starts with four pieces: a paid Apple Developer Program membership, a signed build, an App Store Connect record, and a tester group. Apple lists membership at $99 USD per year or the local-currency equivalent. The processed build may also need export-compliance information before it is eligible, and external testing needs TestFlight test information plus any required review. Internal testing can start without TestFlight App Review; the first build you submit for external testing requires a full review. The complete commands below cover Expo. The stack table gives the available route for other projects without pretending one procedure fits all of them.
The account and build steps are mechanical once you know the path. Passing review depends on the app a reviewer opens. In a fixed June–July 2026 study of 26 AI-built apps, at least 23 had no working automated tests; one shipped a checkout “test suite” that never reached the checkout code. That was a mixed web-app cohort, not a study of iOS submissions. Its useful lesson here is narrower: a green build says nothing about whether sign-in and the core workflow work on a stranger’s device.
Every screen name, status label, and number below was checked against Apple’s own App Store Connect documentation in August 2026.
How to submit an app to TestFlight, step by step
Six steps create four durable objects: a paid membership, a registered bundle ID with a matching App Store Connect record, a signed build, and a tester group. Apple then checks build eligibility and, for external testing, the required test information and review state.
- 01 Enroll in the Apple Developer Program. $99 USD per membership year or the local-currency equivalent, with possible waivers for eligible nonprofits, educational institutions, and government entities. A free Apple Account covers limited personal-device testing only, not TestFlight distribution.
- 02 Register the bundle ID. In the developer portal, under Certificates, Identifiers and Profiles, create an App ID whose bundle identifier matches the one in your project. Xcode can create it for you during upload, but doing it first means one fewer thing failing at the end.
- 03 Create the app record in App Store Connect. The New App dialog asks for platforms, app name, primary language, bundle ID, SKU, and whether user access is limited or full.
- 04 Produce a signed build. In Xcode that is Product > Archive, then Distribute App in the Organizer window, then TestFlight & App Store (or TestFlight Internal Only, which can be shared with your team but never submitted to the App Store). Validate App checks the archive first without submitting it. Without a Mac, a cloud build service signs and uploads the same binary.
- 05 Wait for processing, then answer export compliance. The build only becomes eligible once processing finishes and the Missing Compliance prompt is answered.
- 06 Assign the build to a group. An internal group can install it with no TestFlight App Review. An external group needs Beta App Description, Feedback Email, What to Test, and Beta App Review Information filled in first, then the build goes to review.
Sources for the numbers above: Apple’s membership page for the $99 and the waivers, and Apple’s guidance on inviting external testers, which requires an internal group to exist before you can create an external one (both checked 2026-08-05). Beta App Description and Feedback Email are required before you can share a build with external testers.
Whatever produces the archive, the output is the same thing: a signed binary sitting in App Store Connect, waiting for a status to change.
For screenshots, privacy details, pricing, and final review, use the separate no-Mac route to a public App Store release.
Submitting an Expo app to TestFlight without a Mac
TestFlight submission without a Mac is possible when your stack has a hosted build-and-submit path. Expo’s EAS is one documented example: it compiles and signs the iOS binary remotely, and EAS Submit runs on macOS, Linux, or Windows. Rork’s production path uses Expo for Rork Pro and a separate SwiftUI/Xcode path for Rork Max, so its release checks depend on which path generated the app. Other stacks need their own compatible CI service or a Mac-based build runner; an Expo command is not a universal Capacitor or native-iOS path. FlutterFlow’s production-readiness guide separates its app-store build capability from the device, backend, monitoring, and recovery evidence the shipped app still needs.
| What you built with | How the iOS binary gets built | Do you need a Mac? |
|---|---|---|
| Expo, or React Native on Expo tooling | EAS Build, then EAS Submit | No |
| A Lovable, Base44, Bolt, or Claude Code web app wrapped with Capacitor | A hosted CI service that offers macOS runners | No, if the service provides macOS runners |
| A Flutter app | Codemagic, or another hosted CI with macOS runners, which builds, signs, and publishes to TestFlight | No, if the service provides macOS runners |
| Bare React Native or native Swift | Xcode Cloud, Codemagic, Bitrise, or a rented Mac runner | A Mac, or a Mac you rent by the minute |
| Anything, archived locally | Xcode: Product > Archive, then Distribute App | Yes |
The Capacitor row is where most AI-built apps land. A prompt-built web app wrapped with Capacitor is still an Xcode project underneath, so it needs a macOS runner somewhere; the runner just does not have to be on your desk. Flutter works the same way: Codemagic documents signing the build with a distribution certificate on its own macOS build machine and publishing it to App Store Connect for TestFlight, with the beta-review submission and the tester groups set as options in its Flutter workflow editor (Codemagic’s Flutter App Store Connect publishing docs, checked 2026-08-16).
eas build --platform ios --profile production
eas submit --platform ios
The first command builds and signs the binary in the cloud; the second uploads it to App Store Connect. Both need a paid Apple Developer account. For unattended submissions, Expo documents an eas credentials --platform ios setup that configures an App Store Connect API key for EAS Submit. If you bring your own key, ascApiKeyIssuerId and ascApiKeyId identify it, while ascApiKeyPath points to its .p8 file. Keep that file out of version control and provide it through a secret file store such as EAS file environment variables. Give the key only the App Store Connect role permissions needed for submission. Apple says to store the key securely and revoke it immediately if it is lost or compromised. A committed key is compromised, so revoke it and issue a new one. EAS uploads the binary. TestFlight’s beta description, feedback email, export-compliance response, and tester setup still belong in App Store Connect. Store screenshots and privacy details are separate App Store release work, not something EAS Submit completes.
A cloud pipeline removes the local checkpoint where someone might open the app before submission. Add that checkpoint back deliberately: install the build on a physical device, read every permission prompt, and retest sign-in and the core workflow.
What each TestFlight build status means
After the upload lands, App Store Connect reports a build status. That status establishes the current processing, eligibility, review, testing, rejection, or expiry state; it does not by itself identify every failure that may have occurred before the build reached that state. Apple color-codes them: red means you have to do something or the build expired, yellow means a process is running, green means the build is being tested in TestFlight.
| Status | What it means | What to do next |
|---|---|---|
| Processing | The upload arrived and App Store Connect is preparing it. Apple’s published status list starts after this ends, so a build in Processing has no status yet. | Wait, then recheck the status. Processing alone does not establish whether the build will become eligible. |
| Missing Compliance | The build is missing export compliance documentation. | Answer the export compliance question on the build, or set the Info.plist key so the prompt stops coming back. |
| Waiting for Export Compliance Review, or In Compliance Review | Your compliance documentation is with Apple. | Nothing. Apple says no action is needed. |
| Invalid Binary | Apple received the build but it did not meet all the upload requirements. | Read the rejection email for the ITMS code, fix it, increment the build number, upload again. |
| Not Available for Testing | The provisioning profile is missing an application identifier. | Upload a new build that includes the application identifier. |
| Ready to Submit | The build can go to internal testers now, or be submitted to TestFlight App Review for external testing. | Assign it to an internal group, or complete the external test information and submit it. |
| Waiting for Review | The build is queued for TestFlight App Review. | Wait. It needs approval before external testing starts. |
| In Beta Review | TestFlight App Review is looking at it right now. | Wait. |
| Ready to Test | Your build can be tested by external and internal testers, but you have not added any testers yet. You may have opted to notify testers manually. | Add testers to the group, or send the notification. |
| Testing | At least one group or tester has the build. | Nothing. Collect feedback. |
| Rejected | TestFlight App Review rejected the build, and it can no longer be used in TestFlight. | Fix the reason given, then upload a new build. |
| Expired | The build passed its 90-day availability window. | Upload a newer build. Apple does not extend the window. |
(Status definitions from Apple’s build statuses reference, checked 2026-08-05.)
Two of these get misread constantly. Processing is not a rejection, it is a queue. Waiting for Review means Apple has the build and there is nothing left for you to do, which is a very different situation from a build that never appeared at all.
When the upload fails: the errors you will actually see
Start with six visible failure shapes. Each has a symptom you can observe and a next check.
The upload has no published status yet, or the build does not appear in App Store Connect. If the build appears as processing, wait and recheck its state. If no build appears at all, inspect the upload result and processing evidence. Missing Compliance is a different state: the build exists but is not eligible until you answer the prompt. An unaccepted agreement in Agreements, Tax, and Banking can also block a build from becoming usable; accept whatever is pending and re-check.
Code signing or provisioning profile mismatch. The archive was signed with a certificate or provisioning profile that does not match the bundle identifier or the distribution method. Xcode reports it during Distribute App; a cloud service reports it in the build log. The fix is always the same shape: make the App ID, the profile, and the bundle identifier in your project agree, then rebuild. Signing is one cause to check when a first submission fails before the build becomes eligible.
Invalid Binary. Apple accepted the upload, then failed it during processing. The status alone tells you nothing; the rejection email carries the ITMS code that does. Fix the cause, increment the build number, and upload again. App Store Connect will not accept the same version and build string twice.
ITMS-90683: missing purpose string. Your app, or a library inside it, touches a sensitive API (camera, microphone, location, photos, contacts) without a matching NS...UsageDescription string in the Info.plist. Third-party SDKs trigger this even when your own code never calls the API. Add the key with a sentence that names the real feature.
ITMS-91053: missing API declaration. Since May 1, 2024, an app that calls a required-reason API (file timestamps, user defaults, disk space, active keyboards, system boot time) has to declare approved reasons in an NSPrivacyAccessedAPITypes array inside a privacy manifest, PrivacyInfo.xcprivacy, in the root of the app bundle. Check Apple’s list of SDKs that require a privacy manifest, including repackaged versions of those SDKs. Apple’s privacy-manifest documentation also covers behavior that uses required-reason APIs, collects or enables collection of user data, or contacts tracking domains. For wrapper builds, inspect the finished bundle rather than assuming the wrapper authored every declaration. Make sure the required declarations reach the app bundle.
“No eligible builds” when adding a build to a group. The build exists but is not in an eligible state, which loops back to compliance, processing, or an expired build. Fix the status first; the group assignment is never the problem.
Internal vs external testers on TestFlight, and which one triggers Apple’s review
Internal TestFlight testers, up to 100 App Store Connect users with access to your content, can receive an eligible build without TestFlight App Review. External testing supports up to 10,000 people. The first build you submit for external testing requires a full review; later builds for the same version might not.
This is the distinction most walkthroughs bury, and it’s the one that decides how long you wait before a build reaches anyone outside your own team.
| Internal testers | External testers |
|---|---|
| Up to 100 people, each already holding a role on your App Store Connect team | Up to 10,000 people, invited by email or a public TestFlight link |
| No TestFlight App Review for an eligible internal build | The first external build requires a full review; later builds for the same version might not |
| Available after processing and assignment to the internal group | Available after any required review and assignment to the external group |
(Internal and external tester caps, build availability, and review rules come from Apple’s TestFlight overview, checked 2026-08-05.)
Internal testing is for App Store Connect teammates who know how the app is supposed to behave. External testing reaches people outside that team, and Apple reviews the build before the first external group can use it.
How your testers actually get the build
Your tester installs the free TestFlight app from the App Store first. Nothing you send works until they do, and this is the step that eats the most back-and-forth with non-technical testers.
After that, there are two ways in.
An email invite. You add the address in App Store Connect, Apple emails them, and they tap View in TestFlight. Opening that email on the device itself is the path that works; opening it on a laptop lands them on a web page with a redemption code they then have to type into the TestFlight app. On Apple TV, the code is the only path: open TestFlight, go to Redeem, enter it.
A public link. One URL, shareable anywhere, no email address needed and no UDIDs. You can attach enrollment criteria to the link so only the right devices and OS versions can join, and you can set a tester limit anywhere from 1 to 10,000 so the link stops accepting people at your number (Apple’s external-testers guide, checked 2026-08-05).
Two numbers worth knowing before you plan a beta: each tester can install your build on up to 30 devices, and you can share up to 100 builds at once (Apple’s TestFlight page, checked 2026-08-05).
TestFlight is also not iPhone-only. It covers iOS, iPadOS, macOS, tvOS, visionOS, watchOS, and App Clips, so an iPad-first or Mac build tests through the same path.
What TestFlight’s beta app review actually checks
Once you add the first build to an external group, it goes to TestFlight App Review under the App Review Guidelines. Later builds for the same version might not need a full review, which means Apple can still review some of them. Guideline 2.1 tells developers to test on-device, provide working demo access when login is required, keep backend services available, and remove crashes or obvious technical problems. A build that white-screens on a reviewer’s phone is the mobile version of an error that got caught, recorded nowhere, and never reached anyone who could see it: the flow never reached a device and account outside the build loop.
Only one app in that 26-app study had a working automated suite, and even that suite skipped sign-up, login, and the payment webhook. Automated tests from a web-app corpus do not predict an iOS review result, but the omitted workflows are exactly the ones a TestFlight build still needs exercised on-device. If the first person to try the real login is Apple’s reviewer, the first blocking bug becomes the review outcome. It is the same verification gap the seven-question launch check looks for, caught before launch rather than after it.
Beta review is the first time your app meets someone who wasn’t there when it was prompted into existence.
What happens after a hold, and the separate 2.5.2 dynamic-code question that can affect app-building platforms, is a different post’s job: which kind of App Store rejection you actually got, and what fixes each. The short version here is enough to act on: test the flow yourself, on the device, before Apple’s reviewer does it for you.
The pre-submission pass
Run this before you submit, not after Apple bounces the build back to you.
Apple’s version-and-build guidance is the source for the final upload rule in this checklist.
- 01 Walk sign-up and log-in on a real device yourself, start to finish, not in a simulator
- 02 Do the one core action your app exists for, and confirm it actually completes rather than silently no-opping
- 03 Write a demo account into your App Store Connect review notes if the app has a login, and confirm that account still works today
- 04 Rewrite every permission’s purpose string so it names the real feature, not the platform’s default placeholder text
- 05 After processing, clear the Missing Compliance prompt, and set the export-compliance key in your project so it stops coming back on every build
- 06 If Apple accepted a previous upload, increment the build number before uploading the next binary; App Store Connect uses that string to identify the build
On the export-compliance line: the key is ITSAppUsesNonExemptEncryption in your Info.plist. Set it to NO when your app, including every library it links against, uses only encryption that is exempt, which for most apps means HTTPS calls through the operating system’s own networking. Apple’s wording: without the key, “App Store Connect walks you through an export compliance questionnaire every time you upload a new version of your app. Including the key streamlines the app submission process.” (Apple on ITSAppUsesNonExemptEncryption, checked 2026-08-05). Answer the prompt once in App Store Connect and you clear this build; set the key and you stop seeing the prompt.
Getting held for one of them says nothing about whether your code is insecure in some deeper sense, the question a passing check on a builder-made app still cannot answer. It means the submitted path did not meet the review requirement a stranger just exercised.
Clearing TestFlight is one gate. The rest of what has to hold once real users show up instead of a reviewer (payments that verify server-side, backups you’ve actually restored from, errors that land somewhere you’ll see them) is a separate, longer list that starts before submission and keeps going after launch.
Common questions about submitting an AI-built app to TestFlight
Do I need an Apple Developer account for TestFlight?
Yes. TestFlight distribution requires active Apple Developer Program membership at $99 USD per membership year or the local-currency equivalent (Apple, checked 2026-08-05). A free Apple Account can install limited personal test builds, but it does not replace TestFlight distribution.
How long does TestFlight review take?
Apple does not publish a fixed TestFlight review turnaround. A live tracker of recent submissions, Runway’s App Review Times, showed TestFlight reviews clearing in about an hour on average when I checked on 2026-07-16, though threads on Apple’s own developer forums describe waits stretching to several days around major iOS releases, when review volume spikes. Treat it as variable and submit before the day you need external testers.
Review is also not the first wait. The build has to finish processing before any status appears, and Apple does not publish a processing time either. If no status appears yet, verify whether processing is still underway before inferring rejection. Internal testing avoids TestFlight App Review entirely, so use an internal group for the first device pass while the external build is being prepared.
Why does TestFlight say my build has expired?
Every TestFlight build is available for testing for up to 90 days after upload (Apple’s TestFlight overview, checked 2026-08-05). After that window the build is unavailable to testers. Upload and assign a newer build before the old one expires; Apple does not extend the 90-day window for that build.
Can I submit to TestFlight without a Mac?
Yes, if your toolchain offers a hosted iOS build and submit path. Expo documents EAS Build plus EAS Submit on macOS, Linux, and Windows. A native Xcode workflow still needs macOS, either on your own machine or on a CI runner configured for signing and upload.
My build says “Ready to Submit”. What does that mean?
It means the build is eligible: Apple defines it as a build that can go to internal testers, or be submitted to TestFlight App Review for external testing. Assign it to an internal group, or complete the external-testing information and submit it for review.
Why is my build not showing up in TestFlight?
Open the TestFlight tab in App Store Connect and read any status that appears, because it establishes the build’s current state. If the build is processing, it has no published status yet; if Missing Compliance appears, the unanswered prompt blocks eligibility. If the tab lists no build at all, investigate upload and processing evidence before assigning either cause.
What do I put in Beta App Review Information and What to Test?
Beta App Review Information is the reviewer’s way in: a demo account username and password if your app has a login, a contact email and phone number, and any notes a stranger needs to reach the main screen. What to Test is the note your testers see, so write the specific flows you want exercised in this build. Apple requires a Beta App Description and a Feedback Email before you can share a build with external testers (Apple on providing test information, checked 2026-08-05).
Test the demo account the same day you paste it in. A stale demo login is an easy, self-inflicted beta-review hold.
How many devices can each TestFlight tester use?
Up to 30. Each tester can install your beta on 30 of their own devices (Apple’s TestFlight page, checked 2026-08-05). That is a per-tester device count, not an extra cap on people, so it does not eat into the 100 internal or 10,000 external tester limits.
Is TestFlight safe for my testers to install?
Yes, on the install side: TestFlight is Apple’s own free app from the App Store, the build arrives through Apple rather than as a file someone sideloads, and the first build you send to an external group is reviewed against the App Review Guidelines before anyone outside your team can open it. Tell your testers the part they cannot see: Apple sends the developer crash logs, the tester’s name and email address, usage information, and any feedback they submit (Apple’s TestFlight tester page, checked 2026-08-16). It is still pre-release software on their real phone, and each build stops working at the end of its 90-day window.
Does TestFlight work for anything other than iPhone apps?
Yes. TestFlight covers iOS, iPadOS, macOS, tvOS, visionOS, and watchOS, plus App Clips (Apple’s TestFlight page, checked 2026-08-05). The submission path is the same for every one of those platforms; what changes is which platforms you selected on the App Store Connect record and which device your testers install on.
How do I move a TestFlight build to the App Store?
You do not upload a new binary. The build your testers used is the build you submit: open the app version in App Store Connect, scroll to the Build section, click the add button next to Build, choose the processed build in the Add Build dialog, click Done, then Save (Apple on choosing a build to submit, checked 2026-08-16). Then click Add for Review, put the version in a new or existing draft submission, and click Submit for Review; the status changes to In Review once App Review starts (Apple on submitting an app, checked 2026-08-16). Only one build attaches to a version, and you can swap it as often as you like until you submit. What TestFlight never asked for still has to exist first, though: screenshots, the store description, privacy details, age rating and pricing all belong to the full App Store release path rather than the beta one.
When every fix and release still depends on you
AxonBuild can trace the failure, repair the broken workflow, and ship the next change without rebuilding the parts that already work.