SpawnDroid hosts an Appium 2 server with the UiAutomator2 driver per CI session, on a real Android device. Your pipeline brings its own test framework — anything that speaks W3C WebDriver — so there is no SDK to vendor and no test format to adopt.
1. Start a runner
Upload the build with a scoped API key, then start a session with the build's id. The response carries a webdriverUrl, the device serial and the app package.
RESPONSE=$(curl -sS -X POST https://app.spawndroid.io/api/v1/sessions \
-H "Authorization: Bearer $SPAWNDROID_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"artifactId\": \"$APK_ID\"}")
WD_URL=$(echo "$RESPONSE" | jq -r '.webdriverUrl')
UDID=$(echo "$RESPONSE" | jq -r '.device.serial')
PACKAGE=$(echo "$RESPONSE" | jq -r '.app.package')Provisioning is asynchronous, so poll the session until its state is ready before connecting.
2. Point your tests at it
const url = new URL(process.env.WD_URL)
const driver = await remote({
hostname: url.hostname,
path: url.pathname, // /ci/sess_<id>/<token>/wd/hub
protocol: "https",
capabilities: {
platformName: "Android",
"appium:automationName": "UiAutomator2",
"appium:udid": process.env.UDID,
"appium:appPackage": process.env.PACKAGE,
"appium:autoGrantPermissions": true,
},
})3. Report the result
curl -sS -X DELETE https://app.spawndroid.io/api/v1/sessions/$SESSION_ID \
-H "Authorization: Bearer $SPAWNDROID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"result": "passed"}' # or "failed"Teardown destroys the device and retains the final logcat and a screen recording of the run. The outcome, the logcat viewer and the recording appear in Test Logs, and webhooks forward session.ended to your endpoint.