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
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
An install by package name comes from Phonebox's app library, never from Google Play:
{
"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"}'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:
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
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. 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 APK, such as the debug build your coding agent just made, installs in one command:
phonebox apps "$PHONEBOX_PHONE" install app/build/outputs/apk/debug/app-debug.apkThe 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, which takes files of up to 500 MiB straight to Phonebox's storage, then an install that names it:
{
"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_codethan the phone has fails at once with409 version_downgrade. - A build signed with another key fails on the phone, and its entry's
nextsays so. "replace": truefixes 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 puts it together with walking the new screens.
Open, close and link into apps
Apps open through actions:
{
"actions": [
{ "type": "open_app", "package": "org.wikipedia" },
{ "type": "wait_for", "target": { "text": "Search Wikipedia" }, "timeout_ms": 15000 }
],
"observe": "ui"
}open_appopens an installed app, at a givenactivityif you name one.close_appstops an app. With"clear_data": trueit also erases the app's data, which signs it out of every account.open_urlopens a web address or a deep link, in the app you name withpackage, 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
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
| 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.