# Apps

Upload a build of your app once and Mobile Next Cloud keeps it for your organization. Start a real device with it, install it on a device you are already using, or point CI and agents at it by its file id. The same bytes uploaded twice are stored once.

## Upload a build[​](#upload-a-build "Direct link to Upload a build")

In the web app, open **Apps** and choose **Upload app**. Pick an `.apk` or `.ipa`.

* The file is checked in your browser first. If your organization already has these exact bytes, nothing is uploaded and you see **Already uploaded**.
* Once uploaded, Mobile Next reads the app's name, package or bundle id, version, build number and icon. Builds of the same app are grouped under it.
* iOS builds are re-signed for the device when they are installed, so an unsigned or ad hoc signed device `.ipa` works. Simulator builds are stored but do not install on real devices. For Android, upload an APK, not an App Bundle (`.aab`).

**Apps** lists one row per app with its latest version. Open an app to see all its builds, or **All files** for every upload. From a file's menu you can rename it, change when it expires, download it, or delete it.

## Start a device with an app[​](#start-a-device-with-an-app "Direct link to Start a device with an app")

Choose the play button on an app or a file row. **Start Session** opens with that app selected and lists only devices of its platform. Pick a device and start the session; you can also choose an app in the **Install an app** section when starting any session.

* **Android:** the app is installed and launched before the device is handed to you.
* **iOS:** a new device is always started, and the app is installed while it boots, so it takes a few minutes.

## Install on a running device[​](#install-on-a-running-device "Direct link to Install on a running device")

On a running Android device, choose **Install App** in the device toolbar. Pick an app and a version (newest first, with the filename and upload date), or **Upload app** to add a new build first. The app is installed and launched.

A running iPhone cannot take a new app. To use an iOS build, start a new session with it from **Apps**.

## Limits and expiry[​](#limits-and-expiry "Direct link to Limits and expiry")

| Plan       | Largest file | Storage |
| ---------- | ------------ | ------- |
| Hobby      | 250 MB       | 1 GB    |
| Starter    | 1 GB         | 2 GB    |
| Team       | 2 GB         | 10 GB   |
| Enterprise | 2 GB         | —       |

A build expires 30 days after upload unless you choose otherwise (10 minutes to 90 days). Uploading the same file again extends it. Deleting a build removes it from your organization at once.

## Use it from CI[​](#use-it-from-ci "Direct link to Use it from CI")

Upload the build from your pipeline and start a device with it, using an [API key](https://mobilenext.ai/docs/docs/api/api-keys.md). The flow is the same as the web app: hash, create, upload only if asked, then complete.

```
set -euo pipefail

API=https://app.mobilenext.ai/api/v1

AUTH="Authorization: Bearer $MOBILENEXT_API_KEY"

APP=app/build/outputs/apk/release/app-release.apk



# 1. Create the file. A dedup hit returns the existing file with no `upload`.

jq -n --arg name "$(basename "$APP")" --argjson size "$(stat -c%s "$APP")" \

      --arg sha "$(sha256sum "$APP" | cut -d' ' -f1)" \

      --arg branch "$GITHUB_REF_NAME" --arg commit "$GITHUB_SHA" \

      --arg run "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \

      '{filename: $name, filesize: $size, sha256: $sha,

        build: {branch: $branch, commit: $commit, ciRunUrl: $run}}' > create.json

curl -sf "$API/files" -H "$AUTH" -H 'Content-Type: application/json' -d @create.json > file.json

FILE_ID=$(jq -r .id file.json)



# 2. Upload the bytes only when asked, with the headers it returns.

if [ "$(jq -r '.upload != null' file.json)" = true ]; then

  HEADERS=()

  while IFS= read -r header; do HEADERS+=(-H "$header"); done < <(

    jq -r '.upload.headers | to_entries[] | select(.key | ascii_downcase != "content-length") | "\(.key): \(.value)"' file.json)

  curl -sf -X PUT "${HEADERS[@]}" --upload-file "$APP" "$(jq -r .upload.url file.json)"

fi



# 3. Complete it; the file comes back `ready` or `failed` with a `failureReason`.

if [ "$(jq -r .status file.json)" != ready ]; then

  curl -sf -X POST "$API/files/$FILE_ID/complete" -H "$AUTH" > file.json

fi

[ "$(jq -r .status file.json)" = ready ] || { jq . file.json; exit 1; }



# 4. Start a device with the build and launch it.

SESSION_ID=$(curl -sf -X POST "$API/sessions" -H "$AUTH" | jq -r .id)

curl -sf "$API/sessions/$SESSION_ID/devices" -H "$AUTH" -H 'Content-Type: application/json' \

  -d "{\"filters\": [{\"attribute\": \"platform\", \"operator\": \"EQUALS\", \"value\": \"android\"}],

       \"file\": \"$FILE_ID\", \"launchAfterInstall\": true}"
```

To install on a device that is already running instead, call `POST /api/v1/sessions/{sessionId}/devices/{deviceId}/install` with `{"file": "<file id>", "launchAfterInstall": true}` (Android only). See [Files](https://mobilenext.ai/docs/docs/api/files.md), [Apps](https://mobilenext.ai/docs/docs/api/apps.md) and [Devices](https://mobilenext.ai/docs/docs/api/devices.md) in the API reference.
