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 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.
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: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 Running against your own machine instead? Local mode and the stdio
transport are covered in MCP config.
Accept header naming both content types):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:- 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:startanddevices:act. See Authentication. acquire_deviceanswersdevice_unavailablewith reasonleased. 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 sawexpiresAtin their acquire response. Your organization must hold the device. If you supply aleaseId, it must match the current lease; a stale or invented id returnslease_mismatchwithout 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 islease_required.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.
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.