Black Friday30% off your first month. See prices →
VibeView

Store Submission

Upload to TestFlight without a Mac

Aurimas Mickys — Founder, VibeViewPublished 2026-09-02Updated 2026-10-25

Getting a build into TestFlight usually means a Mac at the end of the pipeline, running Xcode’s Organizer or the Transporter app, and then a trip to App Store Connect to add the build to your tester groups. VibeView removes that last Mac and the trip.

This guide covers the upload and getting the build to your testers. For store screenshots, see Make App Store screenshots without a Mac. The same commands also send tvOS builds to TestFlight; Build an Apple TV app without a Mac covers that side.

A few TestFlight terms come up throughout. An internal group is people on your App Store Connect team. An external group is anyone you invite by email. What to Test is the note testers see with a build. Beta App Review is Apple’s check of a build before external testers can install it; internal groups don’t need it.

What you need

  • An Apple Developer Program membership. Apple requires it to sign and publish an app, with or without a Mac.
  • A VibeView account with the CLI installed. Run npm install -g vibeview, then vibeview login.
  • An App Store Connect API key, stored once in VibeView (next section). To send builds to TestFlight groups, it needs the App Manager or Admin role.
  • Signing credentials for the app, generated with that key or uploaded (see Signing without a Mac).
  • The app record in App Store Connect, created once per app. Apple only accepts uploads for apps that already exist there. Step 2 below creates it.

Create the App Store Connect API key

With the key, VibeView can create certificates and upload builds for your Apple team. You never share your Apple ID password or answer a two-factor prompt. You create the key in a browser, on any OS:

  1. Sign in to App Store Connect as the Account Holder or an Admin. Apple lets only those roles create team keys. If your team has never used the API, the Account Holder must first request access on the same page, and Apple approves it.

  2. Go to Users and Access → Integrations → App Store Connect API → Team Keys and generate a key with the App Manager or Admin role. Write down the Key ID and the Issuer ID shown on that page.

  3. Download the .p8 file and keep it safe. You can download it only once.

  4. Store it in VibeView. You need to be an Admin in your VibeView organization (a separate role from Apple’s Admin):

    vibeview credentials asc-key add --name team --key-id <KEY_ID> --issuer-id <ISSUER_ID> --file AuthKey_<KEY_ID>.p8

You can also add it in the dashboard under Builds → Stores. Once stored, the key can’t be viewed or downloaded again. vibeview credentials asc-key list shows its name (team here), key ID and issuer ID. To rotate it, add a new key with the same name. To stop using it, run vibeview credentials asc-key rm team.

Signing without a Mac

The usual way to get an iOS distribution certificate starts with Keychain Access on a Mac. With the API key stored, you skip that: the credentials generate command in step 1 below does it for you.

VibeView creates an Apple Distribution certificate for your team, or reuses the one it made before. It registers the bundle identifier with Apple if needed and creates an App Store provisioning profile. Production builds use the result right away.

Already have a .p12 and a .mobileprovision? Upload them instead with vibeview credentials add --platform ios --file cert.p12 --profile app.mobileprovision. App Signing covers both routes, expiry, and how a build picks its credential.

From source to your testers, step by step

  1. Create signing credentials (once).

    vibeview credentials generate --platform ios --name release --app <app-id>

    <app-id> is the app’s VibeView id, not anything from Apple. It is the appId in your vibeview.json, and vibeview list-apps lists them. If the app isn’t in VibeView yet, pass --bundle-id com.example.app instead of --app.

  2. Create the app record (once). In App Store Connect, open Apps → + → New App, choose the platform, and pick your bundle identifier. Step 1 registered it with Apple, so it is in the list now. No upload is needed; the record alone is enough.

  3. Check that Apple can accept the upload. This needs no build:

    vibeview submit --check --platform ios --bundle-id com.example.app --group "QA team"

    ready means App Store Connect knows the app and your stored key works. Otherwise it names the problem (no app record, no stored key, or a refused key) and how to fix it. From CLI 0.56.0, the check also lists the app’s TestFlight groups and the latest TestFlight build number. It also says what external testers would be waiting on, such as Beta App Review. With --group, it fails on a group name the app doesn’t have.

  4. Build a signed release.

    vibeview build --cloud --production --platform ios

    The first time, the CLI asks for the command that builds your signed release and where the .ipa lands, then saves both as the production profile in vibeview.json. In CI or with --json it can’t ask, so commit that profile first. The build runs on VibeView’s cloud Macs and is signed with your credential. The signed .ipa lands on your Builds page. To keep Apple from refusing a reused build number, see Build numbers, handled.

  5. Submit it and send it to testers.

    vibeview submit --platform ios --latest --group "QA team" \
      --what-to-test "Try the new checkout" --no-uses-encryption --wait-distribution

    The TestFlight flags need CLI 0.55.0 or later:

    • --latest picks your newest successful production build; --build <id> submits a specific one.
    • --group names a TestFlight group as it is called in App Store Connect. Repeat it for several.
    • --what-to-test is the note testers see, up to 4,000 characters (or --what-to-test-file). It is required when a group is external.
    • --no-uses-encryption answers Apple’s export compliance question for the app. Use --uses-encryption if the app uses encryption beyond the exempt cases; see Export compliance.
    • --wait-distribution keeps the command running until the build reaches your testers (next step).

    Without --group (or groups in vibeview.json), the build goes to TestFlight with no groups added, and you add testers in App Store Connect.

  6. Let it reach your testers. With --wait-distribution, the command keeps following the build after the upload, prints each step as it changes, and exits with a code your pipeline can read (below). Without it, the command exits when the upload ends and VibeView keeps following the build on its own; the build’s Store submissions card on its page in the dashboard shows where it is.

What happens after the upload

The upload succeeds once Apple accepts the binary. VibeView then follows the build in App Store Connect until it reaches your testers. The build’s Store submissions card shows each step:

  1. Processing by Apple. Apple processes the build. VibeView checks on its own.
  2. Export compliance. VibeView answers Apple’s encryption question from your Info.plist or your saved answer (see Export compliance).
  3. What to Test. Your notes are added to the build.
  4. Internal groups. The build is added to the internal groups you chose.
  5. Beta App Review. For external groups, the build is sent to Apple’s review.
  6. External groups. The build is added to the external groups you chose.

A step you didn’t ask for shows as Skipped. A problem in these steps never undoes the upload. It shows as Needs your action or Distribution failed instead.

The submission’s state shows on the build’s Store submissions card, in the Store column on the Builds page and on the Submissions tab:

StateWhat it means
TestFlight · ProcessingApple is processing the build
Sending to testersVibeView is answering export compliance, adding the notes, adding the build to your groups or sending it to Beta App Review
In Beta App ReviewSent to Beta App Review and added to your external groups. Apple releases it to external testers once it approves the build; internal groups already have it
Waiting on AppleNo Beta App Review verdict after 7 days. VibeView keeps checking every 6 hours
Available to testersDone: the build is in TestFlight with the groups you chose
In TestFlightDone, with no groups added
Needs your actionSomething only you can fix; the build’s Store submissions card says what and where
Distribution failedApple marked the build invalid, it expired, or the app or build no longer exists in App Store Connect

When the build needs your action, its Store submissions card names the fix and where to make it. Once you’ve made it, choose Check again. For things like missing beta test information or an open export compliance question, VibeView also looks again on its own every 30 minutes. When it needs your action lists every case.

Internal and external groups

  • Internal groups are people on your App Store Connect team, up to 100. They get the build as soon as Apple has processed it and export compliance is answered.
  • External groups can hold up to 10,000 people per app, across all its external groups. They need Apple’s Beta App Review first. Apple also needs What to Test notes, beta test information (a beta app description and a feedback email) and Beta App Review contact details. VibeView sends the build for review and adds it to your external groups. Apple releases it to them after it approves the build.

Each build stays testable for 90 days after upload.

The internal limit and the 90 days come from Apple’s TestFlight overview, and the external limit from Apple’s invite external testers page (both checked 25 October 2026). VibeView doesn’t change them.

Before you send to an external group for the first time, go through the TestFlight external testing checklist.

Export compliance

For each build, Apple asks whether the app uses encryption beyond the exempt cases, such as HTTPS and the system’s standard crypto. Testers can’t install the build until it’s answered. VibeView answers from the first of these that has an answer:

  1. Your Info.plist. When the app sets ITSAppUsesNonExemptEncryption, Apple reads it from there. Set it to NO only if the app uses no encryption beyond the exempt cases.
  2. The answer saved for the app. --uses-encryption or --no-uses-encryption on vibeview submit, or the question in the dashboard’s Submit dialog, saves your answer for every later build of the app.

With no answer anywhere, distribution stops at Export compliance, and the build’s Store submissions card asks the question. If the app does use non-exempt encryption, Apple needs your export compliance documents. You upload them in App Store Connect, and VibeView continues once Apple has them. See Apple’s export compliance overview.

Seeing Missing Compliance on a build in App Store Connect? TestFlight “Missing Compliance”: fix it once covers the Info.plist key, the Expo setting and how to decide your answer.

From the dashboard

Finished production builds on the Builds page have a Submit to store action (on the build’s own page, Submit to TestFlight). Once App Store Connect answers, the dialog shows:

  • Testers: the app’s TestFlight groups, each marked Internal or External. A group marked Gets every build (an App Store Connect setting) already receives every build, so it is shown checked. With no groups yet, Create a group makes one without leaving VibeView.
  • What to Test: required while an external group is chosen. Reuse last notes copies in the notes you sent last time, and What to Test, by language adds notes in other languages.
  • Apple’s export compliance question, the first time for an app.
  • Ready to send: whether the build number is newer than the latest TestFlight build, whether the app has groups and, for an external group, whether the beta test information and the Beta App Review contact details are filled in. Add it here opens the part that is missing.

The button reads Upload and send to testers when you chose a group. Use these settings next time keeps the key and the groups for the app’s next build.

TestFlight details (from the dialog, or Builds → Stores → your app) sets what Apple needs before external testing, without opening App Store Connect: the test information in each language, the Beta App Review contact and sign-in account, new groups, and external testers added by email.

From the command line, vibeview testflight info, set-info, set-review-contact, groups, create-group and add-testers do the same. They need CLI 0.57.0 or later.

To invite a list of external testers from a file, see Add TestFlight testers from the command line. For notes in other languages from a folder in your repository, see Release notes and What to Test in many languages.

Exit codes for CI

With --wait-distribution, the command checks every 15 seconds and exits:

ExitWhen
0 (settled)The build is in TestFlight with your groups, is in Beta App Review (Apple releases it to external testers after approval), or is still waiting for Apple’s verdict after 7 days
0 (time limit)Distribution hasn’t settled 2 hours 15 minutes after the wait began. The command prints the build page, where VibeView keeps following it
1Distribution needs your action (the message says what to do), failed or was stopped, or the upload itself failed
2Your API token stopped working during the wait. VibeView keeps following the build

To pick a submission up later, for example after vibeview submit --no-wait, run vibeview submissions status <id> --wait-distribution (CLI 0.56.0 or later). The CLI reference has every exit code and the --json events.

For a complete workflow that sends every version tag to TestFlight, see GitHub Actions to TestFlight, no Mac runner.

What stays in App Store Connect

VibeView uploads the build and gets it to your TestFlight groups. These stay in App Store Connect:

  • Submitting a version for App Store review, and phased release.
  • The App Store listing, screenshots, pricing and privacy labels.
  • Removing testers, renaming or deleting a group, adding internal testers and creating a public link.
  • Tester counts and who installed a build.

To ship a build that testers have to the App Store, add it to a version in App Store Connect and submit that version for review there. Done in the store’s console lists where each task lives.

Build numbers, handled

Apple refuses an upload whose build number (CFBundleVersion) it has seen before. Set "autoIncrement": true on the production profile in vibeview.json. Every production build then gets the next number from a counter VibeView keeps per app and platform. The number reaches your build as the VIBEVIEW_BUILD_NUMBER environment variable; wire it in once, as Automatic build numbers shows.

For an app already in TestFlight, VibeView checks your recent uploads in App Store Connect before handing out the first number, and starts above the highest. You can also seed the counter yourself with vibeview versions set.

Common errors and fixes

A refused submission says why in its log. Fix the cause and submit again.

Symptom or messageWhat it meansFix
Apple doesn’t know this app yetNo app record, or its bundle identifier doesn’t match the build’sCreate the record in Apps → + → New App with the build’s bundle identifier
Apple already has build N for this versionThe build number was used beforeRaise the build number and rebuild. With automatic build numbers on, set the counter to N before rebuilding (vibeview versions set N --app <app-id> --platform ios; the next build gets N + 1). Without them, raise CFBundleVersion in your project
No App Store Connect key is stored for this organizationNo API key yetvibeview credentials asc-key add, or Builds → Stores
This organization has several App Store Connect keysVibeView needs to know which key to usePass --asc-key <name>, or choose one in the dashboard
The New App form doesn’t list your bundle identifierThe identifier isn’t registered with Apple yetGenerate credentials first (it registers the identifier), or register the bundle identifier as an App ID under Certificates, Identifiers & Profiles at developer.apple.com
Generating credentials stops at the certificate limitApple caps each team at a small number of distribution certificatesRevoke an unused certificate at developer.apple.com, or upload your own .p12. VibeView never revokes one for you
A production build is refused for an expired credentialEvery credential that applies to the app has expiredGenerate or upload a replacement; profiles usually expire before certificates
A --group name is refusedThe app has no TestFlight group with that nameThe message lists the app’s groups. Names match in any case
The key can’t manage TestFlight testersThe App Store Connect key lacks the App Manager or Admin roleCreate a key with one of those roles, delete the old key in Builds → Stores and add the new one under the same name
Add beta test information or Add Beta App Review contact detailsApple needs these before external testingChoose Add it here on the build’s card, or set them in TestFlight details
Another build is in Beta App ReviewApple reviews one build of a version at a timeWait for that build’s verdict, or remove it from review in App Store Connect. VibeView never removes it for you
Apple rejected this build in Beta App ReviewApple’s reviewers turned the build down; their message is in App Store Connect (TestFlight → App Review)Fix the issue and send a new build with a higher build number

The check in step 3 catches a missing app record, a missing key or a wrong group name before you build. The full lists are in Common refusals and When it needs your action.

Other ways to upload to TestFlight

VibeView isn’t the only route. Here is what each tool’s own docs say (checked 23 October 2026).

On a Mac, Apple’s own tools do it: Xcode’s Organizer, altool (through xcrun) and the Transporter app, per Apple’s upload builds page. Without a Mac:

  • Transporter from the command line. Apple’s Transporter user guide lists Windows 11 and a Red Hat Linux distribution among the systems the command-line tool supports. It uploads a signed .ipa; you still need a way to build and sign one.
  • EAS Submit. Expo’s iOS submit docs say it works on macOS, Linux and Windows. It submits a production .ipa, which Expo recommends building with EAS Build in Expo’s cloud. Our EAS Build comparison covers the differences.
  • fastlane. upload_to_testflight accepts an App Store Connect API key and uploads through Apple’s Transporter. Its docs describe a Linux setup. Building the .ipa still needs Xcode somewhere, usually a hosted Mac CI runner.

VibeView keeps the build, the signing credentials, the upload and the path to your testers in one place.

Android and Fire TV use the same command

For Android and Android TV, vibeview submit --platform android --latest --track internal sends the build to Google Play, using a Google Play service-account key. Use --platform androidtv for an Android TV app. A track is the Google Play release channel the build goes to: internal, alpha, beta or production.

--release-notes adds release notes in your Google Play listing’s default language. --notes-dir adds other languages, one <code>.txt file per language, and needs CLI 0.58.0 or later. The same flag sends What to Test notes in other languages on TestFlight.

--release-status inProgress --rollout 0.1 starts a staged rollout, which you raise later in Play Console. In the dashboard, the Submit dialog offers Staged rollout with a percentage. It also offers Don’t send for review, for when Google asks you to send changes for review from Play Console yourself.

For Fire TV and Fire tablets, --store amazon sends an Android or Android TV .apk to Amazon Appstore review. The Store Submission docs cover the one-time setup for each store.

Submit a Fire TV app to the Amazon Appstore walks through the Amazon side step by step.

When you’d use this

The obvious case is a Windows or Linux team shipping a React Native app with no Mac at all. It also suits teams with Macs who would rather not keep a CI runner on the right Xcode or spread signing certificates across laptops.

Frequently asked questions

Can I upload an .ipa to TestFlight from Windows?

Yes. Build the .ipa as a VibeView production build, then run vibeview submit from Windows; the upload itself happens on VibeView’s side.

Why can't I see my build in TestFlight?

It is usually still processing. The build’s Store submissions card on its page, and the Store column on the Builds page, show TestFlight · Processing until Apple finishes. If Apple marks the binary invalid, the state turns to Distribution failed, and Apple emails the reason to your team’s Account Holder.

Can I send a build to external testers from the command line?

Yes. Pass an external group with --group and a note with --what-to-test. VibeView sends the build to Beta App Review and adds it to the group, and Apple releases it to those testers once it approves the build. --wait-distribution exits 0 while the build is in review.

Does VibeView submit my app for App Store review?

No. VibeView gets the build to your TestFlight testers. Adding it to an App Store version and submitting that version for review stay in App Store Connect.

Can I test the app before uploading it?

Yes, but not the signed .ipa: it is a device build, and simulators can’t install it. Run a simulator build of the same code on a cloud iPhone first, as in Build a React Native app without a Mac.

Try it yourself

Open a live iOS simulator, Android emulator, Apple TV, Android TV or Roku in your browser.

Start free