> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://tester.army/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://tester.army/_mcp/server.

# App Uploads

Before you can automate mobile tests in CI, you need an iOS Simulator build or Android APK/APKS to test against.

#### Build and upload a mobile app with TesterArmy CLI

You are working in a mobile app repository. Build a TesterArmy-compatible mobile artifact and upload
it with the TesterArmy CLI.

Before running commands:

* Inspect the repository structure and identify whether this is an iOS, Android, Expo, or React Native project.
* Do not upload an `.ipa`, `.aab`, or `.xapk`.
* Use an iOS Simulator `.app` build for iOS, or an emulator-runnable `.apk` / `.apks` for Android.
* Do not hard-code API keys or project IDs in committed files.
* Check whether `TESTERARMY_API_KEY` and `TESTERARMY_PROJECT_ID` are already available in the environment.

For iOS:

1. Build a release iOS Simulator app. If this is an Expo / React Native project, run `npx expo prebuild --platform ios` first when native files need to be generated.
2. Prefer uploading the raw `.app` directory with the CLI. The CLI can zip it for you.
3. If a zipped artifact is required, zip the `.app` bundle with the app bundle at the archive root.

For Android:

1. Build a release APK, or use an existing release `.apks` split APK archive.
2. If this is an Expo / React Native project, run `npx expo prebuild --platform android` first when native files need to be generated.
3. Do not upload an Android App Bundle (`.aab`) or `.xapk`.

Upload the artifact:

```bash
mkdir -p .testerarmy

testerarmy upload-app \
  --app-path <path-to-app-artifact> \
  --project "$TESTERARMY_PROJECT_ID" \
  --output .testerarmy/upload.json
```

If this is a temporary CI upload, add `--remove-after 3600`.

After uploading:

* Print the uploaded app ID from `.testerarmy/upload.json`.
* Tell the user which artifact path was uploaded.
* Tell the user whether the artifact was iOS or Android based on the file type.
* Do not run mobile tests unless the user explicitly asks you to.

## What you need to upload

#### iOS

Upload an **iOS Simulator build** as `.app.zip` or `.zip` (archive a `.app` bundle at the zip root). The CLI can also accept a raw `.app` directory and zip it for you.

**Simulator builds only**

Do not upload an `.ipa`. That is a device build. TesterArmy needs a `.app`
bundle built for **iOS Simulator** with the `.app` directory at the archive root.

### Build for iOS Simulator

If you already have an Xcode project, build a simulator app with `xcodebuild`:

```bash
xcodebuild -workspace ios/MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -sdk iphonesimulator \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath ios/build \
  build
```

The simulator app bundle usually ends up here:

```bash
ios/build/Build/Products/Release-iphonesimulator/MyApp.app
```

If you are using Expo / React Native, the [mobile example app](https://github.com/tester-army/mobile-example) is a good reference. Its build flow looks like this:

```bash
npx expo prebuild --platform ios

xcodebuild -workspace ios/testerarmy.xcworkspace \
  -scheme testerarmy \
  -configuration Release \
  -sdk iphonesimulator \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath ios/build \
  build
```

### Zip the `.app` bundle

For dashboard uploads and the presigned API upload flow, archive the `.app` bundle after the build completes:

```bash
cd ios/build/Build/Products/Release-iphonesimulator
zip -r MyApp.app.zip MyApp.app
```

You should end up with a file like `MyApp.app.zip` whose top-level entry is `MyApp.app/`, not `Payload/MyApp.app/`.

#### Android

Upload an **Android build** as `.apk` / `.apks` when Android support is enabled for your workspace.

**Android formats**

Android uploads must be a single `.apk` or a split APK archive (`.apks`).
`.aab` and `.xapk` are not supported.

### Build an Android APK/APKS

For Android tests, upload a release or debug APK. Split APK archives (`.apks`) are also supported when your build/distribution tool produces split APKs:

```bash
./gradlew assembleDebug
```

If you are using Expo / React Native, the [mobile example app](https://github.com/tester-army/mobile-example) is a good reference. Its Android build flow looks like this:

```bash
npx expo prebuild --platform android

cd android
./gradlew assembleDebug
```

The APK usually ends up under `android/app/build/outputs/apk/`.

## Upload via the dashboard

Go to your project and click the **Mobile** tab. Then click **Browse Files** and select your `.app.zip`, `.apk`, or `.apks`.
TesterArmy uploads the artifact to temporary storage first, validates the bundle metadata, and then promotes the validated artifact into your project. If validation fails, the temporary upload is removed and no project app is created.

![Upload app via dashboard](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/testerarmy-subpath.docs.buildwithfern.com/c207c311784e0182343e69592fb0032c0849891f9035a24dde21c4c7a8333b00/docs/assets/docs/uploading-mobile-app.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260817%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260817T151321Z&X-Amz-Expires=604800&X-Amz-Signature=73be8b48c582f778526234ca0b10df4bdb88ebc47c5b494b7f01a58cbe97d804&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

## Upload via the API

For large artifacts, use the three-step presigned flow that streams the bytes straight to storage:

1. `POST /v1/projects/{projectId}/mobile/upload` with `{ filename, fileSize }`. The response includes an `uploadUrl` and `storageKey`.
2. `PUT` the archive bytes to `uploadUrl`.
3. `POST /v1/projects/{projectId}/mobile/upload/confirm` with the same `storageKey`, `filename`, and `fileSize`, plus optional `removeAfter`.

The presigned URL writes to temporary storage. The confirm step validates the artifact and promotes it into project storage; failed validation deletes the temporary object.

See the [API reference](/api-reference/tester-army-api/projects/initiate-a-presigned-mobile-app-upload).

The direct multipart upload (`POST /v1/projects/{projectId}/mobile`) remains supported for existing integrations.

## Create your first mobile test

Before adding CI, upload your build, create at least one mobile test, and run it to make sure it passes. Runs target your most recent uploaded app for the platform automatically; pass an explicit app ID via the API or CLI only when you need to pin a specific build.

When writing prompts, guide the agent like you would guide a human user.

**Weak prompt example:** *"Open settings"*

**Strong prompt example:** *"Tap the profile icon in the top right, open Settings, and verify the notifications toggle is visible"*

Specific prompts produce more reliable tests and make CI failures easier to understand.

## Upload with the CLI

The TesterArmy CLI can upload a raw `.app` directory, an `.app.zip`, an `.apk`, or an `.apks`. Raw `.app` directories are packed for you with the app bundle at the archive root:

```bash
testerarmy upload-app \
  --app-path ios/build/Build/Products/Release-iphonesimulator/MyApp.app \
  --project <projectId>
```

`--app-path` and `--project` are required.

For temporary CI uploads, you can set `removeAfter` in seconds:

```bash
testerarmy upload-app \
  --app-path MyApp.apk \
  --project <projectId> \
  --remove-after 3600 \
  --json
```

Use `--output ./result.json` to write the JSON payload to a specific file, or
`--output ./artifacts/` to write `ta-app-upload-<date>.json` inside a directory.

The command rejects `.ipa` archives.
It also rejects Android `.aab` and `.xapk` bundles.

If you use Expo EAS, see [Expo EAS](/mobile/expo-eas) for the full workflow that
builds the mobile app artifact, downloads it, uploads it, and runs
TesterArmy tests.

## Notes

* Use `removeAfter` to auto-delete temporary uploads created during CI.
* Each project has a 2 GB storage limit.
* **Auto-delete Oldest App** is enabled by default: when a new upload would exceed the storage limit, TesterArmy automatically deletes the oldest uploaded app to make room. Apps used by queued or running tests are never auto-deleted. Disable the toggle in the project's Mobile tab to have over-limit uploads rejected instead.
* Our GitHub Action can upload the `.app` directory directly, so you do not need to zip it when running through GitHub Actions.
* Android uploads require workspace-level Android support to be enabled.