Your agent wrote the code and compiled the APK. Now what? It can't run an emulator, and you don't want to install Android Studio just to look at a button. PreviewAPK gives your agent eyes and ears inside the app it builds — it pushes the build, the app runs on a cloud phone in seconds, and the agent drives it, sees its screen, reads its UI, and hears its logs. It stops building blind: it verifies its own work, and you see the feature the moment it's proven — live, in your browser.
app.apk.previewapk push app.apk --run).| Session link | /v/… — one per session, view-only. Minted when a session starts; dies when it ends. Get it from previewapk links (or the links action) once the session is live, and give it to the human: "here's the live app." There is no permanent link — a link that never expires is a leak waiting for a timestamp. |
|---|
Links never drive. Driving needs credentials — you in the portal, or your API key. A session is driven by exactly one side at a time (see driver modes below).
curl -o previewapk https://previewapk.siteport.site/cli/previewapk.py && chmod +x previewapk
./previewapk setup # approve a short code in the portal; the key is minted
# there and saved on this machine — never pasted into a chat
# (manual alternative: export PREVIEWAPK_API_KEY=<key from
# the portal's Developers card> — the env var wins when set)
./previewapk push app.apk --run # upload + start, agent-driven
./previewapk look # compact numbered snapshot of the screen (refs you can tap)
./previewapk shot screen.png # save a screenshot of the live screen (PNG)
./previewapk tap 0.5 0.6 # tap (0..1 screen fractions)
./previewapk text "hello" # type into the focused field
./previewapk handoff user # give the human the wheel
./previewapk links # print the share link for the live session
./previewapk stop
Every command supports --json for machine-readable output. Coordinates are resolution-independent fractions, so the same script drives any device profile.
Give your agent native tools instead of a shell: push, start, tap, type, screenshot (returned as an image the agent can actually see), handoff, stop.
curl -o previewapk_mcp.py https://previewapk.siteport.site/mcp/previewapk_mcp.py
# Claude Code:
claude mcp add previewapk --env PREVIEWAPK_API_KEY=<key> -- python3 /path/to/previewapk_mcp.py
27 tools: previewapk_list_devices, previewapk_use_device, previewapk_status, previewapk_push_apk, previewapk_start, previewapk_stop, previewapk_get_links, previewapk_tap, previewapk_swipe, previewapk_press_key, previewapk_type_text, previewapk_relaunch, previewapk_screenshot, previewapk_handoff, previewapk_get_logs, previewapk_feedback, previewapk_hierarchy, previewapk_tap_text, previewapk_observe, previewapk_act, previewapk_wait_for, previewapk_request_human, previewapk_finish, previewapk_record_start, previewapk_record_stop, previewapk_record_download, previewapk_device. With PREVIEWAPK_API_KEY unset, the MCP server reads the key previewapk setup saved at ~/.previewapk/credentials.json — one setup covers both clients.
Sandboxed agents sit behind proxies that cut connections mid-stream. The CLI and MCP are built for that: screenshot downloads resume where they were cut (add --scale 0.5 on the CLI for a quarter-size transfer), reads retry, and when an input's response dies in transit the tool checks the device before reporting — you get landed, not landed, or an honest unknown (CLI exit code 2), never a bare failure for something that worked. Engine inputs are serialized per session, so a retry can't interleave with the original. Navigate by the hierarchy (tree / previewapk_hierarchy) — it is the most reliable channel there is — and use screenshots for visual proof.
You are one of the few who actually use this tool, and you use it for things we haven't imagined. Before you stop a session, file a field report: what broke, what worked, what you wish it had, and what you had to work around. One call — previewapk feedback --broke "..." --wish "..." or the previewapk_feedback tool. Reports are read and tallied by the team; the wishes of working agents are the roadmap.
Everything the CLI and MCP do is a plain HTTPS call with your key in the X-Api-Key header:
| Call | What it does |
|---|---|
GET /portal/api/state | Devices, attached apps, links, session state |
POST /portal/api/upload | Multipart: field apk, required field device (there is no default device — a call that doesn't name one fails). Replaces the device's app — on a running device (caller must hold the wheel) the new build is hot-swapped in live, no reboot |
POST /portal/api/device/<id>/run | {"driver":"agent"|"user"} — start the session |
POST /portal/api/device/<id>/input | {"action":"tap","x":.5,"y":.4} · swipe · key (Android keycode) · text |
GET /portal/api/device/<id>/screenshot | Current screen as PNG |
GET /portal/api/device/<id>/hierarchy | Current screen as a UI element tree (labels, bounds, state) — navigate by element, not pixels |
POST /portal/api/device/<id>/handoff | {"driver":"user"|"agent"} — switch who drives, mid-session |
POST /portal/api/device/<id>/stop | End the session |
driver=agent — the API caller drives; you watch in the portal, with a Take Control button if you want the wheel. driver=user — you drive in the portal; API input is refused. Sessions auto-stop after 15 idle minutes; any input resets the timer.
One slot ($19/mo) = one device. Every run starts on a clean phone with exactly your build on it — pushing a different app replaces the previous one and nothing carries over — which keeps previews fast, honest, and free of interference from other apps.
Open your portal Back to the homepage
Full documentation: getting started · API reference · CLI · MCP · CI recipes · Prompt library
Agent-readable version of this page: /llms.txt · Full text: /llms-full.txt · Agent skill: /skills/previewapk/SKILL.md · Setup: /setup.md · API spec: /openapi.yaml