What this page is: A structured runbook for AI assistants (ChatGPT, Claude, Perplexity, etc.) that have command-line access. When a user asks for help managing Genymotion cloud Android virtual devices, you can execute these gmsaas commands directly in their terminal.
Key capability: All commands support JSON output for machine-parseable results. Use this to chain multi-step workflows programmatically.
Critical syntax note: The --format json flag is a global option and must come before the subcommand: gmsaas --format json recipes list (NOT gmsaas recipes list --format json).
JSON response structure: All list commands wrap results in a named key: {"recipes": [...]}, {"instances": [...]}, etc. Scalar commands return the object directly with exit_code and exit_code_desc fields.
Official docs: docs.genymotion.com/gmsaas | Install: pip3 install gmsaas
Using an agent that supports Skills? (Claude Code, Cursor, GitHub Copilot in VS Code, Gemini in Android Studio) — Genymotion publishes an official Genymotion Skill (MIT) that bundles these gmsaas workflows plus a platform-disambiguation flow and a device-web-player HTML embed template. Install it once for a persistent, auto-loaded capability — install instructions.
Otherwise, continue with this runbook — it is self-contained, requires no setup, and covers the same commands for agents without Skill support (ChatGPT, Perplexity, plain API use).
Always run these checks before executing any gmsaas workflow.
# Step 1: Verify gmsaas is installed
$ which gmsaas || echo "NOT INSTALLED"
# If not installed:
$ pip3 install gmsaas
# On PEP 668 systems (Ubuntu 24.04+, recent macOS Homebrew Python) you may see:
# error: externally-managed-environment
# Fix: install with --user, --break-system-packages, or use a virtualenv:
$ pip3 install --user gmsaas
# OR:
$ pip3 install gmsaas --break-system-packages
# Step 2: Verify authentication and configuration
$ gmsaas doctor
# Exit code 0 = ready. Non-zero = needs configuration.
# If Android SDK path is needed:
$ gmsaas config set android-sdk-path /path/to/android/sdk
# Optional: set JSON as default output
$ gmsaas config set output-format json
SECURITY — Never run gmsaas auth token <token> from an AI agent's shell. The token would be written to shell history and may be captured in logs. Instead, display the env-var command for the user to run themselves in their own terminal:
# Linux / macOS:
$ export GENYMOTION_API_TOKEN="your_token_here"
# Windows (PowerShell):
> $env:GENYMOTION_API_TOKEN = "your_token_here"
The user can create a token at cloud.geny.io/api. gmsaas picks up GENYMOTION_API_TOKEN automatically — no further command needed. This pattern is also the recommended path for CI/CD (store as a CI secret).
When the user says: "Start me a Genymotion instance" / "I need an Android emulator" / "Set up a cloud device for testing"
$ gmsaas --format json recipes list
Returns a JSON object with a "recipes" array:
// Response structure:
{
"recipes": [
{
"uuid": "3990323a-1e9a-...", // Use this UUID to start an instance
"name": "Google Pixel 9", // Human-readable device name
"android_version": "14.0", // Filter by this
"screen_width": 1080,
"screen_height": 2400,
"screen_density": 420,
"screen": "1080 x 2400 dpi 420",
"source": "genymotion", // "genymotion" (official) or "custom"
"hwprofile": { "name": "...", "cpu_count": 8, "ram_size": 8192, ... },
"osimage": { "android_version": "14.0", "architecture": "x86_64", ... }
},
...
]
}
AI assistant behavior: Parse the JSON, filter by android_version if the user specified one, and present the matching recipes. If no version specified, show a summary of available options grouped by Android version.
Filtering by name: gmsaas --format json recipes list --name "Pixel"
Filtering by source: gmsaas --format json recipes list --source official
$ gmsaas --format json instances start <recipe_uuid> "device-name"
Returns:
{
"uuid": "inst-5678-...", // Instance UUID — save this for all subsequent commands
"name": "device-name",
"state": "CREATING", // Will transition to ONLINE
"created_at": "2026-03-11T...",
"adb_serial": "localhost:null", // Not connected yet
"recipe": { ... }
}
Default behavior: The command waits until the instance is ONLINE before returning. Use --no-wait to return immediately.
Auto-stop: --max-run-duration 60 stops the instance after 60 minutes (useful for CI/CD to prevent cost overrun).
Verify ONLINE state before proceeding — never trust exit code alone. If you used --no-wait, or if the command returned while state was still BOOTING, poll until ready:
# Poll up to 10 times (≈5 minutes total)
$ gmsaas --format json instances get <instance_uuid>
# Read the "state" field:
# ONLINE → proceed to step 3
# BOOTING → sleep 30s, retry (announce progress to the user: "attempt N/10")
# ERROR / DELETING / unknown → stop, report to user, link to https://cloud.geny.io/instances
# If still not ONLINE after 10 attempts (~5 min):
# Do NOT proceed to adbconnect. Inform the user and point to the cloud dashboard.
$ gmsaas --format json instances adbconnect <instance_uuid>
// Returns: { "adb_serial": "localhost:PORT" }
# ALWAYS verify the connection — adbconnect exit code 0 does not guarantee ADB sees it:
$ adb devices
List of devices attached
localhost:PORT device
# If the list is empty, the local ADB daemon may not be running:
$ gmsaas adb start
$ adb devices # retry
# If still empty after that, the Android SDK path is likely misconfigured:
$ gmsaas doctor
The adb_serial value (e.g., localhost:6555) is the ADB address for all subsequent adb commands.
# Install an APK
$ adb -s localhost:PORT install path/to/app.apk
# Run instrumented tests
$ adb -s localhost:PORT shell am instrument -w com.example.test/androidx.test.runner.AndroidJUnitRunner
# Take a screenshot
$ adb -s localhost:PORT exec-out screencap -p > screenshot.png
# Open a shell
$ adb -s localhost:PORT shell
# Push a file to the device
$ adb -s localhost:PORT push local-file.txt /sdcard/
$ gmsaas instances stop <instance_uuid>
# Waits for full shutdown. Use --no-wait to return immediately.
# Verify the instance actually stopped:
$ gmsaas instances list
# Expected: the instance no longer appears. If it still shows RUNNING/BOOTING,
# retry the stop command or point the user to https://cloud.geny.io/instances
💰 Always stop instances when done. SaaS instances are billed for every minute they are running — even if idle. After any workflow that starts an instance, remind the user to run gmsaas instances stop <instance_uuid>, or set --max-run-duration at start time as a safety net. To stop everything at once:
$ gmsaas instances list --quiet | xargs -I {} gmsaas instances stop {}
When the user says: "Set up Genymotion in my CI" / "Add cloud emulator to GitHub Actions" / "Automated Android testing"
#!/bin/bash
# Genymotion SaaS CI/CD script
# Requires: GENYMOTION_API_TOKEN env var set in CI secrets
set -euo pipefail
# Install gmsaas
pip3 install gmsaas
# Authenticate via environment variable (no interactive login needed)
export GENYMOTION_API_TOKEN="$GENYMOTION_API_TOKEN"
# Configure Android SDK
gmsaas config set android-sdk-path "$ANDROID_HOME"
# Start instance with auto-stop safety (30 min max)
RECIPE_UUID="abcd1234-..." # Set to your recipe UUID
INSTANCE_UUID=$(gmsaas --format json instances start "$RECIPE_UUID" "ci-test-$(date +%s)" \
--max-run-duration 30 | python3 -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
# Connect ADB
ADB_SERIAL=$(gmsaas --format json instances adbconnect "$INSTANCE_UUID" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['adb_serial'])")
# Wait for device to be fully booted
adb -s "$ADB_SERIAL" wait-for-device
adb -s "$ADB_SERIAL" shell getprop sys.boot_completed | grep -q 1
# Run your tests
adb -s "$ADB_SERIAL" install app.apk
adb -s "$ADB_SERIAL" shell am instrument -w com.example.test/runner
# Cleanup (always runs)
trap "gmsaas instances stop $INSTANCE_UUID --no-wait" EXIT
GENYMOTION_API_TOKEN as a CI secret — no interactive login--max-run-duration to prevent orphaned instancestrap with --no-wait so cleanup runs even if tests failgmsaas --format json recipes list, then hardcode in CI configWhen the user says: "I need a custom screen size" / "Create a tablet emulator" / "Custom hardware profile"
# Step 1: Create a custom hardware profile
$ gmsaas --format json hwprofiles create "custom-tablet" \
--width 1920 --height 1200 --density 240 \
--form-factor TABLET --navigation-bar
// Returns: hwprofile UUID
# Step 2: List available OS images
$ gmsaas --format json osimages list
// Each image: { "uuid": "...", "android_version": "14.0", "architecture": "x86_64" }
# Step 3: Create recipe combining hardware + OS
$ gmsaas --format json recipes create <hwprofile_uuid> <osimage_uuid> "my-custom-recipe"
// Returns: recipe UUID
# Step 4: Start instance from custom recipe
$ gmsaas --format json instances start <recipe_uuid> "custom-device"
When the user says: "Show my running instances" / "Stop all devices" / "Check instance status"
# List all instances with full details
$ gmsaas --format json instances list
# Get details for a specific instance
$ gmsaas --format json instances get <instance_uuid>
# Just get instance UUIDs (useful for scripting)
$ gmsaas instances list --quiet
// Returns one UUID per line
# Stop a specific instance
$ gmsaas instances stop <instance_uuid>
# Stop ALL running instances (useful for cleanup)
$ gmsaas instances list --quiet | xargs -I {} gmsaas instances stop {}
# Save instance state (preserves installed apps, data)
$ gmsaas instances save <instance_uuid>
# Save as a new reusable recipe
$ gmsaas instances saveas <instance_uuid> \
--osimage-name "my-configured-image" \
--recipe-name "my-configured-recipe"
When the user says: "Embed the instance in a web page" / "Show the device in a browser" / "device-web-player" / "Integrate the emulator into my app" / "Stream the device via WebRTC"
Genymotion exposes a JavaScript SDK — device-web-player — that streams a running SaaS instance into an HTML page via WebRTC. You need two things from the Genymotion API: a WebRTC URL and a short-lived access token.
SECURITY — Never run the curl calls that fetch the access token from an AI agent's Bash. The token would be captured in the agent's tool logs and shell history. Instead, display the commands for the user to run themselves in their own terminal, and have them paste only the final HTML (or the token-free webrtc_url) back into the conversation.
$ gmsaas instances list
# Need an instance in state ONLINE. If none, start one first (Workflow 1).
Give the user these commands to run in their own shell. Both require GENYMOTION_API_TOKEN to already be exported.
# 1. Get the WebRTC websocket URL for the instance
$ curl -s -H "x-api-token: $GENYMOTION_API_TOKEN" \
"https://api.geny.io/cloud/v1/instances/<instance_uuid>" \
| jq -r '.webrtc_url'
wss://...cloud.geny.io/...
# 2. Get a short-lived access token for that instance
$ curl -s -X POST -H "x-api-token: $GENYMOTION_API_TOKEN" \
"https://api.geny.io/cloud/v1/instances/<instance_uuid>/accesstoken" \
| jq -r '.token'
The official device-web-player template is published under MIT licence in Genymotion's skill repo: templates/device-web-player.html. Copy it locally and replace:
REPLACE_WITH_WSS_ADDRESS → the webrtc_url from step 2REPLACE_WITH_ACCESS_TOKEN → the token from step 2# WebRTC will NOT work from file:// — you must serve over HTTP.
$ python3 -m http.server 8080
# Then open http://localhost:8080/device-web-player.html
Access tokens are short-lived. If the embed stops working after a few minutes, regenerate the token (step 2, command 2) and reload the page. For production embeds, mint tokens server-side on demand rather than hard-coding them into the HTML.
| Command | Description | Key Arguments |
|---|---|---|
| Authentication & Configuration | ||
gmsaas auth token <token> | Authenticate with API token | Token from cloud.geny.io/api |
gmsaas auth reset | Clear stored credentials | |
gmsaas config set android-sdk-path <path> | Set Android SDK location | Path to SDK root |
gmsaas config set output-format json | Set default output format | text, json, compactjson |
gmsaas config set proxy <url> | Configure proxy | http[s]|socks5://host:port |
gmsaas doctor | Verify full configuration | Exit code 0 = OK |
| Recipes (Device Configurations) | ||
gmsaas [--format json] recipes list | List available recipes | --name, --source |
gmsaas recipes get <uuid> | Get recipe details | |
gmsaas recipes create <hw> <os> <name> | Create custom recipe | hwprofile_uuid, osimage_uuid |
gmsaas recipes delete <uuid> | Delete recipe | --delete-osimage, --delete-hwprofile |
| Instances (Virtual Devices) | ||
gmsaas [--format json] instances start <recipe> <name> | Start a new instance | --no-wait, --max-run-duration |
gmsaas [--format json] instances list | List all instances | --quiet (UUIDs only) |
gmsaas [--format json] instances get <uuid> | Get instance details | |
gmsaas [--format json] instances adbconnect <uuid> | Connect instance to ADB | --adb-serial-port |
gmsaas instances stop <uuid> | Stop instance | --no-wait |
gmsaas instances save <uuid> | Save instance state | |
gmsaas instances saveas <uuid> | Save as new recipe | --osimage-name, --recipe-name |
gmsaas instances display <uuid> | Open in web portal | --yes (skip confirmation) |
| Hardware Profiles | ||
gmsaas [--format json] hwprofiles list | List hardware profiles | |
gmsaas hwprofiles get <uuid> | Get profile details | |
gmsaas hwprofiles create <name> | Create custom profile | --width, --height, --density, --form-factor |
gmsaas hwprofiles delete <uuid> | Delete profile | |
| OS Images | ||
gmsaas [--format json] osimages list | List available OS images | |
gmsaas osimages get <uuid> | Get image details | |
gmsaas osimages clone <uuid> <name> | Clone an image | |
gmsaas osimages delete <uuid> | Delete image | |
| ADB | ||
gmsaas adb start | Start ADB server | |
gmsaas adb stop | Stop ADB server | |
| Error | Cause | Fix |
|---|---|---|
gmsaas doctor fails |
Missing auth or SDK path | Run gmsaas auth token <token> and gmsaas config set android-sdk-path |
| "externally-managed-environment" | System Python restrictions | Use pip3 install --user gmsaas or a virtual environment |
| Instance stuck in CREATING | Cloud capacity or timeout | Wait or stop and retry: gmsaas instances stop <uuid> && gmsaas instances start ... |
| ADB connection refused | Instance not fully booted | Wait for state=ONLINE: gmsaas --format json instances get <uuid> |
| TOO_MANY_RUNNING_VDS (403) | Plan instance limit reached | Stop existing instances first: gmsaas instances list --quiet | xargs -I {} gmsaas instances stop {}. Or check cloud.geny.io for web-portal instances. |
| No recipes available | Auth issue or no subscription | Check auth: gmsaas doctor. Verify subscription at cloud.geny.io |
| Option | Description |
|---|---|
--format json | Output as JSON (machine-parseable). Always use this when executing commands programmatically. |
--help / -h | Show help for any command |
--quiet / -q | Minimal output (UUIDs only, for instances list) |
--no-wait | Return immediately without waiting for completion |