Skip to main content
Appetize.io

Appetize.io → the Naive mobile primitive

Appetize.io is a trademark of its respective owner, used here for identification and migration comparison only. No endorsement, partnership, or affiliation is implied.
Appetize.io runs real mobile apps in the cloud: upload an APK/IPA (POST /v1/apps), then embed and drive the device in the browser with the JavaScript SDK (client.startSession(), session.tap, session.type, session.screenshot). It’s a great way to demo, test, and automate a mobile build. But an Appetize account is also a standalone device vendor that sits apart from everything else your agent touches:
  • Every device lives behind a single org X-API-KEY, disconnected from wherever the agent’s cards, email, phone number, and KYC live.
  • The token is coarse and ungoverned: any code holding it can upload a build, spin up a device, and drive it — there is no approval step between “the agent has the key” and “the agent is tapping around a live device,” and nothing scopes one tenant’s devices away from another beyond conventions you maintain yourself.
  • “Which agent spun up that device, who approved it, and what else can this agent do?” is answered in the Appetize dashboard for sessions, and in unrelated systems for everything else. The device has no shared accountability with the rest of the agent’s footprint.
Naive’s mobile primitive gives the agent the same capability — a hosted cloud Android/iOS device you can put an app on, stream, and control — but the device is rooted in one governed identity with permissions enforced at execution time:
  • Naive owns the upstream Mobilerun operator key and scopes every device to your tenant — the agent never holds a device-cloud key (the same operator model as Compute with AWS).
  • Provisioning a device, its lifecycle (reboot/reset/terminate), proxy changes, running/stopping tasks, uploading apps, and mutating wildcard calls are approval-gated — an agent call may return { status: "pending_approval" } until a human approves it, so a leaked agent key can’t silently spin up or drive a device.
  • Naive adds a natural-language agent driver: naive.mobile.run({ task, deviceId }) executes “Open Settings and enable dark mode” on-device — where Appetize gives you a raw device plus per-action control that you script yourself.
  • The tenant user whose Account Kit enables mobile is the same user that owns the agent’s cards, its email inboxes, its phone number, and its vault — one identity, one audit trail, revocable in one place.
This guide maps Appetize’s device model to Naive’s mobile primitive, shows the smallest working swap, and is explicit about the Appetize features that don’t map yet.
Tested against: the Appetize REST API v1 (base https://api.appetize.io/v1, X-API-KEY header — POST /v1/apps, GET /v1/apps, GET /v1/usageSummary), the Appetize JavaScript SDK (js.appetize.io/embed.jswindow.appetize.getClient(selector, config), client.startSession, session.tap / session.type / session.screenshot / session.playAction), and the automation package @appetize/playwright v1.6.0 — and the Naive mobile primitive over the Naive API (base https://api.usenaive.ai/v1), the naive.mobile.* SDK sub-client (@usenaive-sdk/server), and the Naive CLI. Docs snapshot July 2026.Version notes:
  • Appetize v1 identifies a build by its publicKey; the v2 API renames it buildId (the two are interchangeable in v1). Naive addresses a device by its Mobilerun device_id and an app by its library entry — see the concept map.
  • Appetize control is client-side, per-action: you load embed.js, get a client, start a session, and script each tap / type / screenshot. Naive’s core driver is a server-side natural-language agent task (naive.mobile.run) plus raw device tools reached through the wildcard search/call pair.
  • On Naive, provision, device lifecycle, run, stop, uploadApp, and mutating calls are approval-gated by default — Appetize has no equivalent gate; the org token does everything.
  • Naive phones are billed per minute from your credits and there is no upstream auto-stop — always terminate a device you’re done with (credit exhaustion and a max-runtime failsafe auto-terminate as a backstop).

Concept map

Before / after: the core path

The path that matters for almost every device-backed agent is get a cloud device with your app on it, stream it, then drive it. Here it is on both platforms.
The shape is largely the same — device → app → stream → drive → tear down — but the differences are the point of the migration:
  • The agent never holds a device-cloud key. Appetize drives the device with the same org token it uploads with. On Naive, Naive holds the Mobilerun operator key and scopes every device to your tenant; the agent only ever holds its nv_sk_ key.
  • Provisioning and driving are approval-gated. provision, lifecycle, run, uploadApp, and mutating wildcard calls can require a human approval before they execute — a leaked agent key can’t silently spin up or drive a device.
  • Driving is natural-language, not per-action scripting. Appetize expects you to script each tap / type / screenshot. Naive’s run executes a plain-English task on-device (the raw device tools are still there via the wildcard call).
  • The device is your identity, not a separate account. The same tenant user that owns this device owns the agent’s cards, email, phone, and vault.

Approvals: the execution-time gate

This is what “governed identity” means in practice. On Appetize, the org token spins up and drives devices with no gate. On Naive, the sensitive operations are approval-gated, so an agent’s request to provision or drive a device can require a human to approve it first.
  • Sensitive operations are gated server-side. Provision, reboot/reset/terminate, proxy changes, run/stop, uploadApp, and mutating wildcard calls can require human approval depending on the user’s Account Kit — you don’t police it in app code.
  • A leaked agent key can’t spin up or drive a device. It can read status and list, but the device-provisioning and device-driving actions land in the Approvals queue.
  • The meter is yours to stop. Phones are billed per device-minute from credits with no upstream auto-stop; terminate stops the meter (credit exhaustion and a max-runtime failsafe are the only backstops).

Minimal viable migration

The smallest swap that keeps a working device-backed agent alive is provision a device → put the app on it → drive it → terminate.
1

Install the SDK / set your key

Set NAIVE_API_KEY (a server-side nv_sk_ key from the dashboard). You can drop APPETIZE_API_KEY for this path — Naive holds the upstream device-cloud key for you.
2

Enable the mobile primitive for the user

Whether an agent may use mobile at all is policy on its identity. Enable mobile in the user’s Account Kit, and confirm your approval posture (which sensitive operations require a human) — this is the deliberate change from Appetize, where the org token needs no permission to act.
3

Swap the provision + upload calls

Replace getClient(...) + client.startSession(...) with naive.mobile.provision({ country }) then naive.mobile.waitReady(device_id). Replace POST /v1/apps with naive.mobile.uploadApp({ name, fileName }). Keep the returned device_id in place of Appetize’s publicKey — you now address a device, not a build.
4

Swap the control path

Map your scripted session.tap / type / screenshot sequence to a single naive.mobile.run({ task, deviceId }) natural-language task (then read task, screenshots, trajectory). If you need a specific raw device tool, reach it with the wildcard naive.mobile.call("take-screenshot", { path: { deviceId } }). To display the device, read naive.mobile.stream(device_id) (stream_url, stream_token, console_url) instead of the Appetize iframe embed.
5

Always terminate

Replace session.end() with naive.mobile.terminate(device_id). This stops the per-minute meter — there is no upstream auto-stop, so terminate every device you finish with.
6

Ship it

At this point you are off Appetize for the core provision → app → drive → terminate path. Everything below is upside, not a requirement.

Consolidate further once you’re on Naive

This is where the migration pays for itself. On Appetize, the org token scopes devices in one Appetize account and nothing else. On Naive, the unit of isolation is a tenant user, and the device is one of many primitives that identity owns.

Gain #1 — one identity across primitives

  • With Appetize, an agent’s devices are a slice of one account, and per-customer separation is a naming convention you maintain yourself. With Naive, naive.forUser(acme.id) is a single handle to the identity whose Account Kit governs mobile and cards and email and phone and vault.
  • Each tenant only ever sees the devices it created through Naive, and each tenant’s credits fund only its own phones — isolation is the platform’s job, not yours.

Gain #2 — execution-time governance

  • Whether an agent may use mobile at all is policy on the identity — toggled per user in the Account Kit — and device-minutes are metered against the tenant’s plan quota. This is on top of the per-operation approval gate that Appetize has no equivalent for.
  • The agent’s device code path stays the same for every tier. What changes at execution time:
    • Disabled primitive → the call is refused. If the Account Kit doesn’t grant mobile, the provision never runs.
    • Sensitive operation → approval-gated. Provision, lifecycle, run, uploadApp, and mutating calls can return pending_approval until a human approves them.
    • Quota exceeded → capped. Once device-minutes cross the plan’s mobile quota for the period, further provisioning is rejected — no surprise bill from a device left running.
    • No key in the agent → nothing to leak. Naive holds the Mobilerun operator key; the agent only ever holds its scoped nv_sk_ key.

Gain #3 — unified accountability

  • Every provision, task, upload, and terminate for a customer lands in one per-user activity log — alongside their cards, email, phone, and vault events, not in a separate vendor dashboard:
  • That is the question that is hard to answer when devices live in Appetize, cards live in Stripe, the phone number lives in a carrier console, and email lives somewhere else. Under Naive it is a single query.

What does not map yet

A migration guide that hides gaps is worse than none. The core path (provision a device → put an app on it → stream it → drive it → terminate) maps cleanly, and Naive adds the approval gate, per-tenant scoping, and a natural-language agent driver. But Appetize is a mature app-testing and in-browser device platform, and the following capabilities have no direct equivalent on Naive’s mobile primitive today. Check this list against your app before you commit.
Naive’s mobile primitive is a governed cloud-device surface (provision a real Android/iOS device, upload an app, stream it, run natural-language agent tasks, reach the entire Mobilerun API via a wildcard call) — not the full Appetize app-testing platform. If your workflow depends on the @appetize/playwright assertion framework, fine-grained typed session scripting, a free device/OS selection matrix, or in-page iframe embedding, those are the gaps most likely to need Appetize (or Mobilerun directly) alongside Naive. The core provision → app → drive → terminate path maps directly — and the flip side is the gain: the device, its approval gate, its per-tenant scope, and its billing are governed by the same identity and Account Kit as the rest of the agent, with a unified audit trail — not a slice of a standalone account behind an all-or-nothing token.

Where to go next

  • mobile primitive — provision, stream, run agent tasks, apps, wildcard search/call, billing
  • Mobile API reference — every REST endpoint, including the approval-gated ones
  • naive.mobile.* SDK sub-client — the typed client used above
  • Approvals — the human gate that provision / run / upload flow through
  • Compute — the same operator-key model applied to sandboxed code execution
  • Account Kits — the policy model that enables/disables mobile per user
  • Customer billing — the plan + quota that meters device-minutes at execution time
  • Tenant users — the identity that owns the device, cards, email, phone, and vault