API server for droidrun - automate your Android phone using LLMs (Claude, ChatGPT, Gemini, DeepSeek, Ollama). Just describe what you want done.
- Enable Developer Options: Settings > About Phone > Tap "Build Number" 7 times
- Enable USB Debugging: Settings > Developer Options > USB Debugging
- Connect phone via USB
- When prompted, tap "Allow" to authorize your computer
Get a key from one of the supported providers:
- Google AI Studio - Gemini (recommended)
- Anthropic - Claude
- OpenAI - ChatGPT / GPT-4
- DeepSeek - DeepSeek
- Ollama - Local models (no API key needed)
# 1. Start the server
docker run -d --name droidrun \
--privileged \
--network=host \
-v /dev/bus/usb:/dev/bus/usb \
-v ~/.android:/root/.android \
-e DROIDRUN_SERVER_KEY="change-me" \
ghcr.io/8ff/droidrunnerd:latest
# 2. Verify it's running
curl http://localhost:8000/healthMac users: Replace
--network=hostwith-p 8000:8000. See Wireless ADB below.
Docker on Mac doesn't support USB passthrough, so you'll need to connect to your phone over WiFi instead.
- Go to Settings > Developer Options > Wireless debugging
- Enable Wireless debugging
- Tap Pair device with pairing code - note the pairing code and IP:port
docker run -d --name droidrun \
--privileged \
-p 8000:8000 \
-v ~/.android:/root/.android \
-e DROIDRUN_SERVER_KEY="change-me" \
ghcr.io/8ff/droidrunnerd:latest# Pair (one-time) - use IP:port and code from Wireless debugging screen
docker exec -it droidrun adb pair 192.168.1.100:37123
# Enter pairing code when prompted
# Connect (use the main IP:port shown under "Wireless debugging", not the pairing port)
docker exec droidrun adb connect 192.168.1.100:5555
# Verify connection
docker exec droidrun adb devicesNote: The pairing port (e.g., 37123) and connect port (e.g., 5555) are different. Check the Wireless debugging screen for both.
# Download the client (or build: cd client && go build -o droidrun-client)
# Releases: https://github.com/8ff/droidrunnerd/releases
# Set credentials
export DROIDRUN_SERVER_KEY="change-me"
export LLM_API_KEY="your-api-key"
# Run a task (defaults to Google/Gemini)
./droidrun-client -server http://localhost:8000 -key $LLM_API_KEY "open settings"
# Specify provider and model
./droidrun-client -server http://localhost:8000 -key $LLM_API_KEY \
-provider Anthropic -model claude-sonnet-4-20250514 "open settings"
# Run a predefined task
./droidrun-client -server http://localhost:8000 -task tasks/whatsapp-reply.toml
# Discover deep links for an app
./droidrun-client -server http://localhost:8000 -deeplinks com.instagram.android
# Run a task with a deep link (opens specific screen before the agent starts)
./droidrun-client -server http://localhost:8000 -key $LLM_API_KEY \
-app com.instagram.android -deeplink "instagram://mainfeed" "like the first post"# Submit a task (defaults to Google/Gemini)
curl -X POST http://localhost:8000/run \
-H "Content-Type: application/json" \
-H "X-Server-Key: $DROIDRUN_SERVER_KEY" \
-H "X-API-Key: $LLM_API_KEY" \
-d '{"goal":"open WhatsApp and send hello to Mom"}'
# With specific provider/model
curl -X POST http://localhost:8000/run \
-H "Content-Type: application/json" \
-H "X-Server-Key: $DROIDRUN_SERVER_KEY" \
-H "X-API-Key: $LLM_API_KEY" \
-d '{"goal":"open WhatsApp", "provider":"Anthropic", "model":"claude-sonnet-4-20250514"}'
# Check task status
curl -H "X-Server-Key: $DROIDRUN_SERVER_KEY" http://localhost:8000/task/TASK_ID
# Cancel a task
curl -X DELETE -H "X-Server-Key: $DROIDRUN_SERVER_KEY" http://localhost:8000/task/TASK_IDTask files are TOML configs for reusable tasks. Example with a deep link:
[task]
name = "instagram-feed"
description = "Like the first post on Instagram feed"
[task.goal]
app = "com.instagram.android"
deeplink = "instagram://mainfeed"
prompt = """
1. You are on the Instagram main feed.
2. Like the first post you see.
3. Return to home desktop when done.
"""
[task.model]
provider = "Google"
model = "gemini-flash-latest"
[task.options]
reasoning = true
vision = false
max_steps = 15Run with: ./droidrun-client -task tasks/instagram-feed.toml -server http://localhost:8000
Use -deeplinks to discover available deep links for an app before writing task files.
Base URL: http://localhost:8000
Authentication: All endpoints except /health require the X-Server-Key header.
Submit a new task to the queue.
Headers:
Content-Type: application/json
X-Server-Key: your-server-key
X-API-Key: your-llm-api-key
Request:
{
"goal": "open WhatsApp and check unread messages",
"provider": "Google",
"model": "gemini-2.0-flash",
"max_steps": 30
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
goal |
string | Yes | - | What you want the agent to do |
app |
string | No | - | Android package to launch (e.g. com.whatsapp) |
deeplink |
string | No | - | Deep link URI to open (e.g. instagram://mainfeed) |
provider |
string | No | Google |
LLM provider (see below) |
model |
string | No | auto | Model name |
max_steps |
int | No | 30 |
Maximum steps (1-100) |
If both app and deeplink are set, the app is launched first, then the deep link is opened. If only deeplink is set, it opens directly (which implicitly opens the app).
Providers:
| Provider | Default Model |
|---|---|
Google |
gemini-2.0-flash |
Anthropic |
claude-sonnet-4-20250514 |
OpenAI |
gpt-4o |
DeepSeek |
deepseek-chat |
Ollama |
llama3.2 |
Response: 200 OK
{
"task_id": "a1b2c3d4",
"status": "queued",
"position": 0
}Get task status and result.
Headers:
X-Server-Key: your-server-key
Response: 200 OK
{
"id": "a1b2c3d4",
"status": "completed",
"success": true,
"result": "Found 3 unread messages in WhatsApp",
"error": "",
"logs": "...",
"steps": [...],
"request": {
"goal": "open WhatsApp and check unread messages",
"provider": "Google",
"model": "gemini-2.0-flash",
"max_steps": 30
},
"created_at": "2025-01-28T10:00:00Z",
"started_at": "2025-01-28T10:00:01Z",
"finished_at": "2025-01-28T10:00:15Z"
}| Field | Description |
|---|---|
status |
queued, running, completed, failed, cancelled |
success |
Whether the goal was achieved |
result |
Agent's final answer/summary |
error |
Error message if failed |
logs |
Execution logs |
steps |
Array of steps taken |
Cancel a queued or running task.
Headers:
X-Server-Key: your-server-key
Response: 200 OK
{
"status": "cancelled"
}Discover available deep links for an installed app. Runs adb shell dumpsys package and parses intent filters for non-http/https URI schemes.
Headers:
X-Server-Key: your-server-key
Query Parameters:
| Parameter | Required | Description |
|---|---|---|
app |
Yes | Android package name (e.g. com.instagram.android) |
Response: 200 OK
{
"app": "com.instagram.android",
"deeplinks": [
"instagram://camera",
"instagram://mainfeed",
"instagram://reels_home"
]
}Health check. No authentication required.
Response: 200 OK
{
"status": "ok",
"version": "1.0.0",
"queue_size": 0,
"current_task": ""
}All errors return JSON:
{
"error": "error message",
"request_id": "abc123"
}| Code | Description |
|---|---|
400 |
Bad request (invalid JSON, missing goal, etc.) |
401 |
Unauthorized (missing or invalid X-Server-Key) |
404 |
Task not found |
405 |
Method not allowed |
# Build container
docker build -t droidrun .
# Or build binaries directly
cd server && go build -o droidrun-server
cd client && go build -o droidrun-client| Variable | Description |
|---|---|
DROIDRUN_SERVER_KEY |
Required. Server authentication key |
GOOGLE_API_KEY |
Google AI API key |
ANTHROPIC_API_KEY |
Anthropic API key |
OPENAI_API_KEY |
OpenAI API key |
No device detected:
# Check ADB sees your phone
docker exec droidrun adb devices
# Should show your device, not "unauthorized""unauthorized" device:
- Check phone screen for USB debugging prompt
- Tap "Allow" and check "Always allow"
Container won't start:
- Ensure
DROIDRUN_SERVER_KEYis set (required)
This project is a wrapper around droidrun - the actual Android automation magic happens there. Check them out!
MIT