Skip to main content
Five steps: sign in, get a key, paste one block of config, drive a device, read the run back as evidence. You need nothing installed beyond an MCP client.
1

Sign in

The console is at console.phonebase.co. Everything below assumes an account and an organization, and the key in the next step is minted from a signed in session, so this is the step that has to come first.Signing up takes an email address and a password, and confirms the address with a six digit code. The organization is created for you on the way in; you can rename it later in Settings.Keep the tab in front while you submit. A human check runs in the background and can stall in a tab you have switched away from; if nothing happens, see Signing up.
2

Get a key

Every hosted request authenticates with an org-scoped API key: a single bearer string that starts with pbk_.Mint one yourself from the /keys screen in the console. You get a key scoped to your org, carrying the scopes you pick, and optionally narrowed to a subset of your devices. Either role can mint: you do not need to be an org admin. What minting does need is a signed in console session, because an API key cannot mint another key. The scopes you request cannot exceed the scopes your own session holds. It is the /v1/api-keys surface described in Authentication.No device yet either? Buy, drive and return a device is the whole loop, starting from the console.The full token is shown to you exactly once. Only a hash is stored, so nobody (including Phonebase) can read it back later. Put it in an environment variable and treat it like a password:
What the key can do is pinned at issuance: its scopes decide which tools exist for you, and it can optionally be narrowed to a subset of your org’s devices. If a tool seems missing later, that is your key’s scope set, not a fault.
3

Connect

Phonebase speaks MCP over HTTP. Point any MCP client at the hosted control plane with one block of config:
With Claude Code that is one command:
Your client’s tool list is the connection test: if the phonebase tools appear, you are authenticated and in. Which tools appear is decided by your key’s scopes. To check without a client in the way (the endpoint requires an Accept header naming both content types):
Running against your own machine instead? Local mode and the stdio transport are covered in MCP config.
4

Drive

Every session follows the same four beats: acquire a lease, observe, act under that lease, release.
Call list_devices first and read the capabilities on each device before you call anything else. Capabilities differ between devices, and an action the hardware cannot perform is refused rather than silently dropped.If list_devices comes back empty, that is provisioning, not a fault: no devices have been attached to your org yet, or your key was narrowed to a subset that is itself empty. Add one from Devices → Add device in the console; that screen names what is available to buy right now.An empty list is not how an unreachable device shows up. A device your org owns is always listed, and when the control plane cannot currently reach it the entry says so in status instead of going missing. Two statuses mean that, and the difference between them is whether anything answered. offline is a positive report that the device is not up: its gateway is disconnected, or the provider named it and said it was not running. unknown means nothing answered at all: the provider’s fleet listing failed or never mentioned this device, or the row names no reachable transport. Either way the device exists and is yours, and it needs attention on the device side rather than a new device.Treat anything other than online as not drivable. A check written as status === "offline" silently misses the unknown case, which is the one you get when the provider stops answering for a device.leaseId is optional on every action and observation tool: a gesture runs under the lease your organization holds, or takes the device for you. Coordinates are always on a normalized 0 to 1000 grid, which is why the same tap works across screen sizes. Bounds in the ui_snapshot tree are already on that grid, so a value you read there can be tapped directly.Naming the frame does not put your action on a clock. Take as long as you like between the screenshot and the tap, and take more screenshots in between if you want to: neither ages a frame out. What the frame buys you is the answer to the only question that matters here, reported on every action as others: did another hand act on this device since you looked, and was it a person or an agent.When a task needs different hardware, the device you name is the only thing that changes. The tool calls do not.
5

Read the run back

The run comes back as evidence rather than as a claim.Every successful result carries typed structuredContent, shaped by the outputSchema that tool publishes. Every action returns an outcome recording when the device committed it, not merely that the request was accepted.Pass an idempotencyKey with an action and you can look that outcome up later with get_receipt. Reusing the same key on a retry replays what was recorded instead of performing the gesture a second time. A key whose action failed terminally is the exception: it replays the failure and asks you to retry under a new key. See Reading a trace.Unknown or misspelled parameters are refused by the schema, so a typo fails loudly instead of travelling on to a real phone.

When a first run refuses

The three refusals new integrations actually hit, and what each is telling you:
  1. A tool you expected is missing from the tool list. That is your key’s scopes, not a fault: an out-of-scope tool is never registered on your connection, so it genuinely does not exist there. The run tools need runs:start and devices:act. See Authentication.
  2. acquire_device answers device_unavailable with reason leased. Leases are exclusive per device across your whole org: someone else holds it, or your own earlier session never released. Leases expire on their own, so waiting works, and the holder saw expiresAt in their acquire response. Your organization must hold the device. If you supply a leaseId, it must match the current lease; a stale or invented id returns lease_mismatch without moving the phone. Omit the id to use the current lease or, when permitted, take a free device. If the org holds none and the gesture cannot take one, the refusal is lease_required.
  3. stale_frame. The screen changed between your observation and your action, and the action was refused before anything physical happened. Your idempotency key was not consumed: observe again, decide again, act again under the same key.
Everything else (error shapes, every code, retry semantics) is in Troubleshooting and the error reference.

Where to go next

Tool reference

Every tool, its parameters, and the scope it needs.

Device tiers

What the control plane can reach, and how faithful each tier is.

CLI

The same control plane from a terminal, published to npm as phonebase.

Agent tier

Hand a goal to a worker instead of driving each step yourself.