DroidDesk

DroidDesk · API & MCP

DroidDesk API & MCP

Operate the Android phones you rent on DroidDesk from your own code or AI assistant.

REST API
https://api.mobirent.io/v1
MCP (AI assistants)
https://api.mobirent.io/mcp
OpenAPI
https://api.mobirent.io/v1/openapi.json

Use a DroidDesk API key to connect your assistant or application. The key controls which rented phones and actions are available. Phone access depends on the active rental and the permissions chosen when creating the key.

For AI assistants, connect the MCP endpoint. For direct integration, use the REST endpoints below. Read the current screen before acting and inspect the operation status after each action.

Documentation for people and AI agents

Choose a guide to get started or a machine-readable reference for your integration.

Have an old link? The previous guide address remains available and contains the same English guide.

Quick start

  1. Sign in to your DroidDesk account. Rent a phone before running device actions.
  2. Open the cabinet → AI & Tasks → AI / MCP → create a key. Copy the key: it is shown once.
  3. Connect your AI assistant (below), or call the API from your code.
Your first request
export DROIDDESK_API_KEY="dd_live_…"
curl -H "Authorization: Bearer $DROIDDESK_API_KEY" https://api.mobirent.io/v1/phones

Connect your AI

DroidDesk uses remote MCP over Streamable HTTP. Choose a client that supports a bearer authorization header. Tasks using screenshots also require a model and client that can process images. Tool access depends on account features, key permissions and device availability.

Complete MCP guide for people and AI agents (Markdown) → · Tool reference and schemas →

Claude Code (terminal)
claude mcp add --transport http droiddesk https://api.mobirent.io/mcp \
  --header "Authorization: Bearer dd_live_…"
Codex — ~/.codex/config.toml (CLI, IDE extension and desktop app), then restart Codex
[mcp_servers.droiddesk]
url = "https://api.mobirent.io/mcp"
bearer_token_env_var = "DROIDDESK_API_KEY"
Cursor — ~/.cursor/mcp.json
{
  "mcpServers": {
    "droiddesk": {
      "url": "https://api.mobirent.io/mcp",
      "headers": {
        "Authorization": "Bearer dd_live_…"
      }
    }
  }
}

ChatGPT custom MCP connections use OAuth rather than a manually supplied DroidDesk API key. Direct ChatGPT integration is not yet verified. OpenAI Responses API supports remote MCP, but its compatibility with this server has not been verified here.

Keep your key private and revoke it if exposed. See the complete MCP guide for connection examples, device access, uploads, scenarios, SMS and troubleshooting.

MCP capabilities

Ask your assistant to list available tools after connecting. Capabilities depend on account settings and key permissions. Paid rentals require separate permission and a per-order limit. SMS requires your own connected provider account. CAPTCHA support is limited to supported challenge types.

  • Devices and rental: list_phones, phone_status, phone_info, phone_capabilities, list_available_phones, rent_phone, get_rental_order
  • Screen and actions: get_screen, screenshot, list_apps, tap, type_text, swipe, press_key, launch_app, open_url, wait_for, get_operation
  • Templates and scenarios: list_templates, validate_template, save_template, run_template, get_template_run, stop_template_run, list_scenarios, run_scenario, get_scenario_run, continue_scenario_run, stop_scenario_run, run_automation_mode
  • Files, SMS and CAPTCHA: prepare_file_upload, file_transfer_status, sms_offers, sms_order, sms_status, sms_cancel, solve_text_captcha, get_text_captcha, solve_click_captcha, get_image_captcha
  • Getting started: get_started

Files are transferred through a prepared upload and tracked to completion. If a transfer, paid order, CAPTCHA task or run returns an unknown result, inspect its status before repeating it.

Driving a phone

  1. Read the screen. GET /phones/2018/screen returns numbered elements and a snapshot_id.
  2. Act on an element with its number and that snapshot_id, e.g. tap element 2. If the screen changed meanwhile, you get stale_snapshot and nothing is tapped.
  3. Use the new screen that comes back with every finished action, and repeat.

For requests that support an Idempotency-Key, use a new key for a new action and reuse it only for the identical request. An unknown outcome may have happened: inspect the operation and phone before deciding whether to repeat it.

Check the returned operation status and use a fresh screen snapshot when making the next decision.

Account

get/me

The key and its account

Request
curl "https://api.mobirent.io/v1/me" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
Response 200
{
  "user_id": 4356,
  "key": {
    "id": "key_Ab12Cd34Ef56Gh78Ij90",
    "name": "Claude Code",
    "prefix": "dd_live_AbCd",
    "scope": "control",
    "phones": "all",
    "created_at": "2026-10-28T12:00:00Z",
    "expires_at": null,
    "last_used_at": "2026-10-28T15:20:00Z",
    "grace_until": null
  }
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 429rate_limited

Phones

get/phones

Phones this key can use

Your live rentals and phones shared with you, limited to the phones the key is bound to.

Request
curl "https://api.mobirent.io/v1/phones" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
Response 200
{
  "phones": [
    {
      "phone": "2018",
      "name": "Galaxy S22 · Las Vegas, US · T-Mobile 5G",
      "model": "Galaxy S22",
      "country": "US",
      "city": "Las Vegas",
      "carrier": "T-Mobile",
      "network": "mobile",
      "rental_ends_at": "2026-11-23T20:57:56Z",
      "shared": false
    }
  ]
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 429rate_limited
get/phones/{phone}/status

Online, ready to control, rental time left

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
Request
curl "https://api.mobirent.io/v1/phones/2018/status" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
Response 200
{
  "phone": "2018",
  "online": true,
  "control_ready": true,
  "preparing": false,
  "controller": "none",
  "rental_ends_at": "2026-11-23T20:57:56Z",
  "seconds_left": 2280000
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404phone_not_found
  • 409rental_ended
  • 429rate_limited
  • 503access_check_unavailable
get/phones/{phone}/info

Stored device, network and approximate location details

Returns details for an authorized active rental. IP and location are stored observations, may be stale, and are not GPS.

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
Request
curl "https://api.mobirent.io/v1/phones/2018/info" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404phone_not_found
  • 409rental_ended
  • 429rate_limited
  • 503access_check_unavailable
get/phones/{phone}/capabilities

What the phone supports

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
Request
curl "https://api.mobirent.io/v1/phones/2018/capabilities" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404phone_not_found
  • 409rental_ended
  • 429rate_limited
  • 503access_check_unavailable
get/phones/{phone}/apps

Apps you can open with launch_app

Apps your plan shows on this phone. System settings, installers, calls and SMS are never listed.

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
Request
curl "https://api.mobirent.io/v1/phones/2018/apps" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
Response 200
{
  "apps": [
    {
      "package": "com.android.chrome",
      "label": "Chrome"
    },
    {
      "package": "com.instagram.android",
      "label": "Instagram"
    }
  ]
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404phone_not_found
  • 409rental_ended
  • 429rate_limited
  • 503access_check_unavailable; apps_unavailable

Screen

get/phones/{phone}/screen

Current screen as numbered elements

Elements are the visible texts, buttons, fields and lists. Coordinates are in the screen frame. Up to 300 elements; truncated is true when there were more. Limit: 4 per second per phone.

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
Request
curl "https://api.mobirent.io/v1/phones/2018/screen" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
Response 200
{
  "snapshot_id": "snp_Q2xk8vT1aB3c",
  "taken_at": "2026-10-28T15:22:31Z",
  "frame": {
    "width": 720,
    "height": 1600
  },
  "app": {
    "package": "com.android.chrome"
  },
  "keyboard": false,
  "truncated": false,
  "elements": [
    {
      "n": 1,
      "role": "edit",
      "text": "",
      "desc": "Search",
      "bounds": [
        40,
        300,
        680,
        380
      ],
      "click": true,
      "edit": true
    },
    {
      "n": 2,
      "role": "button",
      "text": "Google Search",
      "bounds": [
        120,
        460,
        360,
        540
      ],
      "click": true
    }
  ]
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404phone_not_found
  • 409rental_ended; phone_offline
  • 429rate_limited
  • 502bridge_unavailable
  • 503access_check_unavailable
get/phones/{phone}/screenshot

JPEG screenshot

Prefer screen (cheaper, exact). Use screenshots for games, canvas apps or when elements are missing. Limit: 20 per minute per phone.

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
maxqueryLongest side in px. Default 1024.
qualityqueryJPEG quality. Default 60.
Request
curl "https://api.mobirent.io/v1/phones/2018/screenshot" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
Response 200
{
  "mime": "image/jpeg",
  "data": "/9j/4AAQSkZJRgABAQ…",
  "width": 461,
  "height": 1024
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404phone_not_found
  • 409rental_ended
  • 429rate_limited
  • 502screenshot_failed
  • 503access_check_unavailable

Actions

post/phones/{phone}/actions

Tap, type, swipe, press a key, open an app or a link

Needs a control key. Send a new Idempotency-Key for every new action; repeating a request with the same key returns the same operation and never acts twice. Actions on one phone run one at a time, in order. The response waits up to wait seconds: 200 when the operation finished (with the new screen), 202 while it is still running — then poll GET /operations/{id}. Limit: 5 actions per second per phone.

ParameterInDescription
phone*pathPhone number from GET /phones, e.g. 2018.
Idempotency-Key*header1–64 printable characters, unique per action.
waitquerySeconds to wait for the result. Default 10.
Request
curl -X POST "https://api.mobirent.io/v1/phones/2018/actions" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"type":"tap","element":2,"snapshot_id":"snp_Q2xk8vT1aB3c"}'

Body examples

Tap element 2 of a screen

{"type":"tap","element":2,"snapshot_id":"snp_Q2xk8vT1aB3c"}

Type into element 1 and press Enter

{"type":"type_text","element":1,"snapshot_id":"snp_Q2xk8vT1aB3c","text":"weather in Paris","submit":true}

Scroll down

{"type":"swipe","direction":"down"}

Open a link

{"type":"open_url","url":"https://example.com"}

Open an app

{"type":"launch_app","package":"com.android.chrome"}

Go home

{"type":"press_key","key":"home"}

Wait until a text appears

{"type":"wait_for","text":"Welcome","timeout_s":15}
Response 200
{
  "operation": {
    "id": "op_7fKq2LxPz9",
    "type": "tap",
    "phone": "2018",
    "status": "succeeded",
    "created_at": "2026-10-28T15:22:40Z",
    "started_at": "2026-10-28T15:22:40Z",
    "finished_at": "2026-10-28T15:22:41Z",
    "screen": {
      "snapshot_id": "snp_Q2xk8vT1aB3c",
      "taken_at": "2026-10-28T15:22:31Z",
      "frame": {
        "width": 720,
        "height": 1600
      },
      "app": {
        "package": "com.android.chrome"
      },
      "keyboard": false,
      "truncated": false,
      "elements": [
        {
          "n": 1,
          "role": "edit",
          "text": "",
          "desc": "Search",
          "bounds": [
            40,
            300,
            680,
            380
          ],
          "click": true,
          "edit": true
        },
        {
          "n": 2,
          "role": "button",
          "text": "Google Search",
          "bounds": [
            120,
            460,
            360,
            540
          ],
          "click": true
        }
      ]
    }
  }
}
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen; insufficient_scope, app_hidden, url_scheme_not_allowed
  • 404phone_not_found
  • 409rental_ended; phone_offline
  • 422invalid_request, idempotency_mismatch
  • 429rate_limited
  • 503access_check_unavailable
get/operations/{id}

Status of an action

The new screen is included for 10 minutes after the operation finished.

ParameterInDescription
id*path
Request
curl "https://api.mobirent.io/v1/operations/op_7fKq2LxPz9" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 401invalid_key, key_expired, key_revoked
  • 403feature_disabled, balance_frozen
  • 404operation_not_found
  • 429rate_limited

Templates

get/templates

List my templates

Request
curl "https://api.mobirent.io/v1/templates" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters
post/templates

Create or version a private template

Request
curl -X POST "https://api.mobirent.io/v1/templates" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters
post/templates/validate

Validate schema without operating a phone

Request
curl -X POST "https://api.mobirent.io/v1/templates/validate" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters
get/template-runs

List my recent template runs

Request
curl "https://api.mobirent.io/v1/template-runs" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters
post/template-runs

Start without AI; values are not retained in history

ParameterInDescription
Idempotency-Key*header
Request
curl -X POST "https://api.mobirent.io/v1/template-runs" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters
get/template-runs/{id}

Read run and step events

ParameterInDescription
id*path
Request
curl "https://api.mobirent.io/v1/template-runs/op_7fKq2LxPz9" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters
post/template-runs/{id}/stop

Stop before the next action; an in-flight action may finish

ParameterInDescription
id*path
Request
curl -X POST "https://api.mobirent.io/v1/template-runs/op_7fKq2LxPz9/stop" \
  -H "Authorization: Bearer $DROIDDESK_API_KEY"
  • 403Not enabled or control access required
  • 409Version conflict or device unavailable
  • 422Invalid schema or parameters

Errors

Errors share one shape: {"error": {"code": "phone_not_found", "message": "…", "retryable": false}}. Check code, not the message.

HTTPcodeMeaning
401invalid_key, key_expired, key_revokedThe key is wrong, expired, revoked, or was replaced and its 24-hour grace period is over.
403feature_disabledAI access is not enabled for the account yet.
403insufficient_scopeA view-only key tried to act.
403balance_frozenThe account balance is frozen; contact support.
403app_hidden, url_scheme_not_allowedThe app is not available on your plan; only http(s) links can be opened.
404phone_not_foundNot one of your rentals for this key (the API does not reveal other phones).
409rental_ended, phone_offlineThe rental is over, or the phone is not connected right now.
422invalid_request, idempotency_mismatchBad parameters, or an Idempotency-Key reused for a different action.
429rate_limitedToo many requests; wait for the Retry-After seconds.
503access_check_unavailable, bridge_unavailableTemporary; retry shortly.

Limits

Limits depend on the account and operation. If a request is rate limited, follow the response and retry guidance. Check available capabilities for the selected phone.

OpenAPI file

The machine-readable description (OpenAPI 3.1) for Postman, code generators and AI tools: https://api.mobirent.io/v1/openapi.json

Questions: [email protected]