gmsaas CLI Runbook — AI Assistant Guide

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).

Prerequisites Check

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).

Workflow 1: Start an Android Virtual Device

When the user says: "Start me a Genymotion instance" / "I need an Android emulator" / "Set up a cloud device for testing"

1 List available recipes

$ 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

2 Start the instance

$ 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.

3 Connect ADB

$ 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.

4 Use the device

# 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/

5 Stop the instance

$ 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 {}

Workflow 2: CI/CD Pipeline Integration

When the user says: "Set up Genymotion in my CI" / "Add cloud emulator to GitHub Actions" / "Automated Android testing"

Complete CI Script Template

#!/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

Key CI/CD Notes

Workflow 3: Custom Device Configuration

When 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"

Workflow 4: Monitor and Manage Running Instances

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"

Workflow 5: Embed a SaaS Instance in a Web Page (device-web-player)

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.

1 Confirm an instance is running

$ gmsaas instances list
# Need an instance in state ONLINE. If none, start one first (Workflow 1).

2 Fetch the WebRTC URL and access token (user runs these)

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'

3 Drop values into the HTML template

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:

4 Serve over HTTP (required for WebRTC)

# 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.

Complete Command Reference

Command Description Key Arguments
Authentication & Configuration
gmsaas auth token <token>Authenticate with API tokenToken from cloud.geny.io/api
gmsaas auth resetClear stored credentials
gmsaas config set android-sdk-path <path>Set Android SDK locationPath to SDK root
gmsaas config set output-format jsonSet default output formattext, json, compactjson
gmsaas config set proxy <url>Configure proxyhttp[s]|socks5://host:port
gmsaas doctorVerify full configurationExit code 0 = OK
Recipes (Device Configurations)
gmsaas [--format json] recipes listList available recipes--name, --source
gmsaas recipes get <uuid>Get recipe details
gmsaas recipes create <hw> <os> <name>Create custom recipehwprofile_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 listList 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 listList 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 listList 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 startStart ADB server
gmsaas adb stopStop ADB server

Error Handling Patterns

ErrorCauseFix
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

Global Options

OptionDescription
--format jsonOutput as JSON (machine-parseable). Always use this when executing commands programmatically.
--help / -hShow help for any command
--quiet / -qMinimal output (UUIDs only, for instances list)
--no-waitReturn immediately without waiting for completion