AppForShare

Share a Bitrise build with testers: IPA, APK and AAB

· 9 min read

Share a Bitrise build with testers: IPA, APK and AAB

Bitrise is good at turning a commit into a signed .ipa, .apk or .aab. The next question is how that file reaches the people who test it: a client on an iPhone, a QA vendor on three Android phones, a product manager who just wants a link. This guide shows how to send every Bitrise build to testers with one Script step: an install link and QR code per build, a stable channel link, release notes from the commit, a store readiness gate, and outputs that later steps can post to Slack or a pull request.

AppForShare is the tool we make, so the upload part of this guide uses it. Bitrise has its own answer too, Release Management, and we cover where it is the better fit near the end.

What you need

  • A Bitrise Workflow that already builds your app. The Xcode Archive step sets BITRISE_IPA_PATH. The Android Build and Gradle Runner steps set BITRISE_APK_PATH or BITRISE_AAB_PATH, depending on what you build.
  • An AppForShare API token. Create it in the dashboard under Settings, API tokens. Tokens, and so uploads from CI, are part of Pro ($12 a month) and Team ($39 a month with 5 seats).
  • Node on the build machine. The step runs the AppForShare CLI through npx. If your stack has no Node, add a step that installs Node 18 or newer first.

An AppForShare step for the Bitrise StepLib is in review. Until it is listed, the Script step below does the same job with the same CLI.

Step 1: store the token as a Bitrise Secret

In the Workflow Editor open Secrets and add APPFORSHARE_TOKEN with your token as the value. Leave "Expose for pull requests" off unless you trust every pull request that can start a build. The CLI reads the token from the environment, so it never appears on the command line or in the build log.

Step 2: add a Script step after the build step

For an iOS Workflow, put a Script step right after Xcode Archive and paste this:

#!/usr/bin/env bash
set -euo pipefail
npx --yes appforshare upload "$BITRISE_IPA_PATH" \
  --notes "${GIT_CLONE_COMMIT_MESSAGE_SUBJECT:-}" \
  --channel beta

That is the whole integration. The CLI uploads the file in parts (a broken connection resumes instead of starting again), waits until AppForShare has processed the build, and prints the install link. Testers open the link on the phone and install over the air. There is no tester account and no app to install first.

GIT_CLONE_COMMIT_MESSAGE_SUBJECT is the first line of the commit message, set by the Git Clone step. It becomes the release notes testers see on the install page. If you would rather write notes by hand, put them in a file and pass --notes-file notes.txt.

Android: APK or AAB

Swap the path and keep everything else:

npx --yes appforshare upload "$BITRISE_AAB_PATH" \
  --notes "${GIT_CLONE_COMMIT_MESSAGE_SUBJECT:-}" \
  --channel beta

An .apk is shared as it is. An .aab cannot be installed on a phone directly, so AppForShare runs Google's bundletool and turns it into one universal APK for testers. That APK is signed with your workspace's AppForShare test key, or on Pro and Team with your own upload keystore if an Admin added it in Settings. A test key APK cannot update the Play version of your app, so testers uninstall that first; the install page says which key was used. More on this in installing an .aab on Android.

Step 3: use a channel for a link that never changes

--channel beta puts the build into a channel called Beta, created the first time you use it. A channel has one stable link and QR code that always opens the newest build put into it. Pin that link in your team chat once and testers never ask "which link is the latest?" again. If an iOS and an Android project are paired, the same channel link sends iPhones to the iOS build and Android phones to the Android build. Putting an older build back into a channel is a rollback of the link (apps already installed do not change). Channels are part of Pro and Team; see Beta and Live channels.

Step 4: stop the Workflow on a store blocker

Every build uploaded with an account goes through a store readiness check: iOS SDK version, minimum iOS version, privacy manifests, Play target API level, 16 KB page size for native libraries, debuggable or debug signed builds, profile expiry and more. The result is ready, review, blocked or unknown. It is guidance, not a promise that Apple or Google will approve the build.

Add --fail-on blocked and the CLI exits with code 2 when the result is blocked. The Script step fails, so the Workflow stops before anything that would send the build further, such as a deploy to TestFlight. The build itself stays uploaded and its link works, so you can open the build page and read the finding. --fail-on review is stricter and also fails on "needs review".

Step 5: pass the link to later steps

With --json the CLI prints one line of JSON with url, slug, readiness, findings and, when you used a channel, channel.url. Export what you need with envman so the next steps (a Slack message, a comment on the pull request) can use it:

#!/usr/bin/env bash
set -uo pipefail
out=$(npx --yes appforshare upload "$BITRISE_IPA_PATH" \
  --notes "${GIT_CLONE_COMMIT_MESSAGE_SUBJECT:-}" \
  --channel beta --fail-on blocked --json)
code=$?
[ -n "$out" ] || exit "$code"

field() {
  printf '%s' "$out" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s);const v=process.argv[1].split(".").reduce((o,k)=>o==null?o:o[k],j);process.stdout.write(v==null?"":String(v))})' "$1"
}

envman add --key APPFORSHARE_URL --value "$(field url)"
envman add --key APPFORSHARE_READINESS --value "$(field readiness)"
channel_url=$(field channel.url)
[ -n "$channel_url" ] && envman add --key APPFORSHARE_CHANNEL_URL --value "$channel_url"

exit "$code"

The script keeps the CLI's exit code, so --fail-on blocked still fails the step, but it exports the link first. A later step marked to run even when earlier steps failed can then post "build blocked, see $APPFORSHARE_URL" instead of nothing. The AppForShare step that is in review exports the same values under the same names (APPFORSHARE_URL, APPFORSHARE_SLUG, APPFORSHARE_CHANNEL_URL, APPFORSHARE_READINESS), so later steps will not need to change when you switch to it.

Step 6: tell the right people

A link nobody hears about is not much use. Three options, from light to managed:

  • Email the link. --email [email protected],[email protected] sends one plain email per address with the app, version, release notes and an Install button, to up to 5 addresses per upload (Pro and Team). See email the install link.
  • Tester groups. --groups "QA,Client" shares the build with named tester groups once it is ready, and each tester gets the new build email (Team). Testers still need no account; private projects open only for invited testers. See tester groups.
  • Webhooks. On Pro and Team, AppForShare can post build ready, build failed, installs and tester feedback to Slack, Microsoft Teams, Google Chat, Discord or your own endpoint, without any extra step in the Workflow.

Options at a glance

OptionWhat it doesPlan
--notes / --notes-fileRelease notes on the install pagePro and Team
--channel betaPuts the build into a channel with a stable linkPro and Team
--fail-on blockedExit code 2 on a readiness blocker; build stays uploadedPro and Team
--jsonOne line of JSON for later stepsPro and Team
--email a,bEmails the install link to up to 5 addressesPro and Team
--groups "QA"Shares the build with tester groupsTeam
--passwordPuts a password on the install linkPro and Team (from CI)

Every option runs through the API token, which is why the whole table is Pro and Team. The full command list is on the CI docs and the Bitrise page is /docs/ci/bitrise.

iPhone testers: the device list

An ad hoc .ipa installs only on iPhones whose UDID is in the build's provisioning profile. That is Apple's rule for every tool outside TestFlight. If a tester's device is missing, the install fails. AppForShare's install page has install help that explains the likely cause, and a tester who tapped Remember this device on the UDID finder sees whether their iPhone is in the build before installing. To collect devices without asking testers to sign up anywhere, see registering a tester's iPhone without an account. After you add new devices, build again on Bitrise so the new profile is inside the .ipa; AppForShare never re-signs your build.

Where Bitrise Release Management is the better fit

Bitrise sells its own distribution layer, and it is fair to say what it offers. As listed on November 4, 2026 on the Bitrise pricing page, Release Management Basic is free "with existing Bitrise plan", with 2 GB per artifact, 1,000 artifacts per app and 150 day retention. Its distribution docs (read the same day) describe public install pages with no account, several access links per build with their own expiry or none, and an optional access code. Since July 2026 it also has external tester groups by email, with no Bitrise account needed.

If your team is happy inside Bitrise and that covers what testers need, staying there is reasonable and costs nothing extra. AppForShare is worth the Script step when you want things we did not find in the Bitrise docs we read: an install page in 13 languages with install help when an iPhone install fails, tester feedback with screenshots straight from the install page without an SDK, a store readiness check on every build, and a way for iPhone testers to send their device without any account. The trade offs are laid out in AppForShare vs Bitrise, the pricing side in Bitrise pricing for small teams, and moving testers over in the Bitrise move guide.

Common problems

  • "No build to upload" or an empty path. The Script step runs before the build step, or the build step produced a different file type. Check the order and print $BITRISE_IPA_PATH.
  • The CLI says the token is missing or not valid. The Secret name differs from APPFORSHARE_TOKEN, or the Secret is not exposed to that build (pull requests from forks).
  • The build is bigger than the plan allows. Pro takes files up to 2 GB and Team up to 4 GB.
  • Testers report "Unable to install" on iPhone. Their device is not in this build's profile. Register it, build again, upload again.

Once the Script step works, each Bitrise build gives testers a link without anyone copying files around. If you also run store releases from the same place, the App Store phased release guide and the Google Play staged rollout guide cover the steps after testing.

Keep reading

App Distribution Platform · · 5 min read

AppForShare File Size and Upload Limits

AppForShare limits the size of each build you upload by plan: 50 MB without an account, 150 MB on the Free plan, 2 GB on Pro and 4 GB on Team. Accounts also have a storage total, and every plan...