Phonebox / 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.

View as Markdown

Every app request on a phone needs a ready phone. Apps and their data stay on the phone while it is parked. Apps shows how to open, close and link into them with actions.

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.

curl https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
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 }
  ]
}
FieldTypeMeaning
packagestringThe Android package name.
labelstringThe name the launcher shows.
version_namestringThe version as the app displays it.
version_codeintegerThe version as a number.
systembooleantrue for an app that came with the phone.

The readiness and phone-service errors in Common errors apply.

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. 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.

ParameterTypeDefaultRules
querystringNonePart of a package or app name, up to 100 characters, such as chrome. Without it, the whole library is listed.
cursorstringNoneThe next_cursor of the previous page, sent back unchanged.
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.

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

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 describes, and your own APK by upload.
  • Your own APK, by an upload whose status is ready. It installs the package the APK declares at the APK's versionCode, and its entry in List app installs succeeds only once the phone lists that package at that version.
FieldTypeDefaultRules
packagestringNoneAn Android package name: at least two parts separated by dots, starting with a letter, up to 255 characters. Give this or upload.
uploadstringNoneAn upload's ID, upl_…, from this project. Give this or package.
replacebooleanfalseWith 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.
POST /v1/phones/{id}/apps
{
  "package": "com.example.app"
}
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:

Response: install
{
  "package": "com.example.app",
  "status": "running"
}

An install by upload answers with its upload too:

Response: install
{
  "package": "com.example.app",
  "upload": "upl_q3m7x2k6v4ta",
  "install": "ins_7m2k6q3x4v5a",
  "status": "running"
}
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. Until it has finished, open_app fails with app_not_found.

ErrorWhen
400 validation_failedThe body names neither or both of package and upload, package isn't a valid package name, or replace comes without upload.
404 upload_not_foundNo upload with this ID exists in the project.
409 upload_not_readyThe 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_downgradeThe 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_largeThe body is larger than 100 KB.
409 install_in_progressAnother 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 shows when the running install has finished, or failed. details.package names the app.
422 app_not_availableThe 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_unknownThe 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 apply too.

List app installs

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

Lists the phone's recent install attempts.

curl https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps/installs \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
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" }
  ]
}
FieldTypeMeaning
packagestringThe package being installed.
uploadstring or nullThe upload an install by upload put on the phone, or null for an app from the library.
installstring or nullAn 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.
statusstringrunning, succeeded or failed. An install by upload succeeds only once the phone lists its package at the upload's version_code.
errorstring or nullFor a failed install, its reason as a machine code, such as install_failed. Otherwise null.
nextstring or nullWhat 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_attimestamp or nullWhen the install started and last changed, or null when that isn't known.

The readiness and phone-service errors in Common errors apply.

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.

curl -X DELETE https://phonebox.dev/v1/phones/ph_7kx2m6q4v3ta/apps/org.wikipedia \
  -H "Authorization: Bearer $PHONEBOX_API_KEY"
Response: uninstall
{
  "package": "org.wikipedia",
  "uninstalled": true
}
ErrorWhen
400 validation_failed{package} isn't a valid package name.
404 app_not_foundThe app isn't installed. details.package names it.
502 action_outcome_unknownThe phone service didn't confirm the uninstall. List the apps to see whether it happened.

The readiness and phone-service errors in Common errors apply too.

On this page