Devices & Inspector
The devices and inspector API powers Ghost’s mobile testing capabilities. These endpoints let you discover connected iOS simulators and Android emulators, capture screenshots, inspect the UI element tree, generate test automation selectors, correlate UI interactions with network traffic, and produce AI-enhanced bug reports — all from Ghost’s desktop interface.
Think of the inspector as a remote control and X-ray machine for your mobile device. You can see what’s on the screen (screenshots), look inside the screen to see the individual UI elements (hierarchy), figure out how to target specific elements in test code (selectors), and connect what the user is doing on screen to what the app is sending over the network (traffic correlation).
Device Lifecycle
Section titled “Device Lifecycle”List Devices
Section titled “List Devices”GET /api/v1/inspector/devicesReturns all discovered devices — iOS simulators, Android emulators, or physical devices connected via USB or Wi-Fi.
Response:
[ { "id": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", "name": "iPhone 16 Pro", "platform": "ios", "os_version": "17.2", "connection_type": "simulator", "state": "connected", "client_ip": "127.0.0.1", "model": "iPhone16,1", "running_app": "com.example.MyApp", "screen_width": 1179, "screen_height": 2556, "wda_width": 393, "wda_height": 852, "inspector_port": 8100, "session_id": "01HWXYZ...", "last_seen": "2024-01-15T10:30:00Z", "error": "" }]| Field | Type | Description |
|---|---|---|
id | string | Device identifier — UDID for iOS simulators, ADB serial for Android emulators |
name | string | Human-readable device name (e.g., “iPhone 16 Pro”, “Pixel 7 API 34”) |
platform | string | "ios" or "android" |
os_version | string | Operating system version |
connection_type | string | How the device is connected: "simulator", "emulator", "usb", or "wifi" |
state | string | Current state: "discovered" (found but not connected), "connecting" (connection in progress), "connected" (ready for inspection), "error" (connection failed), "disconnected" (was connected, now stopped) |
client_ip | string | Device’s IP address (or localhost for simulators/emulators) |
model | string | Hardware model identifier |
running_app | string | Bundle ID of the foreground app |
screen_width / screen_height | integer | Screen resolution in physical pixels |
wda_width / wda_height | integer | iOS only — WDA (WebDriverAgent) reports coordinates in logical points, which differ from physical pixels due to screen scale (e.g., 3x on retina). Ghost uses both coordinate spaces for accurate tap positioning |
inspector_port | integer | Port of the running inspection agent (WDA for iOS, atx-agent for Android) |
session_id | string | WDA session ID (iOS only) |
last_seen | timestamp | When the device was last detected by discovery |
error | string | Error message if the device is in the "error" state |
Returns 503 if the device manager is not initialized.
Get Device
Section titled “Get Device”GET /api/v1/inspector/devices/{id}Returns a single device by its ID. Same response format as the list endpoint.
Returns 404 if the device is not found.
Connect Device
Section titled “Connect Device”POST /api/v1/inspector/devices/{id}/connectInitiates the connection process for a device. This returns immediately with a 202 Accepted response — the actual connection happens in a background goroutine because it can take a while (especially on iOS where WebDriverAgent may need to be built for the first time).
Connection timeout: 120 seconds. WDA build on iOS can take 30–60 seconds on the first run (subsequent connects are much faster because the build is cached).
What happens during connection:
- Android: Allocates a port (starting from 17912), sets up ADB port forwarding, starts the atx-agent service, and polls until it responds (10 attempts at 500ms intervals)
- iOS Simulator: Scans ports 8100–8109 for a running WDA instance. If none found, clones the WDA project, builds it, launches it, and waits up to 30 seconds for it to start
Connection progress is broadcast via device.updated and device.connected WebSocket events.
Response: 202 Accepted with the current device info snapshot.
Disconnect Device
Section titled “Disconnect Device”POST /api/v1/inspector/devices/{id}/disconnectStops all inspector services for the device — screenshot capture, touch monitoring, and platform-specific cleanup.
Response: {"ok": true}
Broadcasts a device.disconnected WebSocket event.
Screenshots
Section titled “Screenshots”Ghost captures screenshots from connected devices at approximately 1 frame per second and stores them in a circular ring buffer — like a security camera that keeps the last 30 seconds of footage and overwrites the oldest frame when it runs out of space.
Capture Screenshot
Section titled “Capture Screenshot”GET /api/v1/inspector/devices/{id}/screenshot?offset=0Returns a screenshot from the ring buffer as raw binary image data.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
offset | integer | 0 | Which frame to return: 0 = most recent, 1 = one frame before that, up to 29 = oldest available frame |
Ring buffer details:
- Size: 30 frames
- Capture rate: ~1 frame per second
- Memory: ~100 KB per frame × 30 frames = ~3 MB per connected device
- Format: JPEG (Android via atx-agent) or PNG (iOS via simctl)
Response headers:
Content-Type: auto-detected from the image data (image/jpegorimage/png)Content-Length: exact byte countCache-Control: no-store(prevent browser caching — each request should get the latest frame)
Returns 404 if no screenshot is available or the offset exceeds the number of stored frames.
Screenshot Metadata
Section titled “Screenshot Metadata”GET /api/v1/inspector/devices/{id}/screenshot/metaReturns metadata about the ring buffer — used by the frontend to power the scrub slider (a timeline control that lets you scroll through recent screenshots).
Response:
{ "count": 30, "oldest_ts": 1705312800000, "newest_ts": 1705312830000}| Field | Type | Description |
|---|---|---|
count | integer | Number of frames currently stored (0 to 30) |
oldest_ts | integer | Unix timestamp in milliseconds of the oldest frame |
newest_ts | integer | Unix timestamp in milliseconds of the newest frame |
Returns all zeros if the buffer is empty.
Element Hierarchy
Section titled “Element Hierarchy”The element hierarchy is a tree representation of every UI element visible on the screen — buttons, text fields, images, containers, and their nesting relationships. It’s the same data you’d see in Android Studio’s Layout Inspector or Xcode’s View Hierarchy Debugger.
Get Full Hierarchy
Section titled “Get Full Hierarchy”GET /api/v1/inspector/devices/{id}/hierarchyReturns the complete UI element tree. Android parses the hierarchy from atx-agent’s XML dump. iOS parses from WDA’s source endpoint (depth 15).
Response: A recursive Element tree:
{ "id": "node_0", "class_name": "android.widget.FrameLayout", "text": "", "resource_id": "", "accessibility_id": "", "content_desc": "", "bounds": {"x": 0, "y": 0, "width": 1080, "height": 2400}, "visible": true, "clickable": false, "enabled": true, "scrollable": false, "depth": 0, "index": 0, "platform": "android", "element_count": 156, "children": [ { "id": "node_1", "class_name": "android.widget.Button", "text": "Submit", "resource_id": "com.app:id/submit_button", "accessibility_id": "Submit", "content_desc": "Submit form", "bounds": {"x": 100, "y": 500, "width": 200, "height": 48}, "visible": true, "clickable": true, "enabled": true, "depth": 1, "index": 0, "platform": "android", "children": [] } ]}Each Element has these fields:
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier within this hierarchy snapshot |
class_name | string | The UI component type (e.g., android.widget.Button, XCUIElementTypeButton) |
text | string | Visible text content |
resource_id | string | Android resource ID (e.g., com.app:id/button) |
accessibility_id | string | Accessibility identifier (used by screen readers and test automation) |
content_desc | string | Android content description |
label | string | iOS label property |
name | string | iOS name property |
value | string | iOS value property (e.g., text field content) |
bounds | object | Screen position and size: {x, y, width, height} |
attributes | object | Additional platform-specific attributes as key-value pairs |
children | array | Child elements (the nesting that makes this a tree) |
depth | integer | How deeply nested this element is (0 = root) |
index | integer | Position among siblings |
platform | string | "android" or "ios" |
visible | boolean | Whether the element is visible on screen |
clickable | boolean | Whether tapping this element does something |
enabled | boolean | Whether the element accepts interaction |
focused | boolean | Whether this element currently has focus |
scrollable | boolean | Whether this element can be scrolled |
selected | boolean | Whether this element is in a selected state |
checked | boolean | Whether this checkbox/toggle is checked |
traits | string | iOS accessibility traits |
package_name | string | Android package name |
element_count | integer | Total number of descendants (only on the root element) |
Element at Point
Section titled “Element at Point”GET /api/v1/inspector/devices/{id}/element?x=150&y=300Finds the deepest (most specific) element at the given screen coordinates. This is what powers the “click on the screenshot to select an element” feature — you click a point on the screenshot, and Ghost figures out which element is at that exact spot by traversing the hierarchy tree.
Query parameters:
x(required) — X coordinate in screen pixelsy(required) — Y coordinate in screen pixels
Response: A single Element object (same structure as above, but just one element, not the full tree).
Returns 400 if x or y is missing/invalid, 404 if no element is found at that point.
Generate Selectors
Section titled “Generate Selectors”GET /api/v1/inspector/devices/{id}/selectors/{nodeId}Generates test automation selectors for a specific element — these are the “addresses” you’d use in test code to find this element. Ghost generates multiple selectors using different strategies, ranked by reliability.
Path parameters:
{id}— device ID{nodeId}— element ID from the hierarchy tree
Response:
{ "selectors": [ { "strategy": "accessibility_id", "value": "Submit", "reliability": "excellent", "formats": { "raw": "Submit", "appium_java": "driver.findElement(AppiumBy.accessibilityId(\"Submit\"))", "appium_python": "driver.find_element(AppiumBy.ACCESSIBILITY_ID, \"Submit\")", "maestro": "- tapOn:\n id: \"Submit\"" } }, { "strategy": "resource_id", "value": "com.app:id/submit_button", "reliability": "good", "formats": { "raw": "com.app:id/submit_button", "appium_java": "driver.findElement(AppiumBy.id(\"com.app:id/submit_button\"))", "appium_python": "driver.find_element(AppiumBy.ID, \"com.app:id/submit_button\")", "espresso": "onView(withId(R.id.submit_button))" } } ]}Selector strategies by platform:
| Platform | Strategies (priority order) | Description |
|---|---|---|
| Android | accessibility_id, resource_id, content_desc, ui_selector, xpath | accessibility_id and resource_id are most reliable; xpath is a last resort |
| iOS | accessibility_id, predicate_string, class_chain, xpath | accessibility_id is best; predicate_string and class_chain are iOS-specific powerful alternatives |
Reliability levels:
| Level | Meaning |
|---|---|
"excellent" | Highly stable — unlikely to break across app updates (e.g., accessibility IDs set by developers) |
"good" | Reliable in most cases (e.g., resource IDs, content descriptions) |
"ok" | Works but may be fragile (e.g., complex compound selectors) |
"fragile" | May break easily (e.g., absolute XPath that depends on exact tree structure) |
Code format keys:
| Key | Framework |
|---|---|
raw | The raw selector value without any framework |
appium_java | Appium Java client code |
appium_python | Appium Python client code |
maestro | Maestro YAML format |
espresso | Android Espresso (Android only) |
xcuitest | XCUITest (iOS only) |
Traffic Correlation
Section titled “Traffic Correlation”Correlated Flows
Section titled “Correlated Flows”GET /api/v1/inspector/devices/{id}/correlation?ts=1705312815000&window=5000Finds HTTP flows that happened around the same time as a UI event — this helps you see what network requests an element tap or screen transition triggered.
Query parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
ts | integer (required) | — | — | Reference timestamp in Unix milliseconds. Flows near this time are returned |
window | integer | 3,000 | 30,000 | How many milliseconds before and after ts to search (so window=5000 looks ±5 seconds) |
session_id | string | — | — | Optional Ghost session ID to scope the query. If omitted, searches the active session |
Flow limit: Up to 500 flows are queried from the database for the time window.
Response:
[ { "flow_id": "01HWXYZ...", "method": "POST", "host": "api.example.com", "path": "/api/v1/checkout", "status_code": 200, "duration_ms": 245.5, "time_delta_ms": -150.0, "relevance": "primary", "size": 4567, "started_at": "2024-01-15T10:30:14.850000000Z", "request_content_type": "application/json", "request_body": "{\"cart_id\": \"abc123\"}", "response_content_type": "application/json", "response_body": "{\"order_id\": \"xyz789\", \"status\": \"confirmed\"}" }]| Field | Type | Description |
|---|---|---|
flow_id | string | Flow identifier — click to jump to the flow in Ghost’s traffic list |
time_delta_ms | number | How many milliseconds before (negative) or after (positive) the reference timestamp this flow started. Helps you see the sequence of events |
relevance | string | "primary" (likely related to the user action) or "noise" (probably background SDK traffic like analytics or push notifications) |
sdk_category | string | If the flow is noise, which SDK category it belongs to (e.g., “Firebase”, “Sentry”, “Analytics”) |
request_body / response_body | string | First 2,048 bytes of the body as a preview. Binary content shows a placeholder like [binary image/png, 12345 bytes] |
Results are sorted by absolute time proximity — flows closest to the reference timestamp appear first.
Interactions
Section titled “Interactions”Get Device Interactions
Section titled “Get Device Interactions”GET /api/v1/inspector/devices/{id}/interactions?since=300&limit=50Returns recent touch and input events captured from the device — taps, scrolls, text input, screen transitions.
Query parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
since | integer | 300 | 3,600 | How many seconds of history to return (300 = last 5 minutes) |
limit | integer | 50 | 200 | Maximum number of events to return |
Response:
[ { "timestamp": "2024-01-15T10:30:14Z", "action": "tap", "x": 540, "y": 1200, "element_class": "android.widget.Button", "element_text": "Submit", "element_id": "com.app:id/submit_button", "detail": "", "confidence": "high" }]| Field | Type | Description |
|---|---|---|
action | string | Type of interaction: "tap", "long_press", "scroll", "text_change", "text_input", "screen_change", "value_change", "state_change", "key" |
confidence | string | "high" (from Android getevent kernel stream — direct hardware input) or "medium" (from iOS hierarchy diffing — inferred from UI changes) |
The internal interaction log holds a maximum of 100 events — older events are dropped as new ones arrive.
Get Interaction Context
Section titled “Get Interaction Context”GET /api/v1/inspector/devices/{id}/interaction-context?since=300&limit=20&session_id=01HWXYZ...Returns a richer interaction sequence that includes selectors for each element and correlated network flows — designed specifically for generating test automation code.
Query parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
since | integer | 300 | 3,600 | Seconds of history |
limit | integer | 20 | 100 | Maximum interaction entries |
session_id | string | (active) | — | Ghost session for flow correlation. Falls back to the currently active session |
Flow limit: Up to 500 flows are queried for correlation.
Response:
{ "device": { "platform": "android", "os_version": "14", "app": "com.example.app", "resolution": "1080x2400" }, "interactions": [ { "timestamp": 1705312814000, "action": "tap", "element_class": "android.widget.Button", "element_text": "Submit", "element_id": "com.app:id/submit_button", "x": 540, "y": 1200, "selectors": [ { "strategy": "accessibility_id", "value": "Submit", "reliability": "excellent", "formats": {"appium_java": "..."} } ], "triggered_flows": [ { "method": "POST", "path": "/api/v1/checkout", "status_code": 200, "duration_ms": 245.5, "flow_id": "01HWXYZ..." } ] } ]}Noise flows (SDK background traffic) are excluded from triggered_flows — only relevant network requests are shown.
Device Actions
Section titled “Device Actions”POST /api/v1/inspector/devices/{id}/tapSends a tap event to the device at specific screen coordinates — like physically touching the screen at that point.
Request body (1,024-byte limit):
{ "x": 540, "y": 1200}Both x and y are required (validated with pointer-based nil checks — sending 0 is valid, but omitting the field is an error).
Response: {"ok": true}
Input Text
Section titled “Input Text”POST /api/v1/inspector/devices/{id}/inputTypes text or sends a key press to the device. You must provide exactly one of text or key — not both, not neither.
Request body (4,096-byte limit):
{ "text": "hello@example.com"}Or:
{ "key": "enter"}Valid key values: "enter", "backspace", "tab"
Response: {"ok": true}
WebViews
Section titled “WebViews”List WebViews
Section titled “List WebViews”GET /api/v1/inspector/devices/{id}/webviewsDetects WebView elements in the UI hierarchy and (on Android) discovers debuggable WebView sockets through /proc/net/unix.
Response:
{ "webviews": [ { "element_id": "node_42", "class_name": "android.webkit.WebView", "package": "com.example.app", "inspectable": true } ], "sockets": [ { "pid": 12345, "socket_name": "webview_devtools_remote_12345" } ]}Detected WebView classes:
- Android:
android.webkit.WebView,android.webkit.WebViewClient,com.tencent.smtt.sdk.WebView(Tencent X5),com.uc.webview.export.WebView(UC Browser engine) - iOS:
XCUIElementTypeWebView, plus any class containing “WebView” or “SafariViewController”
The sockets field is Android-only and shows debuggable WebView DevTools sockets.
Bug Reports
Section titled “Bug Reports”Generate Bug Report
Section titled “Generate Bug Report”POST /api/v1/inspector/devices/{id}/bug-reportGenerates a comprehensive bug report by combining device context, screenshots, element information, correlated network traffic, interaction history, and optional AI enhancement.
Request body (64 KB limit):
{ "node_id": "node_42", "flow_ts": 1705312815000, "window_ms": 300000, "offset": 0, "session_id": "01HWXYZ..."}| Field | Required | Default | Description |
|---|---|---|---|
node_id | No | — | Element ID for which to generate selectors. If omitted, the report includes traffic and interactions but not element-specific data |
flow_ts | No | now | Reference timestamp (Unix ms) for flow correlation. Defaults to the current time |
window_ms | No | 300,000 | Correlation window in milliseconds. Default is 5 minutes (300,000 ms) |
offset | No | 0 | Screenshot ring buffer offset (0 = latest) |
session_id | No | — | Ghost session to scope flow queries |
What the handler does:
- Gathers device info — platform, OS version, app, screen resolution
- Captures screenshot — from ring buffer at the specified offset (failure is non-fatal)
- Fetches element + selectors — if
node_idis provided - Queries correlated flows — up to 200 flows within the correlation window
- Collects interactions — from the last 5 minutes
- Generates the bug report — deterministic assembly of all gathered data
- Starts GIF encoding — runs in a background goroutine alongside AI enhancement
- AI enhancement — 60-second timeout. If the LLM is available, it enhances the report title, description, and summary. Falls back gracefully if AI times out or fails
GIF details:
- Max size: 5 MB
- Max frames: 30 (from the ring buffer)
- Scale factor: 0.25× (quarter resolution to save size)
- Frame delay: 500ms between frames (50 centiseconds)
The report is automatically saved as an artifact (type "bug_report", format "json"), and an artifact.created WebSocket event is broadcast.
Response: A comprehensive bug report object including:
| Field | Description |
|---|---|
title | Bug report title (AI-enhanced if available) |
description | Detailed description |
device_context | Device, OS, app, resolution, timestamp |
summary | Executive summary |
screenshot_b64 | Base64-encoded screenshot image |
element | The targeted element (if node_id was provided) |
selectors | Test automation selectors for the element |
api_evidence | Non-error primary correlated flows |
findings | Detected issues (error/warning/info with messages) |
errors | Failed HTTP requests promoted as errors (status 4xx/5xx, first ~200 chars of body) |
repro_steps | Generated reproduction steps with types (action/observation/network), timestamps, triggered flows, and inferred flags |
waterfall_data | Timing waterfall for correlated flows |
markdown | Full report as markdown text |
screenshot_gif_b64 | Base64-encoded animated GIF of recent screenshots |
ai_enhanced | Whether AI enhancement succeeded |
artifact_id | ID of the saved artifact |
created_at | When the report was generated |
Mobile Setup
Section titled “Mobile Setup”These endpoints help set up mobile devices for traffic capture — discovering simulators/emulators, installing certificates, and configuring proxy settings.
Platform Info
Section titled “Platform Info”GET /api/v1/mobile/platformReturns the host operating system and CPU architecture. Used by the frontend to show platform-appropriate setup instructions.
Response:
{ "os": "darwin", "arch": "arm64"}Verify Connection
Section titled “Verify Connection”GET /api/v1/mobile/verifyVerify that a device is routing traffic through Ghost’s proxy. The device hits this endpoint through the proxy — if it succeeds, the proxy connection is working.
Response:
{ "connected": true, "client_ip": "192.168.1.50"}iOS Mobileconfig Profile
Section titled “iOS Mobileconfig Profile”GET /api/v1/mobile/profileNo authentication required — this endpoint is accessed from Safari on the iOS device, which doesn’t have Ghost’s auth token.
Generates and serves an Apple Configuration Profile (.mobileconfig) containing Ghost’s CA certificate. Opening this in Safari triggers the iOS certificate installation flow.
The profile contains the certificate only — no proxy configuration (global HTTP proxy payloads require a supervised device, so proxy settings must be configured manually in Wi-Fi settings).
Content-Type: application/x-apple-aspen-config
Content-Disposition: attachment; filename="ghost-proxy.mobileconfig"
Each download generates fresh random UUIDs for the profile identifiers.
List iOS Simulators
Section titled “List iOS Simulators”GET /api/v1/mobile/simulatorsLists all currently booted iOS simulators. Uses xcrun simctl list devices --json with a 10-second timeout.
Response:
{ "simulators": [ { "udid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", "name": "iPhone 16 Pro", "runtime": "iOS 17.2", "state": "Booted" } ], "available": true}Returns {"simulators": [], "available": false} on non-macOS systems or when Xcode command-line tools aren’t installed.
Install Simulator Certificate
Section titled “Install Simulator Certificate”POST /api/v1/mobile/simulators/install-certInstalls Ghost’s CA certificate into an iOS simulator’s keychain using xcrun simctl keychain <udid> add-root-cert.
Request body (1,024-byte limit):
{ "udid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890"}The UDID is validated against ^[A-Za-z0-9][A-Za-z0-9.:_-]*$ to prevent command injection (rejects values starting with - which could be interpreted as flags).
Response: {"ok": true}
List Android Emulators
Section titled “List Android Emulators”GET /api/v1/mobile/emulatorsLists connected Android emulators. Uses adb devices with a 10-second timeout and filters for serials starting with emulator-.
Response:
{ "emulators": [ { "serial": "emulator-5554", "state": "device" } ], "available": true}Returns {"emulators": [], "available": false} when ADB is not installed.
Set Emulator Proxy
Section titled “Set Emulator Proxy”POST /api/v1/mobile/emulators/set-proxyConfigures an Android emulator to route HTTP traffic through Ghost. Uses adb shell settings put global http_proxy 10.0.2.2:{proxyPort} — the address 10.0.2.2 is Android emulator’s special alias for the host machine’s loopback (localhost).
Request body (1,024-byte limit):
{ "serial": "emulator-5554"}Response: {"ok": true}
Clear Emulator Proxy
Section titled “Clear Emulator Proxy”POST /api/v1/mobile/emulators/clear-proxyRemoves the HTTP proxy setting from an Android emulator using adb shell settings put global http_proxy :0.
Request body (1,024-byte limit): {"serial": "emulator-5554"}
Response: {"ok": true}
Push Emulator Certificate
Section titled “Push Emulator Certificate”POST /api/v1/mobile/emulators/push-certPushes Ghost’s CA certificate file to the emulator’s SD card using adb push. The certificate is pushed to /sdcard/ghost-ca.crt — the user must then manually install it from Android Settings > Security > Install from storage.
Request body (1,024-byte limit): {"serial": "emulator-5554"}
Response: {"ok": true}
WebSocket Events
Section titled “WebSocket Events”| Event | Trigger | Payload |
|---|---|---|
device.discovered | New device found during discovery scan | Device info object |
device.updated | Device state changed (connecting, error, etc.) | Updated device info |
device.connected | Inspector successfully connected to device | Device info with state: "connected" |
device.disconnected | Inspector disconnected from device | Device info with state: "disconnected" |
device.removed | Device disappeared from discovery | Device info |
artifact.created | Bug report auto-saved as artifact | Artifact summary (id, type, title, format, size, metadata) |
Summary of Limits
Section titled “Summary of Limits”| Limit | Value | Context |
|---|---|---|
| Connect timeout | 120 seconds | Device connection (WDA build can be slow) |
| AI enhancement timeout | 60 seconds | Bug report AI analysis |
| Mobile command timeout | 10 seconds | All xcrun/adb commands |
| Screenshot buffer | 30 frames | Ring buffer size per device |
| Interaction log | 100 events | Per-device in-memory cap |
| Hierarchy log | 100 snapshots | Per-device in-memory cap |
| Correlation flow limit | 500 flows | Max flows queried for correlation |
| Bug report flow limit | 200 flows | Max flows in bug reports |
| Interaction context flow limit | 500 flows | Max flows for test code generation |
| Correlation default window | 3 seconds | ±3s around reference timestamp |
| Correlation max window | 30 seconds | Maximum correlation window |
| Bug report window | 5 minutes | Default correlation window for bug reports |
| GIF max size | 5 MB | Animated screenshot GIF cap |
| GIF max frames | 30 | Maximum frames in GIF |
| GIF scale factor | 0.25× | Quarter-resolution to save size |
| Body preview | 2,048 bytes | First 2 KB of request/response in correlations |
| Tap body limit | 1,024 bytes | Request body for tap command |
| Input body limit | 4,096 bytes | Request body for text/key input |
| Bug report body limit | 64 KB | Request body for bug report generation |
| Mobile action body limit | 1,024 bytes | All mobile setup POST endpoints |
| Interactions default | 50 events, 5 min | Default limit and time window |
| Interactions max | 200 events, 1 hour | Maximum limit and time window |
| Interaction context default | 20 entries, 5 min | Default for test code generation |
| Interaction context max | 100 entries, 1 hour | Maximum for test code generation |