# Double tap
Source: https://docs.tapkit.ai/api-reference/double-tap
POST /phones/{phone_id}/double-tap
Double tap a point on the screen
Tap twice quickly at a point. Double tapping zooms in many apps and selects a word in a text field.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Horizontal position in pixels from the left edge.
Vertical position in pixels from the top edge.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/double-tap \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"x": 585, "y": 1266}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Download screenshot
Source: https://docs.tapkit.ai/api-reference/download-screenshot
GET /jobs/{job_id}/download
Fetch the image from an async screenshot
Returns the PNG captured by a [screenshot](/api-reference/screenshot) requested with `?async=true`. Returns `404 DATA_NOT_FOUND` if the image isn't available yet.
The `job_id` returned by the screenshot request.
```bash cURL theme={null}
curl https://api.tapkit.ai/v1/jobs/$JOB_ID/download \
-H "X-API-Key: $TAPKIT_API_KEY" \
-o screen.png
```
# Drag
Source: https://docs.tapkit.ai/api-reference/drag
POST /phones/{phone_id}/drag
Press, move, and release
Press at one point, move to another, and release. Use a drag to move a slider, scroll an exact distance, or pull to refresh.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Start position, pixels from the left edge.
Start position, pixels from the top edge.
End position, pixels from the left edge.
End position, pixels from the top edge.
How fast to move: `slow`, `medium`, or `fast`.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/drag \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from_x": 585, "from_y": 600, "to_x": 585, "to_y": 1400, "speed": "medium"}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Flick
Source: https://docs.tapkit.ai/api-reference/flick
POST /phones/{phone_id}/flick
Quick swipe from a point
Swipe quickly from a point in one direction. Flicks scroll lists, turn pages, and dismiss sheets. To move further down a list, flick `up`.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Horizontal position in pixels from the left edge.
Vertical position in pixels from the top edge.
`up`, `down`, `left`, or `right`.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/flick \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"x": 585, "y": 1500, "direction": "up"}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Get apps
Source: https://docs.tapkit.ai/api-reference/get-apps
GET /phones/{phone_id}/apps
List the apps installed on a phone
Returns the phone's installed apps and Apple's built-in apps. TapKit saves the list the first time you request it. Call [Refresh apps](/api-reference/refresh-apps) after you install or remove apps.
The phone's ID, from [List phones](/api-reference/list-phones).
## Response
Each app's `name` and `bundle_id`.
Number of apps.
When the list was last fetched from the phone. Absent if it was fetched for this request.
```bash cURL theme={null}
curl https://api.tapkit.ai/v1/phones/$PHONE_ID/apps \
-H "X-API-Key: $TAPKIT_API_KEY"
```
```json 200 theme={null}
{
"apps": [
{ "name": "Safari", "bundle_id": "com.apple.mobilesafari" },
{ "name": "Settings", "bundle_id": "com.apple.Preferences" }
],
"app_count": 2,
"updated_at": "2026-09-23T18:30:00Z",
"source": "database"
}
```
# Get job
Source: https://docs.tapkit.ai/api-reference/get-job
GET /jobs/{job_id}
Check the status of an action
Poll a job started with `?async=true`. See [Jobs](/api-reference/introduction#jobs).
The `job_id` returned by the action.
## Response
The job's ID.
`pending`, `running`, `completed`, or `failed`.
On failure, `error` describes what went wrong.
When the job was created.
When the job finished.
```bash cURL theme={null}
curl https://api.tapkit.ai/v1/jobs/$JOB_ID \
-H "X-API-Key: $TAPKIT_API_KEY"
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "failed",
"result": { "error": "TIMEOUT" },
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:31:00Z"
}
```
# Get phone status
Source: https://docs.tapkit.ai/api-reference/get-status
GET /phones/{phone_id}/status
Check whether a phone is connected
The phone's ID, from [List phones](/api-reference/list-phones).
## Response
The phone's ID.
The phone's TapKit name, or the name it reports if you haven't renamed it.
`online`, `available`, or `offline`. See [List phones](/api-reference/list-phones).
Screen width in pixels.
Screen height in pixels.
The response has other fields that aren't part of the API. Don't rely on them.
```bash cURL theme={null}
curl https://api.tapkit.ai/v1/phones/$PHONE_ID/status \
-H "X-API-Key: $TAPKIT_API_KEY"
```
```json 200 theme={null}
{
"phone_id": "d9fb00a7-1c7a-44dc-88f6-d06ba5061fdd",
"phone_name": "Test phone",
"connection_status": "online",
"width": 1170,
"height": 2532
}
```
# Hold and drag
Source: https://docs.tapkit.ai/api-reference/hold-and-drag
POST /phones/{phone_id}/hold-and-drag
Long press, then drag
Press and hold at one point, then drag to another and release. Use it to move app icons, reorder lists, and drag and drop.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Start position, pixels from the left edge.
Start position, pixels from the top edge.
End position, pixels from the left edge.
End position, pixels from the top edge.
How long to hold before moving, in milliseconds.
How fast to move: `slow`, `medium`, or `fast`.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/hold-and-drag \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from_x": 300, "from_y": 800, "to_x": 900, "to_y": 800, "hold_duration_ms": 500, "speed": "medium"}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Home
Source: https://docs.tapkit.ai/api-reference/home
POST /phones/{phone_id}/home
Go to the Home Screen
Go to the Home Screen from any app.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/home \
-H "X-API-Key: $TAPKIT_API_KEY"
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# API overview
Source: https://docs.tapkit.ai/api-reference/introduction
Control connected iPhones over HTTP
The REST API sends touch and keyboard input to your iPhones and takes screenshots. It has the same actions that agents get through [MCP](/integrations/mcp), for code that doesn't use an agent framework.
## Base URL
```
https://api.tapkit.ai/v1
```
## Authentication
Send an API key in the `X-API-Key` header. Create keys in the [web app](/web-app#api-keys). Keys start with `tk_`.
```bash theme={null}
curl https://api.tapkit.ai/v1/phones \
-H "X-API-Key: $TAPKIT_API_KEY"
```
A key has access to every phone in your organization. Keep it out of source control and client-side code.
## Before you call an action
A phone accepts actions when its Mac app is open and signed in, the phone is connected, unlocked, and set up in the Mac app, and the phone is active on your subscription.
[List phones](/api-reference/list-phones) returns each phone's ID and `connection_status`.
## Coordinates
Coordinates are integer pixels on the phone's screen, measured from the top-left corner. They match the pixels of a [screenshot](/api-reference/screenshot). [List phones](/api-reference/list-phones) reports each screen's `width` and `height`.
## Jobs
Every action runs as a job on the phone's Mac. By default, a request waits up to 60 seconds for the job to finish and then returns it:
```json theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
`status` is `pending`, `running`, `completed`, or `failed`. A failed action still returns HTTP 200, with the reason in `result.error`. If an action doesn't finish in 60 seconds, its job fails with `"error": "TIMEOUT"`.
Add `?async=true` to get the job ID back immediately, then poll [Get job](/api-reference/get-job):
```json theme={null}
{ "job_id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61" }
```
## Errors
Other errors return an HTTP error status, with details under `detail`:
```json theme={null}
{
"detail": {
"error": "PHONE_NOT_FOUND",
"message": "Phone not found",
"context": { "phone_id": "..." }
}
}
```
| Status | `error` | Meaning |
| ------ | ----------------------- | ------------------------------------------------------------------------------------------- |
| 401 | `AUTH_REQUIRED` | No `X-API-Key` header |
| 401 | `INVALID_API_KEY` | The key is wrong or revoked |
| 402 | `SUBSCRIPTION_REQUIRED` | Your organization has no active subscription |
| 404 | `PHONE_NOT_FOUND` | No phone with that ID in your organization |
| 409 | `PHONE_INACTIVE` | The phone isn't active on your subscription. Activate it in the [web app](/web-app#phones). |
| 400 | `MAC_APP_NOT_RUNNING` | The phone's Mac app isn't connected |
| 408 | `TIMEOUT` | A screenshot didn't arrive in time |
# Press key
Source: https://docs.tapkit.ai/api-reference/keypress
POST /phones/{phone_id}/keypress
Press a keyboard key
Press a keyboard key on the phone, optionally holding modifier keys. Use it to delete text, submit a field, dismiss the keyboard, or move the cursor.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
`enter`, `escape`, `backspace`, `arrow_up`, `arrow_down`, `arrow_left`, or `arrow_right`.
Modifier keys to hold: any of `control`, `shift`, `alternate`, `command`.
How many times to press the key. At least 1.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/keypress \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"key": "backspace", "repeat": 5}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# List phones
Source: https://docs.tapkit.ai/api-reference/list-phones
GET /phones
List every phone in your organization
Returns every phone your organization has connected, including offline ones.
## Response
The phone's ID. Pass it as `phone_id` to every other endpoint.
The name the phone reports, such as "Tom's iPhone".
The name you gave the phone in TapKit, if any. See [Rename phone](/api-reference/rename-phone).
`online` if the phone is connected and selected on its Mac, `available` if it's connected to a Mac but not selected, or `offline`.
`active` or `inactive`. Only active phones accept actions.
The Mac the phone is connected to.
Screen width in pixels.
Screen height in pixels.
When the phone was first connected.
The response has other fields that aren't part of the API. Don't rely on them.
```bash cURL theme={null}
curl https://api.tapkit.ai/v1/phones \
-H "X-API-Key: $TAPKIT_API_KEY"
```
```json 200 theme={null}
[
{
"id": "d9fb00a7-1c7a-44dc-88f6-d06ba5061fdd",
"name": "iPhone",
"display_name": "Test phone",
"connection_status": "online",
"activation_state": "active",
"connected_mac_id": "3f1e8c52-6a0b-4d8e-9b7a-1c2d3e4f5a6b",
"width": 1170,
"height": 2532,
"created_at": "2026-09-11T17:02:44Z"
}
]
```
# Refresh apps
Source: https://docs.tapkit.ai/api-reference/refresh-apps
POST /phones/{phone_id}/apps/refresh
Fetch a phone's app list again
Fetches the app list from the phone and saves it. Returns the same `apps` and `app_count` as [Get apps](/api-reference/get-apps).
The phone's ID, from [List phones](/api-reference/list-phones).
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/apps/refresh \
-H "X-API-Key: $TAPKIT_API_KEY"
```
# Rename phone
Source: https://docs.tapkit.ai/api-reference/rename-phone
PATCH /phones/{phone_id}/settings
Change the name TapKit and agents use for a phone
The phone's ID, from [List phones](/api-reference/list-phones).
## Body
The new name.
## Response
The updated phone, in the same shape as [List phones](/api-reference/list-phones).
```bash cURL theme={null}
curl -X PATCH https://api.tapkit.ai/v1/phones/$PHONE_ID/settings \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"display_name": "Test phone"}'
```
# Screenshot
Source: https://docs.tapkit.ai/api-reference/screenshot
GET /phones/{phone_id}/screenshot
Capture a phone's screen
Returns a PNG of the phone's screen at full resolution. Pixel positions in the image are the coordinates gesture endpoints expect.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately. Fetch the image with [Download screenshot](/api-reference/download-screenshot) once the [job](/api-reference/get-job) completes.
## Response
`image/png` bytes. If the screenshot doesn't arrive within 60 seconds, the request fails with `408 TIMEOUT`.
```bash cURL theme={null}
curl https://api.tapkit.ai/v1/phones/$PHONE_ID/screenshot \
-H "X-API-Key: $TAPKIT_API_KEY" \
-o screen.png
```
# Tap
Source: https://docs.tapkit.ai/api-reference/tap
POST /phones/{phone_id}/tap
Tap a point on the screen
Tap once at a point.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Horizontal position in pixels from the left edge.
Vertical position in pixels from the top edge.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/tap \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"x": 585, "y": 1266}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Tap and hold
Source: https://docs.tapkit.ai/api-reference/tap-and-hold
POST /phones/{phone_id}/tap-and-hold
Long press a point on the screen
Press and hold at a point. A long press opens context menus and previews, and puts the Home Screen into edit mode.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Horizontal position in pixels from the left edge.
Vertical position in pixels from the top edge.
How long to hold, in milliseconds.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/tap-and-hold \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"x": 585, "y": 1266, "duration_ms": 1000}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Triple tap
Source: https://docs.tapkit.ai/api-reference/triple-tap
POST /phones/{phone_id}/triple-tap
Triple tap a point on the screen
Tap three times quickly at a point. Triple tapping selects a line or paragraph of text.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
Horizontal position in pixels from the left edge.
Vertical position in pixels from the top edge.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/triple-tap \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"x": 585, "y": 1266}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Type
Source: https://docs.tapkit.ai/api-reference/type
POST /phones/{phone_id}/type
Type text into the focused field
Type text into the focused field. Tap the field first so the keyboard is showing.
The phone's ID, from [List phones](/api-reference/list-phones).
Return `{"job_id": ...}` immediately instead of waiting for the action. See [Jobs](/api-reference/introduction#jobs).
## Body
The text to type. US-ASCII only, up to 100 characters.
Emoji, accented letters, and other characters outside US-ASCII can't be typed. Split text longer than 100 characters across several requests.
```bash cURL theme={null}
curl -X POST https://api.tapkit.ai/v1/phones/$PHONE_ID/type \
-H "X-API-Key: $TAPKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "hello world"}'
```
```json 200 theme={null}
{
"id": "5d2c9a0e-7f7b-4c1e-9d1a-2f4b8e0c3a61",
"status": "completed",
"result": {},
"created_at": "2026-09-23T18:30:00Z",
"completed_at": "2026-09-23T18:30:01Z"
}
```
# Examples
Source: https://docs.tapkit.ai/examples
Prompts to try with TapKit
Paste any of these into the Mac app's agent chat, or into Claude or Codex with the TapKit plugin installed. Sign in to the apps you want the agent to use first. Agents can't get past Face ID or CAPTCHAs, and won't type a password you haven't given them.
## Book a flight
The agent searches for flights, fills in the dates and airports, and compares the results. Give it the airports, dates, and number of passengers, and say whether to stop at the results or go on to book.
> In Safari, go to Google Flights and search for round-trip flights from Orlando to Denver, leaving next Friday and returning Sunday. Find the cheapest option and show me the results.
## Book a haircut
Salon booking apps such as Booksy, Vagaro, and Fresha all follow the same steps: pick a service, a place, a stylist, and a time. Name the service and time you want, and tell the agent how to pick a stylist so it doesn't stop to ask.
> Open Booksy and book a men's haircut for Saturday at 2pm. Pick the closest barbershop with availability and book with whoever is free.
## Order dinner
The agent finds a restaurant in a delivery app, adds items to the cart, and checks out. List exactly what to order, and include a tip amount if you care about it. The app needs a saved address and payment method before the agent can place the order.
> Open Uber Eats and order a pepperoni pizza and a Caesar salad from the highest-rated Italian restaurant nearby. Use my saved address and payment method.
## Dating apps
The agent reads each profile, likes or passes based on what you tell it to look for, and can write opening messages to matches. Set a limit on how many profiles to review so it doesn't run indefinitely.
> Open Hinge and look at 10 profiles. Like people who seem active and outdoorsy, and pass on profiles without a bio. If we match with someone, send a friendly opening message that mentions something from their profile.
## QA testing
The agent can run a test script on a real iPhone, against your own app or anyone else's, without test IDs or XCUITest. Write the test as a series of steps, each with what should be on screen afterwards, and ask for a pass or fail on each step.
> Open Safari and go to example.com. Check that the page shows the heading "Example Domain". Tap "More information...". Check that a new page loads, and report whether each step passed.
## Writing prompts
Name the app, and give specific dates, names, and amounts rather than "a cheap one". Say whether the agent may send, post, or pay, or should check with you first. Agents can't type emoji or accented letters.
# How it works
Source: https://docs.tapkit.ai/how-it-works
How the Mac, the iPhone, and your agent connect
TapKit has three parts: your iPhone, the TapKit Mac app, and the TapKit cloud.
## The Mac hosts the iPhone
The iPhone stays plugged into a Mac running the TapKit app. The Mac app is the only part of TapKit that talks to the phone. It sees the phone's screen and carries out taps, flicks, typing, and key presses on it.
To disconnect a phone, unplug it or quit the Mac app.
## Agents reach the Mac through the cloud
While you're signed in, the Mac app keeps a connection open to the TapKit cloud. Claude, Codex, and your own code send commands to the cloud through [MCP](/integrations/mcp) or the [REST API](/api-reference/introduction). The cloud forwards each command to the Mac that the target phone is plugged into, and sends the result back.
The built-in agent in the Mac app works with the phone directly and only uses the network to call a hosted language model.
## Why this architecture?
* **No jailbreak, no sideloading, no developer mode.** Nothing is installed on the iPhone.
* **Works with any app.** TapKit interacts with the phone through the same visible interface a person uses, rather than relying on app-specific integrations.
* **Remote by default.** The API layer means your agent can be anywhere. The Mac and phone can be in a closet, a data center, or on your desk.
## What happens when you run a task
```mermaid theme={null}
sequenceDiagram
participant Agent as Your AI Agent
participant Server as TapKit Server
participant Mac as Mac App
participant Phone as iPhone
Agent->>Server: Send command (API / MCP)
Server->>Mac: Route to host Mac
Mac->>Phone: Execute action
Phone-->>Mac: Updated screen
Mac-->>Server: Screenshot
Server-->>Agent: Result
Note over Agent: Decide next action
Agent->>Server: Next command...
```
Every TapKit agent works in a loop. It takes a screenshot, decides what to do, then taps, flicks, types, or presses a key. Then it takes another screenshot to check the result, and repeats until the task is done.
## When a phone is ready
A phone works with TapKit when the Mac app is open and signed in, the iPhone is plugged in and unlocked, and you've finished [setup](/setup) for it in the Mac app. The phone also has to be active on your TapKit subscription. If a phone isn't ready, it shows as offline. See [Setup](/setup#troubleshooting).
# Introduction
Source: https://docs.tapkit.ai/index
Give your agent an iPhone
TapKit lets AI agents use real iPhones. Plug an iPhone into a Mac running the TapKit app, and an agent can take screenshots of it, tap, and type. The agent can be TapKit's built-in agent, Claude, Codex, or your own code.
Nothing is installed on the iPhone. You don't need to jailbreak it, turn on developer mode, or use a simulator.
## What you can do with it
Tell the TapKit Mac app what to do on a phone and watch it work.
Install the TapKit plugin and ask your assistant to use your iPhone.
Point Cursor or your own agent at `https://mcp.tapkit.ai/mcp`.
Send taps, keystrokes, and screenshot requests from your own code.
## Get started
Install the Mac app, connect an iPhone, and run your first task.
How the Mac, the iPhone, and your agent connect.
# Claude app
Source: https://docs.tapkit.ai/integrations/claude-app
Control iPhones from Claude Chat and Cowork
The TapKit plugin adds the TapKit connector and TapKit's [skills](/integrations/skills) to Claude Chat and Cowork. You install it from Cowork in the Claude app. To use TapKit in the terminal, see [Claude Code](/integrations/claude-code).
## 1. Add the TapKit marketplace
1. Open **Cowork** in the Claude app.
2. In the left sidebar, click **Customize**.
3. Go to **Personal Plugins** and click the **+** button.
4. In the popup, hover over **Create Plugin**.
5. Click **Add Marketplace**.
6. Paste this and confirm:
```
jootsing-research/tapkit-plugins-claude
```
## 2. Install the plugin
Still under **Customize** in Cowork, click **Browse Plugins**. Find **TapKit** in the list, click **Install**, then click **Manage**. TapKit now appears under **Personal Plugins** in the Customize sidebar.
## 3. Allow the tools
In the Customize sidebar, click the **TapKit** plugin, click **Connectors**, and select the **TapKit** connector. Set its tool permission to **Always allow**.
## 4. Sign in
The first time Claude uses TapKit, it opens TapKit in your browser. Sign in and click **Allow access**.
## Example prompts
* "Open Settings and take a screenshot"
* "Go to Safari and search for flights to Denver"
* "Open DoorDash and order from the first restaurant"
* "Take a screenshot and tell me what apps are on the home screen"
## Troubleshooting
Open the TapKit connector under **Customize** and reconnect it.
Check that the Mac app is open and signed in, and isn't showing a setup message for the phone. Ask Claude to "list my phones" to see what TapKit sees.
Unlock the phone and check that it's still plugged in. If the Mac app shows a problem with the phone, fix that first.
# Claude Code
Source: https://docs.tapkit.ai/integrations/claude-code
Control iPhones from Claude Code
The TapKit plugin for Claude Code adds the TapKit MCP server and TapKit's [skills](/integrations/skills).
## Install
Run these in Claude Code:
```
/plugin marketplace add jootsing-research/tapkit-plugins-claude
/plugin install tapkit@tapkit-plugins-claude
/reload-plugins
```
## Sign in
Run `/mcp`, select the TapKit server, and choose **Authenticate**. Sign in to TapKit in your browser and click **Allow access**.
If the browser doesn't open, open the URL that Claude Code prints. If the redirect back to Claude Code fails, copy the full URL from the browser's address bar and paste it into Claude Code.
## Try it
```
What's on my phone right now?
```
Claude asks for permission the first time it uses each TapKit tool. Choose the "don't ask again" option to allow the tool for the rest of the project, or approve each action as it comes.
## Update
```
/plugin marketplace update tapkit-plugins-claude
```
## Troubleshooting
Run `/mcp` and check the TapKit server's status. If it needs authentication, authenticate again.
Check that the Mac app is open and signed in, and isn't showing a setup message for the phone. Ask Claude to "list my phones" to see what TapKit sees.
Unlock the phone and check that it's still plugged in. If the Mac app shows a problem with the phone, fix that first.
# Codex app
Source: https://docs.tapkit.ai/integrations/codex-app
Control iPhones from the Codex desktop app
The TapKit plugin for Codex adds the TapKit MCP server and TapKit's [skills](/integrations/skills) to the Codex desktop app. The plugin's source is [on GitHub](https://github.com/jootsing-research/tapkit-plugins-codex). For more on Codex plugins, see [OpenAI's docs](https://developers.openai.com/codex/plugins).
Adding the marketplace uses the [Codex CLI](https://developers.openai.com/codex/cli), so install it first.
## 1. Add the TapKit marketplace
Run this command in your terminal:
```bash theme={null}
codex plugin marketplace add jootsing-research/tapkit-plugins-codex
```
If the Codex app is open, restart it to load the new marketplace.
## 2. Open Plugins
In the Codex app, click **Plugins** in the left sidebar.
## 3. Select the TapKit marketplace
On the Plugins page, open the marketplace dropdown and select **Tap Kit Plugins**.
## 4. Install the plugin
Find **Tap Kit** under **Productivity**, click the **+** button, then click **Install Tap Kit** in the popup. Tap Kit gets a checkmark in the plugin list when it's installed.
## 5. Sign in
Sign in to TapKit in your browser when Codex asks, and click **Allow access**.
## Try it
```
What phones can you see with TapKit right now?
```
## Update
```bash theme={null}
codex plugin marketplace upgrade tapkit-plugins
```
Restart the Codex app afterwards.
# Codex CLI
Source: https://docs.tapkit.ai/integrations/codex-cli
Control iPhones from the Codex CLI
The TapKit plugin for Codex adds the TapKit MCP server and TapKit's [skills](/integrations/skills). The plugin's source is [on GitHub](https://github.com/jootsing-research/tapkit-plugins-codex). For more on Codex plugins, see [OpenAI's docs](https://developers.openai.com/codex/plugins).
You need the [Codex CLI](https://developers.openai.com/codex/cli) installed.
## Install
1. Add the TapKit marketplace:
```bash theme={null}
codex plugin marketplace add jootsing-research/tapkit-plugins-codex
```
2. Start Codex and open the plugin directory:
```bash theme={null}
codex
```
```
/plugins
```
3. Switch to the **Tap Kit Plugins** marketplace, open **Tap Kit**, and select **Install plugin**.
4. Sign in to TapKit in your browser when Codex asks, and click **Allow access**.
## Try it
```
What phones can you see with TapKit right now?
```
## Update
```bash theme={null}
codex plugin marketplace upgrade tapkit-plugins
```
# MCP
Source: https://docs.tapkit.ai/integrations/mcp
Connect any MCP client to TapKit
TapKit runs a hosted MCP server. Any client that supports remote MCP servers with OAuth can connect to it. The only thing you run locally is the [Mac app](/setup).
```
https://mcp.tapkit.ai/mcp
```
The server uses the Streamable HTTP transport.
## Connect
Most clients accept a config like this:
```json theme={null}
{
"mcpServers": {
"tapkit": {
"type": "http",
"url": "https://mcp.tapkit.ai/mcp"
}
}
}
```
Point your MCP client at the URL. The client must support OAuth 2.1 with PKCE and dynamic client registration. The server publishes its OAuth metadata at `/.well-known/oauth-protected-resource/mcp`.
## Sign in
The first time your client connects, it opens TapKit in your browser. Sign in with Google, then click **Allow access** to let the client view and interact with your connected iPhones.
The MCP server only accepts OAuth. [API keys](/web-app#api-keys) are for the [REST API](/api-reference/introduction).
## Tools
Start with `list_phones`. Every other tool takes the `phone_id` it returns.
| Tool | Parameters | Description |
| ------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `list_phones` | None | List phones with their IDs, status, and screen size |
| `get_phone_status` | `phone_id` | Connection status and screen size of one phone |
| `screenshot` | `phone_id` | Capture the screen as an image |
| `tap` | `phone_id`, `x`, `y` | Tap a point |
| `double_tap` | `phone_id`, `x`, `y` | Double tap, for zooming or selecting a word |
| `triple_tap` | `phone_id`, `x`, `y` | Triple tap, for selecting a line or paragraph |
| `long_press` | `phone_id`, `x`, `y`, `duration`? | Press and hold, for context menus. `duration` is in ms, default 1000 |
| `flick` | `phone_id`, `x`, `y`, `direction` | Quick swipe `up`, `down`, `left`, or `right` from a point |
| `drag` | `phone_id`, `from_x`, `from_y`, `to_x`, `to_y`, `speed`? | Press, move, and release. `speed` is `slow`, `medium`, or `fast`, default `medium` |
| `hold_and_drag` | `phone_id`, `from_x`, `from_y`, `to_x`, `to_y`, `hold_duration_ms`?, `speed`? | Long press, then drag, for moving items. Hold defaults to 500 ms |
| `type_text` | `phone_id`, `text` | Type into the focused field. US-ASCII only, up to 100 characters |
| `press_key` | `phone_id`, `key`, `modifiers`?, `repeat`? | Press `enter`, `escape`, `backspace`, or an arrow key, optionally holding `control`, `shift`, `alternate`, or `command` |
| `press_home` | `phone_id` | Go to the Home Screen |
## Coordinates
Screenshots are JPEGs scaled so the long edge is at most 1344 pixels. Tool coordinates are pixels in that image, measured from the top-left corner. The server converts them to the phone's native resolution.
Action tools don't return a screenshot. Call `screenshot` after an action to see the result.
## Limits
Each connection can make 300 requests per minute. Agents can only control phones that are online and active on your subscription.
# Integrations
Source: https://docs.tapkit.ai/integrations/overview
Use TapKit from Claude, Codex, and any MCP client
Every integration connects to the same hosted MCP server, `https://mcp.tapkit.ai/mcp`, and signs in with your TapKit account in the browser. The plugins for Claude and Codex also install [skills](/integrations/skills) that teach the agent how to navigate iOS.
All integrations need the [TapKit Mac app](/setup) running with at least one connected, active iPhone.
Plugin for Claude in the terminal.
Plugin for Claude Chat and Cowork.
Plugin for Codex in the terminal.
Plugin for the Codex desktop app.
Cursor, your own agent, or anything else that speaks MCP.
iOS navigation know-how for agents without a TapKit plugin.
To control phones from your own code without an agent framework, use the [REST API](/api-reference/introduction).
# Skills
Source: https://docs.tapkit.ai/integrations/skills
Teach agents how to use TapKit and navigate iOS
Skills are Markdown instructions that an agent loads when it needs them. They follow the open [Agent Skills](https://agentskills.io) standard. TapKit's skills teach an agent how to control an iPhone through TapKit and where to find things in iOS and Apple's apps. The skills' source is [on GitHub](https://github.com/jootsing-research/skills).
The [Claude](/integrations/claude-code) and [Codex](/integrations/codex-cli) plugins include these skills. Install them separately only for other agents.
## Install
```bash theme={null}
npx skills add jootsing-research/skills
```
Choose your agent when prompted. Skills don't give an agent access to your phone, so connect the agent to the [MCP server](/integrations/mcp) too.
## Available skills
The core `tapkit` skill teaches an agent how to use TapKit's tools. The rest include a few skills for getting around iOS, such as finding apps and editing text, and several skills for specific Apple apps.
# Mac app
Source: https://docs.tapkit.ai/mac-app
The app that connects your phones to TapKit
The TapKit Mac app connects your iPhones to your TapKit account. Once a phone is connected, the built-in agent, Claude, Codex, the API, and any other MCP client can use it. TapKit can only see or control a phone while the app is open and signed in.
## Connected phones
The main window shows the live screen of every iPhone connected to this Mac. To add a phone, plug it in. It goes through the same trust, screen, and Bluetooth checks as the first one. See [Setup](/setup#adding-more-phones).
Each phone has to stay plugged in, unlocked, and paired with the Mac over Bluetooth. If a phone isn't ready, the app tells you why and what to do.
## Gestures and device controls
You can control a phone by hand from the Mac app. Pick a gesture and click on the phone's screen. The gestures are tap, double tap, triple tap, tap and hold, flick, drag, and hold and drag. **Live** lets you click and drag on the screen like a finger. **Home** goes to the Home Screen.
Use manual control to test the connection, set up a phone before a task, or take over while an agent is working. Your Mac's keyboard doesn't type on the phone.
## Agent chat
The built-in agent lets you try TapKit without setting up another tool. Open a phone's chat and type a task:
> Add eggs to my Groceries list in Reminders
The agent looks at the screen, acts, and repeats until the task is done or it needs your input. Each step shows on the phone and in the chat, and you can stop the agent at any time. The agent can use any connected phone, and asks which one if your task doesn't say.
TapKit doesn't save chats when you quit.
## Connection status
When a phone isn't ready, first check that TapKit is open and signed in and that the iPhone is plugged in and unlocked. The Mac app shows a message for the phone saying what's wrong, such as asking you to tap **Trust** on the iPhone or reconnect Bluetooth. If agents still can't use the phone, check that it's active on your subscription in the [web app](/web-app#phones).
[Setup troubleshooting](/setup#troubleshooting) covers specific problems.
## Settings
Most settings are behind the **Account** button in the toolbar. There you can rename a phone, which changes the name agents see. You can also fix Camera or Bluetooth access, copy the MCP server URL to [connect another AI tool](/integrations/overview), manage your subscription, add phones to it, and sign out.
If a phone gets stuck in a state you can't fix, choose **TapKit > Settings...** (⌘,) and click **Run Setup Again** to go through setup from the start.
TapKit updates itself. Choose **TapKit > Check for Updates...** to check now.
# Quickstart
Source: https://docs.tapkit.ai/quickstart
Connect an iPhone and run your first task
## What you need
You need a Mac running macOS 14.6 or later, an iPhone, and a USB data cable.
## 1. Install TapKit and connect your iPhone
Download TapKit, drag it into **Applications**, and open it. The app walks you through signing in, allowing Camera and Bluetooth access, trusting the Mac on your iPhone, and pairing over Bluetooth.
Each setup step, and what to do if one fails.
## 2. Run a task in the Mac app
Open the chat for your phone in the Mac app and type a task in the **Do anything** box:
> Open the Clock app and start a 2 minute timer
The agent takes screenshots, taps, and types on the phone while you watch. For more tasks to try, see [Examples](/examples).
## 3. Connect your own AI tools
Install the TapKit plugin for Claude or Codex, or add the TapKit MCP server to any MCP client.
Claude Code, the Claude app, Codex, and other MCP clients.
Control phones from your own code.
# Security and privacy
Source: https://docs.tapkit.ai/security
What TapKit can access and where your data goes
## What's on the iPhone
TapKit installs nothing on the iPhone and doesn't need a jailbreak or developer mode. The Mac app can only reach a phone that is plugged into the Mac, trusts it, and is paired with it over Bluetooth.
TapKit can't get past the passcode, Face ID, or anything else that would stop a person holding the phone. To cut off TapKit, unplug the phone, quit the Mac app, or forget the Mac in **Settings > Bluetooth**.
## Mac permissions
| Permission | Used for | Not used for |
| ---------- | ---------------------------------------------- | -------------------------- |
| Camera | Seeing the iPhone's screen | Your Mac's webcam |
| Bluetooth | Pairing with the iPhone and sending it actions | Any other Bluetooth device |
## Where screenshots and actions go
**Built-in agent.** The Mac app captures screenshots and sends them through TapKit's servers to a hosted language model, which picks the next action. The Mac app sends actions straight to the phone.
**MCP and REST API.** Your agent's commands go through TapKit's servers to your Mac. Screenshots come back the same way, and TapKit stores them. Your agent's provider, such as Anthropic or OpenAI, receives every screenshot your agent requests.
**Live streaming.** When you watch a phone in the web app, Agora streams its screen to your browser.
The Mac app shows each phone's screen on the Mac. It only sends the screen elsewhere when an agent takes a screenshot or someone opens a live stream.
## Access control
The Mac app signs in through tapkit.ai and keeps its session in the macOS Keychain.
MCP clients sign in with OAuth, and you approve each one on a consent screen. Access tokens expire after an hour, and clients refresh them automatically.
The REST API uses API keys from the [web app](/web-app#api-keys). TapKit stores only a hash of each key, and a revoked key stops working immediately.
Everyone in your organization can use its phones.
## Agent safety
TapKit's skills tell agents to check typed text and stop before submitting it unless you've asked them to. They also tell agents not to type passwords you haven't given them. These rules reduce risk but don't remove it, because an agent with your phone can do anything you can. Use a phone and accounts you're comfortable handing to an agent.
## Legal
Read the [Privacy Policy](https://tapkit.ai/privacy), [Terms of Service](https://tapkit.ai/terms), and [list of subprocessors](https://tapkit.ai/subprocessors). Report security issues to [security@tapkit.ai](mailto:security@tapkit.ai).
# Setup
Source: https://docs.tapkit.ai/setup
Install the Mac app and connect an iPhone
The TapKit Mac app walks you through setup. This page covers the same steps and what to do when one fails.
## Requirements
You need a Mac running macOS 14.6 or later, an iPhone, and a USB data cable. The iPhone stays plugged into the Mac while TapKit uses it.
## 1. Install
Download TapKit.dmg, open it, and drag **TapKit** into **Applications**. Then open TapKit from Applications.
TapKit updates itself. To check for an update now, choose **TapKit > Check for Updates...**.
## 2. Sign in
Click **Log in with browser**. TapKit opens tapkit.ai in your browser. Sign in with Google, then click **Open the desktop app**. The Mac app continues once you're signed in.
## 3. Allow Camera and Bluetooth
TapKit needs two macOS permissions. [Security and privacy](/security#mac-permissions) explains how it uses them.
| Permission | Button | Why |
| ---------- | ------------------- | ------------------------------------------------------------------------- |
| Camera | **Enable Camera** | Lets TapKit see the iPhone's screen. TapKit never uses your Mac's webcam. |
| Bluetooth | **Allow Bluetooth** | Lets TapKit pair with the iPhone and control it. |
Click each button and choose **Allow** when macOS asks. If you denied a permission earlier, the button opens System Settings instead. Turn on TapKit under **Privacy & Security > Camera** or **Privacy & Security > Bluetooth**.
## 4. Connect your iPhone
Plug the iPhone into the Mac and unlock it. Select it under **Connected iPhones** and click **Continue**. If the iPhone asks whether to trust this computer, tap **Trust** and enter your passcode.
TapKit then checks that it can see the phone's screen. Keep the phone plugged in and unlocked during this step. TapKit may put the phone to sleep for a moment and ask you to unlock it again. If TapKit still can't see the screen, it asks to restart the phone. Click **Restart iPhone**, then unlock the phone when it comes back on.
## 5. Pair over Bluetooth
TapKit turns on AssistiveTouch on the iPhone for you. Then, on the iPhone, open **Settings**, tap **Bluetooth**, and tap your Mac's name.
The Mac app moves on as soon as the phone connects. If your Mac's name doesn't appear, go back to Settings and tap **Bluetooth** again.
## 6. Subscribe
Click **Start subscription** to check out in your browser, or **Explore TapKit first** to skip this step. Agents can only control phones that are active on a subscription. You can subscribe or add phones later from **Account > Billing** in the Mac app or from the [web app](/web-app#billing).
When you see **Your agents have an iPhone now.**, click **Start using TapKit**.
## Adding more phones
Plug in another iPhone. It appears next to your other phones in the Mac app and goes through the same trust, screen, and Bluetooth steps. Each phone takes one phone slot on your subscription.
To run the whole setup again, choose **TapKit > Settings...** (⌘,) and click **Run Setup Again** on the **General** tab.
## Troubleshooting
Unlock the iPhone and try a different cable or USB port. Some cables only charge. Unplug and reconnect, and tap **Trust** if the iPhone asks.
TapKit can't unlock a phone, so the phone has to stay unlocked. Set **Settings > Display & Brightness > Auto-Lock** to **Never** to stop it from locking between tasks.
Keep the phone plugged in and unlocked. If TapKit offers to restart the phone, let it. If that doesn't work, click **Try Again**, or unplug the phone, restart it yourself, and plug it back in.
Check that Bluetooth is on for both devices and that the Mac app is still on the pairing step. Leave **Settings > Bluetooth** and open it again to refresh the list.
Check that AssistiveTouch is on in **Settings > Accessibility > Touch > AssistiveTouch**. If it's on and taps still fail, open the phone's **More** menu in the Mac app and choose **Reset AssistiveTouch**.
Open **System Settings > Privacy & Security**, choose **Camera** or **Bluetooth**, and turn on TapKit. Then quit and reopen TapKit.
Still stuck? Email [support@tapkit.ai](mailto:support@tapkit.ai).
# Web app
Source: https://docs.tapkit.ai/web-app
Manage phones, API keys, your team, and billing at tapkit.ai
Use the [TapKit web app](https://tapkit.ai/dashboard) to manage your organization. Sign in with the same account you use in the Mac app.
## Phones
The Phones page lists every phone your organization has connected. For each phone it shows the status, the Mac it's connected to, and whether it's active.
A phone's status is **Online** when it's connected and selected on its Mac, **Available** when it's connected to a Mac but not selected, and **Offline** otherwise.
Agents can only control active phones. Your plan sets how many phones can be active at once. Click **Activate** or **Deactivate** to choose which ones.
To rename a phone, click the pencil next to its name. Agents see this name. To remove a phone you no longer use, click **Edit**.
### Live stream
Click **View Stream** to watch a phone's screen in real time and tap, flick, and drag on it from your browser. The phone's Mac must be online, and each Mac can stream one phone at a time.
## API keys
API keys authenticate requests to the [REST API](/api-reference/introduction).
To create a key, click **Create API Key** and give it a name. Copy the key right away. It starts with `tk_`, and you can't view it again after you close the dialog.
The list shows each key's prefix, when it was created, and when it was last used. Click **Revoke** to disable a key immediately.
## Sessions
The Sessions page shows how much streaming and viewing time your organization has used this billing period. Its history lists each live stream with its phone, start time, length, and viewers.
## Team
Invite people to your organization by email as a **Member** or **Admin**. Admins can remove members and revoke pending invitations. Everyone in the organization shares its phones and API keys.
## Settings
Set your display name, which other people see when you watch a stream, and your organization's name.
## Billing
You pay for your subscription per phone. Subscribe or change how many phones you pay for on the Billing page.
Added phones are available immediately, and you pay a prorated amount for the rest of the billing period. Removed phones stay active until the period ends. You can choose which phones to deactivate then, or TapKit deactivates the oldest ones.
Click **Manage Payment & Invoices** to update your card, download invoices, or cancel in Stripe.