# Apps

Source: https://phonebox.dev/docs/api-reference/apps

> Search the app library, list a phone's installed apps, install apps from the library or your own APK in the background, follow the installs, and uninstall apps.



Every app request on a phone needs a `ready` phone. Apps and their data stay on the phone while it is parked. [Apps](/docs/using-phones/apps) shows how to open, close and link into them with actions.

## List apps [#list-apps]

`GET /v1/phones/{id}/apps` needs the `phones:read` scope.

Lists every installed app, the system apps that came with the phone included.

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

```json title="Response: apps"
{
  "data": [
    { "package": "com.android.chrome", "label": "Chrome", "version_name": "129.0.6668.100", "version_code": 666810033, "system": true },
    { "package": "com.android.settings", "label": "Settings", "version_name": "14", "version_code": 34, "system": true },
    { "package": "org.wikipedia", "label": "Wikipedia", "version_name": "2.7.50502", "version_code": 50502, "system": false }
  ]
}
```

| Field          | Type    | Meaning                                     |
| -------------- | ------- | ------------------------------------------- |
| `package`      | string  | The Android package name.                   |
| `label`        | string  | The name the launcher shows.                |
| `version_name` | string  | The version as the app displays it.         |
| `version_code` | integer | The version as a number.                    |
| `system`       | boolean | `true` for an app that came with the phone. |

The readiness and phone-service errors in [Common errors](/docs/api-reference#common-errors) apply.

## Search the app library [#search-the-app-library]

`GET /v1/apps/library` needs the `phones:read` scope.

Searches Phonebox's app library: the apps a phone installs by package with [Install an app](#install-an-app). It holds store and system apps, the same for every project. It names no phone, and it isn't Google Play: an app that isn't here installs from the Play Store app on the phone.

| Parameter | Type   | Default | Rules                                                                                                           |
| --------- | ------ | ------- | --------------------------------------------------------------------------------------------------------------- |
| `query`   | string | None    | Part of a package or app name, up to 100 characters, such as `chrome`. Without it, the whole library is listed. |
| `cursor`  | string | None    | The `next_cursor` of the previous page, sent back unchanged.                                                    |

```bash
curl "https://phonebox.dev/v1/apps/library?query=chrome" \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
```

Each page holds up to 50 apps; `next_cursor` is `null` on the last one. Phonebox refreshes the library every few hours. While it loads for the first time, a search answers `429 rate_limited`: search again after `Retry-After`.

```json title="Response: library"
{
  "data": [
    { "package": "com.android.chrome", "label": "Chrome", "version_code": 666810033, "version_name": "134.0.6998.135" }
  ],
  "next_cursor": null
}
```

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

`POST /v1/phones/{id}/apps` needs the `phones:control` scope.

Starts installing an app and answers at once, while the install continues in the background. It installs one of two things:

* **An app from Phonebox's app library**, by its package name: the library's own version of it. It doesn't reach Google Play, or any uploaded APK: a Play Store app installs through the Play Store app on the phone, as [Apps](/docs/using-phones/apps#apps-from-the-play-store) describes, and your own APK by `upload`.
* **Your own APK**, by an [upload](/docs/api-reference/uploads) whose status is `ready`. It installs the package the APK declares at the APK's `versionCode`, and its entry in [List app installs](#list-app-installs) succeeds only once the phone lists that package at that version.

| Field     | Type    | Default | Rules                                                                                                                                                                       |
| --------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `package` | string  | None    | An Android package name: at least two parts separated by dots, starting with a letter, up to 255 characters. Give this or `upload`.                                         |
| `upload`  | string  | None    | An upload's ID, `upl_…`, from this project. Give this or `package`.                                                                                                         |
| `replace` | boolean | `false` | With `upload`: uninstall the app first when it is on the phone, which deletes its data and sign-ins. Use it for a build signed with another key, or an older `versionCode`. |

```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"}'
```

It answers `202 Accepted`:

```json title="Response: install"
{
  "package": "com.example.app",
  "status": "running"
}
```

An install by upload answers with its upload too:

```json title="Response: install"
{
  "package": "com.example.app",
  "upload": "upl_q3m7x2k6v4ta",
  "install": "ins_7m2k6q3x4v5a",
  "status": "running"
}
```

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

Follow the install with [List app installs](/docs/api-reference/apps#list-app-installs). Until it has finished, `open_app` fails with `app_not_found`.

| Error                        | When                                                                                                                                                                                                                                                                                                                                                                   |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400 validation_failed`      | The body names neither or both of `package` and `upload`, `package` isn't a valid package name, or `replace` comes without `upload`.                                                                                                                                                                                                                                   |
| `404 upload_not_found`       | No upload with this ID exists in the project.                                                                                                                                                                                                                                                                                                                          |
| `409 upload_not_ready`       | The upload isn't `ready`: `details.status` says what it is, and `next` what to do. With `retryable: true`, the upload is being prepared for this phone: send the install again in a minute.                                                                                                                                                                            |
| `409 version_downgrade`      | The phone has a newer `versionCode` of the upload's app: `details.installed_version_code` and `details.version_code`. Nothing ran. Build with a higher `versionCode`, or send `replace: true`.                                                                                                                                                                         |
| `413 payload_too_large`      | The body is larger than 100 KB.                                                                                                                                                                                                                                                                                                                                        |
| `409 install_in_progress`    | Another install is still running on the phone, which runs at most two installs at once, and one per app. This error is retryable. Installs take a few seconds, so send it again shortly, a few times at most. [List app installs](/docs/api-reference/apps#list-app-installs) shows when the running install has finished, or failed. `details.package` names the app. |
| `422 app_not_available`      | The app isn't in Phonebox's app library. `details.package` names it, and `next` says to use the Play Store app on the phone or upload the APK. Your own uploads install only by `upload`.                                                                                                                                                                              |
| `502 action_outcome_unknown` | The phone service didn't confirm the install, so it may have started. List the installs before you try again. For an install by upload, `details.install` names its row there, which Phonebox follows to its end: don't send the install again until that row has ended.                                                                                               |

With `replace`, Phonebox first checks that the phone can start another install, so an `install_in_progress` refusal then comes before anything is uninstalled. An error that comes after the uninstall has `details.uninstalled: true`: the app and its data are already gone, and `next` says when sending the same request again is safe.

The readiness and phone-service errors in [Common errors](/docs/api-reference#common-errors) apply too.

## List app installs [#list-app-installs]

`GET /v1/phones/{id}/apps/installs` needs the `phones:read` scope.

Lists the phone's recent install attempts.

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

```json title="Response: installs"
{
  "data": [
    { "package": "org.wikipedia", "upload": null, "install": null, "status": "succeeded", "error": null, "next": null, "started_at": "2026-09-29T10:02:10.518Z", "updated_at": "2026-09-29T10:02:51.064Z" },
    { "package": "com.example.app", "upload": "upl_q3m7x2k6v4ta", "install": "ins_7m2k6q3x4v5a", "status": "succeeded", "error": null, "next": null, "started_at": "2026-09-29T10:03:02.000Z", "updated_at": "2026-09-29T10:03:05.000Z" }
  ]
}
```

| Field                      | Type              | Meaning                                                                                                                                                                                               |
| -------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `package`                  | string            | The package being installed.                                                                                                                                                                          |
| `upload`                   | string or null    | The upload an install by upload put on the phone, or `null` for an app from the library.                                                                                                              |
| `install`                  | string or null    | An install by upload's own ID, `ins_…`, which its start answered with. Find your install's row by it. `null` for an app from the library.                                                             |
| `status`                   | string            | `running`, `succeeded` or `failed`. An install by upload succeeds only once the phone lists its package at the upload's `version_code`.                                                               |
| `error`                    | string or null    | For a failed install, its reason as a machine code, such as `install_failed`. Otherwise `null`.                                                                                                       |
| `next`                     | string or null    | What to do about a failed install by upload. When the phone already had the app, it says a build signed with another key may be installed, and to uninstall it or install again with `replace: true`. |
| `started_at`, `updated_at` | timestamp or null | When the install started and last changed, or `null` when that isn't known.                                                                                                                           |

The readiness and phone-service errors in [Common errors](/docs/api-reference#common-errors) apply.

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

`DELETE /v1/phones/{id}/apps/{package}` needs the `phones:control` scope.

Removes an app with all of its data and sign-ins. `{package}` is the app's package name.

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

```json title="Response: uninstall"
{
  "package": "org.wikipedia",
  "uninstalled": true
}
```

| Error                        | When                                                                                      |
| ---------------------------- | ----------------------------------------------------------------------------------------- |
| `400 validation_failed`      | `{package}` isn't a valid package name.                                                   |
| `404 app_not_found`          | The app isn't installed. `details.package` names it.                                      |
| `502 action_outcome_unknown` | The phone service didn't confirm the uninstall. List the apps to see whether it happened. |

The readiness and phone-service errors in [Common errors](/docs/api-reference#common-errors) apply too.
