Test your Android build with a coding agent
Install the APK your coding agent just built on a Phonebox phone in one command, check its version, then have the agent walk the new screens and report what it saw.
A coding agent such as Claude Code, Codex or Cursor can change your Android app, put the new build on a Phonebox phone, click through the screens it changed, and tell you what it saw. This guide sets that up with the CLI. It assumes your agent has the agent skill and PHONEBOX_API_KEY in its environment.
Once there is a phone, getting a new build onto it is one command after the build:
./gradlew assembleDebug
phonebox apps "$PHONEBOX_PHONE" install app/build/outputs/apk/debug/app-debug.apkCreate a phone for testing
Create one phone for this work, and keep reusing it: a parked phone keeps its apps and signed-in accounts, and costs nothing while it waits for the next build.
echo "android-qa-$(date +%s)" > android-qa.key # once for this phone; a repeat of the create reads it again
phonebox create --name android-qa --idle 15m --max 2h --idempotency-key "$(cat android-qa.key)" --no-wait | tee android-qa.json
export PHONEBOX_PHONE=$(grep -o 'ph_[a-z2-7]\{12\}' android-qa.json | head -1) # the new phone's "id"
phonebox status --wait # waits until the phone is ready, only reading it; safe to run again--no-waitprints the new phone at once, andphonebox status --waitthen waits until it is ready. The phone bills from the moment it exists.- The key and the phone are kept in files, because an agent's shell may not keep variables from one command to the next. In a new shell, run the
exportline again first. - If
createfails, run it again with the same key and the same flags: you get the phone the first attempt created, if it created one, and never a second one. If it exits without printing a phone, runphonebox lsand use the newest phone namedandroid-qabefore you create another. - A phone is gone only when its
statusisfailedordeleted, and thencreateexits with that phone's error: create the next one with a new key. An error that names the phone but is retryable, such as a start whose phone parked again, leaves the same phone to start again. --idle 15mgives the agent time between steps, and--max 2hcaps each session. When a build takes longer than the idle timeout, park the phone while it runs andphonebox startit afterwards.
Install your build in one command
Build the APK, then install it on the phone:
./gradlew assembleDebug
phonebox apps "$PHONEBOX_PHONE" install app/build/outputs/apk/debug/app-debug.apkThe command uploads the APK, waits while Phonebox reads its manifest, installs it and waits until the phone lists the app at the APK's versionCode. Each step is a line on stderr, and the last line on stdout is the install:
{"uploading":"app/build/outputs/apk/debug/app-debug.apk","bytes":8388608}
{"processing":"upl_q3m7x2k6v4ta","package":null,"version_code":null}
{"ready":"upl_q3m7x2k6v4ta","package":"com.example.app","version_code":42}
{"installing":"com.example.app","upload":"upl_q3m7x2k6v4ta","version_code":42}
{"package":"com.example.app","upload":"upl_q3m7x2k6v4ta","install":"ins_7m2k6q3x4v5a","status":"succeeded","error":null,"next":null,"started_at":"2026-09-29T10:00:08.000Z","updated_at":"2026-09-29T10:00:11.000Z"}- Any signed APK installs, debug builds included, up to 524,288,000 bytes (500 MiB). An Android App Bundle (
.aab) or a split APK can't be installed on its own: build an APK. An unsigned release build is refused: sign it first. - A build signed with another key, or an older
versionCode, needs--replace. Android keeps one signing key per app and refuses to downgrade it, so a debug build from another machine, or one whoseversionCodewent back, fails.--replaceuninstalls the app first, which deletes its data and sign-ins, then installs the new build. Without it, an older build is refused before anything runs (409 version_downgrade), and an install that fails over another signing key says so in itsnextstep. - One upload installs on many phones. The command prints the upload's ID;
POST /v1/phones/{id}/appswith{"upload": "upl_…"}installs it on another phone without uploading again.phonebox uploadslists your uploads, which are deleted 7 days after their last install. - Rebuilding is cheap. Installing the same file again reuses its upload (through the REST API, completing answers with that ready upload; the new one is a
duplicatethat names it), and a newer build of the package retires the older uploads within a day. A project's uploads hold at most 500 MiB, and a project creates at most 50 uploads a day. Uploads has every limit and status.
The SDK does the same with await phone.installApk("app/build/outputs/apk/debug/app-debug.apk"). Through the REST API, create an upload, send the file to its upload_url with curl, complete it, and install it: Uploads shows each request. Over MCP, the create_app_upload, complete_app_upload and install_app tools do the same, as MCP shows.
Check the version before you test
The install already waits until the phone lists your package at the build's versionCode. When the build arrives another way, such as from Google Play, the agent checks it before it walks a single screen. phonebox apps lists the installed apps with their version_code:
phonebox appsThe agent finds your package in the list and compares its version_code with the build you made. When the phone still has an older one, the new build hasn't arrived yet, and the agent never tests the old build in its place.
Walk the new screens and report
With the right build installed, the agent opens the app and works through the screens you changed, looking after every action and saving screenshots as evidence:
phonebox open com.example.app
phonebox wait --text "Welcome" --timeout 20s
phonebox look
phonebox tap --text "Settings"
phonebox look
phonebox screenshot settings.jpg
phonebox tap --text "Notifications"
phonebox look
phonebox screenshot notifications.jpg
phonebox parkA prompt like this gives your agent the whole task:
Build a debug APK of the app with the new notification settings screen, and install it on my Phonebox
phone ($PHONEBOX_PHONE) with `phonebox apps "$PHONEBOX_PHONE" install <the APK>`. If the install says
another signing key or an older version is on the phone, run it again with --replace. Open Settings >
Notifications, try each toggle, and look after every tap. Save a screenshot of each screen you visit.
Then park the phone and report what you saw, with the screenshots, and anything that looked wrong.To test the build your testers get from Google Play instead, name that route in the prompt.
The agent reports from what phonebox look and the screenshots showed, and the version check makes sure that it tested your new build. Ask it to park the phone when it's done: an idle phone parks itself about its idle_timeout after the last request, but the minutes until then are billed.
Another route: Google Play
To test a build the way your testers get it, the phone installs it from the Play Store, signed in as a tester. It takes more setup than the one-command install, and phonebox apps install PACKAGE doesn't reach Google Play: the agent drives the Play Store app on the phone.
Set up once
- Choose a tester account. Use a Google account that you keep for testing. Add it to the testers of your internal or closed testing track, and accept the testing invitation with it. For debug builds, also let it download from Internal app sharing, in that page's settings in Play Console.
- Sign the phone into that account. Google's sign-in asks for a password and often a second step, so hand this part to a person. Open the Play Store with
phonebox open com.android.vending, then create a live view link withphonebox live --expires 15mand open it yourself to sign in. Sign in to apps and hand off to a human walks through a handoff. The phone stays signed in while it is parked, so you do this only once. - For debug builds, turn on Internal app sharing in the phone's Play Store, in the same live view. Google's Play Console help describes where it is: in the Play Store's settings, tapping the Play Store version seven times shows developer options, which hold the switch.
Publish each build
- A release build goes to a testing track. Upload it to your internal or closed testing track in Play Console, or with the publishing tool your project already uses. A testing track takes only release builds: Play Console refuses a debuggable build on every track. A new build can take a while to reach testers after you publish it.
- A debug build goes through Internal app sharing. Upload the APK or app bundle on Play Console's Internal app sharing page, which takes debug builds. It gives you a link to that build.
Install it from the Play Store
The agent installs or updates the app as a tester would, driving the Play Store's screens with look and tap. For a testing track, it opens the app's page in the Play Store:
phonebox open "market://details?id=com.example.app"
phonebox look
phonebox tap --text "Update"
phonebox wait --text "Open" --timeout 30sFor Internal app sharing, it opens the build's link instead, with phonebox open and the link, and then looks and taps in the same way. The first install of the app shows Install instead of Update. A download can take longer than one wait, so when wait times out, the agent looks, and waits again while the Play Store still shows the download moving. When the download stops moving, or an error appears, the agent parks the phone and reports to you.
Clean up between builds
- The phone keeps the installed app while it is parked, and
phonebox startresumes it for the next build. phonebox apps rm com.example.appuninstalls the app, with its data, when you need a clean install.phonebox uploads rm upl_…deletes an upload you no longer need; otherwise it is deleted 7 days after its last install.