# Apps

Source: https://phonebox.dev/docs/using-phones/apps

> List, install, open and uninstall apps on a phone, your own APKs included.



Every app request needs the phone to be `ready`. Installed apps and their data, signed-in accounts included, stay on the phone when it parks, so an app you set up once is there the next time you start the phone.

## List installed apps [#list-installed-apps]

```bash
curl https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
```

The response's `data` lists every installed app, the system apps that came with the phone included, and each entry has these fields:

| Field          | Meaning                                                 |
| -------------- | ------------------------------------------------------- |
| `package`      | The Android package name, such as `com.android.chrome`. |
| `label`        | The name the launcher shows.                            |
| `version_name` | The version as the app displays it.                     |
| `version_code` | The version as a number.                                |
| `system`       | `true` for an app that came with the phone.             |

## Install an app [#install-an-app]

An install by package name comes from Phonebox's app library, never from Google Play:

```json title="POST /v1/phones/{id}/apps"
{
  "package": "com.example.app"
}
```

```bash
curl https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps \
  -H "Authorization: Bearer $PHONEBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"package": "com.example.app"}'
```

Phonebox starts the install and answers `202 Accepted` right away, with the package and `"status": "running"`, while the install continues in the background. An app that isn't in the library fails with `422 app_not_available`, whose `next` says to install it from the Play Store app on the phone instead.

A phone runs at most two installs at once, and one per app. One more, while two run or while the same app is still installing, fails with `409 install_in_progress`. That error is retryable. An install usually takes a few seconds, so wait a few seconds, check the installs list below, or `phonebox apps installs` in the CLI, and send the install again once the running install has finished. After a few refused tries, stop and tell your user.

To follow an install, list the recent installs:

```bash
curl https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps/installs \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
```

Each entry in `data` has the `package`, the `upload` and `install` IDs for an install by upload (otherwise `null`), a `status` of `running`, `succeeded` or `failed`, an `error` code when it failed (otherwise `null`) with a `next` step when Phonebox can tell, and `started_at` and `updated_at`. You can also just try to open the app: `open_app` fails with `app_not_found` until the install has finished.

### Apps from the Play Store [#apps-from-the-play-store]

The app library is a small set of ready-to-install apps, not a complete app store. In the console, **Install → Open Play Store** opens Google Play on the phone; **Upload an APK** installs your own APK.

To install an app from Google Play, use the Play Store app on the phone, as a person would. The phone needs a Google account for that. Google's sign-in asks for a password and often a second step, so hand it to a person once with a [live view link](/docs/using-phones/live-view). The phone stays signed in while it is parked. Then open the app's page with an `open_url` action for `market://details?id=` and the package, observe, and tap Install.

### Your own builds [#your-own-builds]

Your own APK, such as the debug build your coding agent just made, installs in one command:

```bash
phonebox apps "$PHONEBOX_PHONE" install app/build/outputs/apk/debug/app-debug.apk
```

The CLI uploads the file, waits while Phonebox reads its manifest, installs it and waits until the phone lists the app at the APK's `versionCode`. The SDK does the same with `phone.installApk(path)`. Through the REST API it is two steps: an [upload](/docs/api-reference/uploads), which takes files of up to 500 MiB straight to Phonebox's storage, then an install that names it:

```json title="POST /v1/phones/{id}/apps"
{
  "upload": "upl_q3m7x2k6v4ta",
  "replace": false
}
```

It answers `202 Accepted` with the APK's package, the upload, the install's own ID (`ins_…`) and `"status": "running"`. The install's entry in the installs list has the same ID, and it succeeds only once the phone lists the package at the upload's `version_code`. Android keeps one signing key per app and doesn't downgrade one, so:

* An older `version_code` than the phone has fails at once with `409 version_downgrade`.
* A build signed with another key fails on the phone, and its entry's `next` says so.
* `"replace": true` fixes both: it uninstalls the app first, which deletes its data and sign-ins, then installs the upload.

[Test your Android build with a coding agent](/docs/guides/test-android-builds) puts it together with walking the new screens.

## Open, close and link into apps [#open-close-and-link-into-apps]

Apps open through [actions](/docs/using-phones/actions):

```json title="POST /v1/phones/{id}/actions"
{
  "actions": [
    { "type": "open_app", "package": "org.wikipedia" },
    { "type": "wait_for", "target": { "text": "Search Wikipedia" }, "timeout_ms": 15000 }
  ],
  "observe": "ui"
}
```

* `open_app` opens an installed app, at a given `activity` if you name one.
* `close_app` stops an app. With `"clear_data": true` it also erases the app's data, which signs it out of every account.
* `open_url` opens a web address or a deep link, in the app you name with `package`, or in whichever app handles it.

`open_app` and `close_app` fail with `app_not_found` when the app isn't installed. In the first seconds after a phone becomes ready, it can still be finishing its own start-up, and it may send an app you just opened to the background. If the app isn't in front when you observe, open it again.

## Uninstall an app [#uninstall-an-app]

```bash
curl -X DELETE https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps/org.wikipedia \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
```

The response is `{"package": "org.wikipedia", "uninstalled": true}`. Removing an app that isn't installed fails with `404 app_not_found`. Uninstalling deletes the app's data with it.

## From the CLI and the SDK [#from-the-cli-and-the-sdk]

| CLI                                                 | SDK                                                |
| --------------------------------------------------- | -------------------------------------------------- |
| `phonebox apps`                                     | `phone.apps.list()`                                |
| `phonebox apps install com.example.app`             | `phone.apps.install("com.example.app")`            |
| `phonebox apps install ./app-debug.apk [--replace]` | `phone.installApk("./app-debug.apk", { replace })` |
| `phonebox uploads`                                  | `pb.uploads.list()`                                |
| `phonebox apps rm org.wikipedia`                    | `phone.apps.uninstall("org.wikipedia")`            |
| `phonebox apps installs`                            | `phone.apps.installs()`                            |
| `phonebox open org.wikipedia`                       | `phone.open("org.wikipedia")`                      |

`phonebox open` and `phone.open()` open a URL or deep link instead when you give them one.
