Skip to main content

Discovery Endpoints

Endpoints for discovering controllable elements in the UI.

Discover Elements​

POST /control/discover

Scan the DOM for controllable elements.

Request Body​

{
"root": "#app",
"interactiveOnly": true,
"includeHidden": false,
"limit": 100,
"types": ["button", "input", "select"],
"selector": ".form-control"
}
FieldTypeDefaultDescription
rootstringdocumentKeep only elements inside this element (a root that matches nothing is refused)
interactiveOnlybooleanfalseOnly interactive elements
includeHiddenbooleantrueHidden elements are included unless this is false
limitnumbernoneMax elements to return, applied after filtering
typesstring[]allFilter by element types
selectorstringnoneKeep only elements matching this CSS selector
includeContentbooleantrueContent rows are included unless this is false
contentOnlybooleanfalseOnly content rows
contentRolestringnoneOnly content rows with this content role
includeMediabooleantrueMedia rows are included unless this is false
mediaOnlybooleanfalseOnly media rows (images, picture, video, SVG, canvas, background images)
mediaTypestringnoneOnly media of this type (image, picture, video, svg, …)
brokenOnlybooleanfalseOnly media that failed to load
missingAltOnlybooleanfalseOnly images with no alt text that are not decorative
srcPatternstringnoneRegex matched against the media src (ReDoS-guarded; a redacted src reads [REDACTED])
oversizeThresholdnumbernoneOnly media whose effectiveResolution is at least this

Every option is honoured the same way on every transport — the in-page executor, the server handler, the live relay and the relay's cached snapshot — because they share one filter. Two rules govern it:

  • A filter never passes a row it cannot evaluate. A media-fact filter (mediaType, brokenOnly, missingAltOnly, srcPattern, oversizeThreshold) leaves out an image whose fact is unmeasured — say, one that has not loaded yet, so its effective resolution is unknown — and lists it in the response's unevaluated array ({ id, option, code }). An empty unevaluated says every row was evaluated.
  • An option a transport cannot honour is refused, typed — never answered with an empty list. root / selector over the relay's cached snapshot (it carries no DOM) and media-fact filters over a snapshot from an SDK build without media observations answer UB-OBS-CAPABILITY-UNAVAILABLE-IN-BUILD; a media question over an empty registry answers UB-OBS-PRODUCER-NOT-RUN; a request that contradicts itself (mediaOnly with interactiveOnly) answers UB-VALIDATION-ERROR.

Each media row carries mediaMetadata: one observation envelope ({ status, value, provenance }) whose value holds the measured facts — natural and rendered size, devicePixelRatio, effectiveResolution (source pixels per device pixel actually displayed), objectFit, dimsReserved, loading / decoding / fetchPriority, encodedBodySize, isLcpCandidate, clsSources, slotId — and whose provenance.coverage.unmeasured names every fact that could not be measured, with the reason.

Response​

{
"success": true,
"data": {
"elements": [
{
"id": "submit-btn",
"type": "button",
"label": null,
"tagName": "button",
"role": "button",
"accessibleName": "Submit",
"actions": ["click", "focus", "blur", "hover"],
"state": {
"visible": true,
"enabled": true,
"focused": false,
"rect": {
"x": 100,
"y": 200,
"width": 120,
"height": 40
}
},
"registered": true
},
{
"id": "email-input",
"type": "input",
"tagName": "input",
"role": "textbox",
"accessibleName": "Email address",
"actions": ["click", "type", "clear", "focus", "blur"],
"state": {
"visible": true,
"enabled": true,
"focused": false,
"value": "",
"rect": {
"x": 100,
"y": 100,
"width": 300,
"height": 40
}
},
"registered": true
}
],
"total": 2,
"durationMs": 15.5,
"timestamp": 1234567890
}
}

Discovered Element Fields​

FieldTypeDescription
idstringElement identifier
typestringElement type (button, input, etc.)
labelstringRegistered label (if any)
tagNamestringHTML tag name
rolestringARIA role
accessibleNamestringComputed accessible name
actionsstring[]Available actions
stateobjectCurrent element state
registeredbooleanIf registered with UI Bridge

Element Types​

Discovery returns these element types:

TypeElements
button<button>, <input type="submit">, [role="button"]
input<input type="text">, <input type="email">, etc.
textarea<textarea>
select<select>, [role="listbox"]
checkbox<input type="checkbox">, [role="checkbox"]
radio<input type="radio">, [role="radio"]
link<a>, [role="link"]
form<form>
menu[role="menu"]
menuitem[role="menuitem"]
tab[role="tab"]
dialog<dialog>, [role="dialog"]
customOther interactive elements

Examples​

Find All Buttons​

curl -X POST http://localhost:9876/ui-bridge/control/discover \
-H "Content-Type: application/json" \
-d '{"types": ["button"]}'

Find Interactive Elements in Form​

curl -X POST http://localhost:9876/ui-bridge/control/discover \
-H "Content-Type: application/json" \
-d '{
"root": "#login-form",
"interactiveOnly": true
}'

Find Visible Inputs​

curl -X POST http://localhost:9876/ui-bridge/control/discover \
-H "Content-Type: application/json" \
-d '{
"types": ["input", "textarea"],
"includeHidden": false
}'