Auto-generated from
src/api-schema.ts- Do not edit directly.
# Is it working?
curl localhost:8700/status
# What tabs are connected?
curl localhost:8700/windows
# What's on the page?
curl -X POST localhost:8700/tree -d '{"mode":"actionable"}'
# Click something
curl -X POST localhost:8700/click -d '{"selector":"#submit"}'
GET /statusServer status
Returns server info and connected browser count.
Response: { serverVersion, ready, windows: […], browsers: n, desktopApp, pid, … }
Use to verify the server is running — but gate a test lane on ready, not on the 200. A
server can be up with zero connected tabs: /status answers fine and there is still nothing to
drive, so a lane that adopts it fails later on a timeout that points at the caller’s own code.
ready is true when at least one top-level tab is connected. hj doctor checks this (plus
ambiguous targeting) and exits non-zero, which is the one-command preflight for a lane.
GET /statsEfficiency and usage statistics
Returns metrics showing Haltija’s efficiency vs raw DOM events.
Response includes:
Use this to verify efficiency claims (99%+ event reduction, etc.) and debug performance.
GET /version[Deprecated] Use /status instead
Deprecated: Version is included in /status response.
GET /docs[Deprecated] Use /api instead
Deprecated: Use /api for complete API documentation.
GET /apiFull API reference
Complete API documentation with all endpoints.
Returns structured JSON with all endpoints, their parameters, and examples.
POST /treeGet DOM tree structure
Returns hierarchical view of page elements. Best for understanding page structure before interacting.
Response structure: { tag, id?, classes?, attrs?, text?, value?, checked?, children?, flags?: { interactive, hidden, hasAria, … } }
Flags help identify interactive elements (buttons, inputs) and hidden content. Form inputs include live value/checked state (not just HTML attribute).
Use ancestors:true to see parent elements when inspecting deep elements.
Parameters:
| Name | Type | Description |
|---|---|---|
selector |
string,null | Root element selector |
depth |
number,null | Max depth (-1 = unlimited). Default: unlimited |
includeText |
boolean,null | Include text content (default true) |
visibleOnly |
boolean,null | Only visible elements (default false) |
interactiveOnly |
boolean,null | Only interactive elements and their ancestors (default false) |
pierceShadow |
boolean,null | Pierce shadow DOM (default true) |
pierceFrames |
boolean,null | Pierce same-origin iframes (default true) |
compact |
boolean,null | Minimal output (default false) |
ancestors |
boolean,null | Include ancestor path from root (default false) |
includeBox |
boolean,null | Include each node’s bounding box { x, y, w, h, visible } (default false) |
mode |
string,null | ‘actionable’ returns a page action summary (url, title, headings, buttons, links, inputs, selects) instead of the DOM tree |
window |
string,null | Target window ID |
Examples:
{"depth":3}
{"selector":"form"}
{"interactiveOnly":true}
{"selector":"#deep-element","ancestors":true}
POST /queryQuery DOM elements by selector
Quick element lookup. Returns basic info: tagName, id, className, textContent, attributes.
Use this to check if an element exists before clicking/typing. For detailed info, use /inspect instead.
Response: { tagName, id, className, textContent, attributes: {…} }
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector |
all |
boolean,null | Return all matches (default false = first only) |
Examples:
{"ref":"42"}
{"selector":"#submit-btn"}
Response:
{
"success": true,
"data": {
"tagName": "button",
"id": "submit-btn",
"className": "btn primary",
"textContent": "Submit",
"attributes": {
"id": "submit-btn",
"class": "btn primary",
"type": "submit"
}
}
}
{"selector":"input[type=\"text\"]","all":true}
POST /inspectDeep inspection of an element
Get everything about ONE element: geometry, computed styles, ARIA attributes, scroll position, visibility state.
Response includes:
Use before clicking to verify element is visible and enabled.
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector |
fullStyles |
boolean,null | Include all computed styles (default: false) |
matchedRules |
boolean,null | Include matched CSS rules with specificity (default: false) |
window |
string,null | Target window ID |
Examples:
{"ref":"42"}
{"selector":"#submit"}
Response:
{
"success": true,
"data": {
"selector": "body > form > button#submit",
"tagName": "button",
"classList": [
"btn",
"primary"
],
"box": {
"x": 100,
"y": 200,
"width": 120,
"height": 40,
"visible": true,
"display": "inline-block",
"visibility": "visible",
"opacity": 1
},
"text": {
"innerText": "Submit",
"textContent": "Submit",
"innerHTML": "Submit"
},
"attributes": {
"id": "submit",
"class": "btn primary",
"type": "submit"
},
"properties": {
"disabled": false,
"hidden": false,
"type": "submit"
},
"hierarchy": {
"parent": "form#login",
"children": 0,
"depth": 4
},
"styles": {
"display": "inline-block",
"visibility": "visible",
"opacity": "1"
}
}
}
{"selector":"input[name=\"email\"]"}
POST /inspectAllInspect multiple elements
Deep inspection of ALL elements matching selector (up to limit).
Same detailed info as /inspect, but for multiple elements. Great for:
Response: array of inspection objects
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output - returns single element as array |
selector |
string,null | CSS selector |
limit |
number,null | Max elements (default 10) |
fullStyles |
boolean,null | Include all computed styles (default: false) |
matchedRules |
boolean,null | Include matched CSS rules with specificity (default: false) |
window |
string,null | Target window ID |
Examples:
{"selector":"button, [role=\"button\"]","limit":20}
{"selector":"input, select, textarea"}
{"selector":"nav a","limit":15}
POST /findFind elements by text content
Search for elements containing specific text. Saves writing querySelector + filter patterns.
Returns first match by default, or all matches with all:true.
Response: { found: true, selector: “…”, element: {…} } or { found: true, elements: […] }
Parameters:
| Name | Type | Description |
|---|---|---|
text |
string | Text to search for (substring match) (required) |
tag |
string,null | Limit to specific tag (button, a, div, etc) |
exact |
boolean,null | Require exact text match (default false = substring) |
all |
boolean,null | Return all matches (default false = first only) |
visible |
boolean,null | Only visible elements (default true) |
window |
string,null | Target window ID |
Examples:
{"text":"Submit","tag":"button"}
{"text":"Learn more","tag":"a"}
{"text":"OK","tag":"button","exact":true}
{"text":"Delete","tag":"button","all":true}
{"text":"Error:"}
POST /formExtract all form values as structured JSON
Get all form field values without needing to know the component’s API.
Introspects forms and returns structured data:
Response: { fields: { name: value, … }, form: { action, method, id } }
Works with standard forms and most framework components (React, Vue, etc).
Parameters:
| Name | Type | Description |
|---|---|---|
selector |
string,null | Form selector (default: first form on page) |
includeDisabled |
boolean,null | Include disabled fields (default false) |
includeHidden |
boolean,null | Include hidden fields (default false) |
window |
string,null | Target window ID |
Examples:
{}
{"selector":"#login-form"}
{"selector":"form","includeHidden":true}
POST /mapAffordance map — what can be interacted with, and what it is wired to
Returns a map of the page’s affordances. Two tiers, and the difference matters:
Native (source: "tosi-agent") — when the page exposes an agent surface at
globalThis.tosiAgent (a tosijs app calling enableAgentInterface()), the map is the app’s OWN
wiring records. That carries what the DOM cannot: which state path each control is bound to and in
which direction — ⟷ two-way (user-writable), ⟵ bound-to-DOM (display only), absent
(static) — plus the handler path each event calls, and the list of callable actions.
With that you can act through paths instead of synthesized input:
hj eval "tosiAgent.write('app.filter', 'milk')" or tosiAgent.call('app.addItem').
Fallback (source: "dom") — any other page, reconstructed from tags/roles/labels/state, each
node carrying a haltija ref for hj click <ref>. Deliberately approximate: it has NO binding
provenance, because that information does not exist in the DOM. Always check source before
trusting the map as wiring rather than as a guess.
Cheaper and more stable than a screenshot for deciding what to do next: no fonts, themes, viewport or animation timing, and structure (nesting) is carried for free.
Parameters:
| Name | Type | Description |
|---|---|---|
global |
string,null | Global to probe for the agent surface (default ‘tosiAgent’) |
maxNodes |
number,null | Cap on DOM-fallback nodes (default 400) |
image |
boolean,null | Also render the map as a schematic PNG (rasterized — an image of the map costs a vision encoder far fewer tokens than dense JSON, but has a fixed ~1-1.5k floor, so it only wins on big maps; response.cost reports both) |
scale |
number,null | Device-pixel scale for the schematic image (default 2) |
window |
string,null | Target window ID |
Examples:
{}
POST /clickClick an element
Scrolls element into view, then performs full click sequence: mouseenter, mouseover, mousedown, mouseup, click.
Three ways to target elements:
Options:
Automatically fails if element is not found or is disabled. Check response.success to verify.
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector of element to click |
text |
string,null | Text content to find (alternative to selector) |
tag |
string,null | Tag name when using text (default: any clickable element) |
autoWait |
boolean,null | Wait for element to appear before clicking (default false) |
timeout |
number,null | Max wait time in ms when autoWait is true (default 5000) |
diff |
boolean,null | Return DOM diff showing what changed after click (default false) |
diffDelay |
number,null | Wait ms before capturing “after” state (default 100) |
window |
string,null | Target window ID |
Examples:
{"ref":"42"}
{"selector":"#submit"}
{"selector":".btn-primary"}
{"text":"Save"}
{"text":"Submit","tag":"button"}
{"text":"Learn more","tag":"a"}
{"selector":"[role=\"button\"][aria-label=\"Close\"]"}
{"selector":".add-item","diff":true}
{"selector":".modal-button","autoWait":true}
{"selector":".slow-element","autoWait":true,"timeout":10000}
POST /typeType text into an element
Focus element and type text character by character with realistic event lifecycle.
Target element via ref (from /tree output) or selector.
Simulates real user behavior:
Handles native inputs, textareas, contenteditable, and framework-wrapped inputs (React, MUI, etc).
Options:
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector of input/textarea/contenteditable |
text |
string | Text to type (required) |
autoWait |
boolean,null | Wait for element to appear before typing (default false) |
timeout |
number,null | Max wait time in ms when autoWait is true (default 5000) |
humanlike |
boolean,null | Human-like delays and typos (default true) |
focusMode |
string,null | How to focus: mouse (default), keyboard, or direct |
clear |
boolean,null | Clear existing content before typing (default false) |
blur |
boolean,null | Blur element after typing to trigger change event (default true) |
typoRate |
number,null | Typo probability 0-1 (default 0.03) |
minDelay |
number,null | Min ms between keys (default 50) |
maxDelay |
number,null | Max ms between keys (default 150) |
diff |
boolean,null | Return DOM diff showing what changed after typing (default false) |
diffDelay |
number,null | Wait ms before capturing “after” state (default 100) |
window |
string,null | Target window ID |
Examples:
{"selector":"#email","text":"user@example.com"}
{"selector":"input[type=\"password\"]","text":"secret123"}
{"selector":"input","text":"hello","humanlike":false,"focusMode":"direct"}
{"selector":"#search","text":"query","focusMode":"keyboard"}
{"selector":"#name","text":"New Name","clear":true}
{"selector":"[contenteditable]","text":"Hello world"}
POST /keySend keyboard input
Send key press with full event lifecycle: keydown → keypress → beforeinput → input → keyup.
Target element defaults to document.activeElement. Use ref or selector to focus a specific element first.
Supports modifiers (ctrlKey, shiftKey, altKey, metaKey) and repeat count for holding keys.
Common keys: Enter, Escape, Tab, ArrowUp/Down/Left/Right, Backspace, Delete, Home, End, PageUp/PageDown, F1-F12, or any printable character.
Parameters:
| Name | Type | Description |
|---|---|---|
key |
string | Key to press (e.g., “Enter”, “Escape”, “a”, “ArrowDown”) (required) |
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | Element to focus first (default: activeElement) |
ctrlKey |
boolean,null | Hold Ctrl/Control |
shiftKey |
boolean,null | Hold Shift |
altKey |
boolean,null | Hold Alt/Option |
metaKey |
boolean,null | Hold Meta/Command |
repeat |
number,null | Repeat count for key hold (default 1) |
window |
string,null | Target window ID |
Examples:
{"key":"Escape"}
{"key":"Enter"}
{"key":"s","ctrlKey":true}
{"key":"Tab","selector":"#first-input"}
{"key":"Tab","shiftKey":true}
{"key":"ArrowDown","repeat":3}
{"key":"a","ctrlKey":true,"selector":"#editor"}
POST /dragDrag from an element
Simulates drag gesture: mousedown on element, mousemove by delta, mouseup.
Good for: sliders, resize handles, drag-and-drop reordering, range inputs.
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector of drag handle |
deltaX |
number,null | Horizontal distance in pixels |
deltaY |
number,null | Vertical distance in pixels |
duration |
number,null | Drag duration in ms (default 300) |
window |
string,null | Target window ID |
Examples:
{"ref":"15","deltaX":100}
{"selector":".slider-handle","deltaX":100}
{"selector":".resize-handle","deltaX":50,"deltaY":50}
{"selector":".drag-item","deltaY":80}
POST /highlightVisually highlight an element
Draw attention to an element with colored border and optional label.
Great for showing users what you found or pointing out issues. Use /unhighlight to remove.
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector |
label |
string,null | Label text to show |
color |
string,null | CSS color (default #6366f1) |
duration |
number,null | Auto-hide after ms (omit for manual) |
window |
string,null | Target window ID |
Examples:
{"ref":"42","label":"Found it!"}
{"selector":"#login-btn","label":"Click here"}
{"selector":".error","label":"Problem","color":"#ef4444"}
{"selector":"button","duration":3000}
POST /unhighlightRemove highlight
Remove any active highlight overlay created by /highlight.
POST /scrollScroll to element or position
Smooth scroll with natural easing. Multiple modes:
At least one of ref, selector, x, y, deltaX, or deltaY must be provided.
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector to scroll into view |
x |
number,null | Absolute X position in pixels |
y |
number,null | Absolute Y position in pixels |
deltaX |
number,null | Relative horizontal scroll in pixels |
deltaY |
number,null | Relative vertical scroll in pixels |
duration |
number,null | Animation duration in ms (default 500) |
easing |
string,null | Easing function: ease-out (default), ease-in-out, linear |
block |
string,null | Vertical alignment: center (default), start, end, nearest |
window |
string,null | Target window ID |
Examples:
{"ref":"42"}
{"selector":"#pricing"}
{"y":0}
{"selector":"footer"}
{"deltaY":500}
{"selector":"#section","duration":1000,"easing":"ease-in-out"}
POST /waitWait for time, element, or condition
Flexible wait for async UI scenarios. Multiple modes:
All modes support timeout (default 5000ms). Returns immediately if condition already met.
Response: { success: true, waited: ms, found?: boolean }
Parameters:
| Name | Type | Description |
|---|---|---|
ms |
number,null | Milliseconds to wait |
forElement |
string,null | CSS selector to wait for |
hidden |
boolean,null | Wait for element to disappear (default false) |
timeout |
number,null | Max wait time in ms (default 5000) |
pollInterval |
number,null | Polling interval in ms (default 100) |
window |
string,null | Target window ID |
Examples:
{"ms":500}
{"forElement":".modal"}
{"forElement":".loading","hidden":true}
{"forElement":"button[data-ready]","timeout":10000}
{"forElement":".dropdown","ms":100}
POST /callCall a method or get a property on an element
Call a method or access a property on an element by selector. Convenience wrapper around /eval.
This avoids writing querySelector boilerplate. Two modes:
Return value is JSON-serialized. Promises are awaited.
Response: { success: true, data:
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output (e.g., 1, 42) - preferred for efficiency |
selector |
string,null | CSS selector of the element |
method |
string | Method name to call or property name to get (required) |
args |
array,null | Arguments to pass (omit to get property value) |
window |
string,null | Target window ID |
Examples:
{"ref":"42","method":"value"}
{"selector":"#email","method":"value"}
{"selector":"#agree","method":"checked"}
{"selector":"#content","method":"innerHTML"}
{"selector":"#item","method":"dataset"}
{"selector":"dialog","method":"open"}
{"selector":"#my-popover","method":"showPopover","args":[]}
{"selector":"#my-popover","method":"hidePopover","args":[]}
{"selector":"video","method":"play","args":[]}
{"selector":"#email","method":"focus","args":[]}
{"selector":"#section","method":"scrollIntoView","args":[{"behavior":"smooth"}]}
{"selector":"#btn","method":"setAttribute","args":["disabled","true"]}
{"selector":"#box","method":"getBoundingClientRect","args":[]}
POST /navigateNavigate to a URL
Navigate the browser to a new URL. Waits for page load to complete.
Use /location after to verify navigation succeeded.
Parameters:
| Name | Type | Description |
|---|---|---|
url |
string | URL to navigate to (required) |
window |
string,null | Target window ID |
Examples:
{"url":"https://example.com/login"}
{"url":"/dashboard"}
POST /refreshRefresh the page
Hard reload the current page, bypassing all caches (CSS, JS, images). Use soft: true for cache-friendly reload.
Parameters:
| Name | Type | Description |
|---|---|---|
soft |
boolean,null | Use cached resources if available (default false = hard refresh that busts all caches) |
window |
string,null | Target window ID |
Examples:
{}
{"soft":true}
GET /locationGet current URL and title
Returns current page info.
Response: { url, title, pathname, search, hash }
Use after /navigate to verify you’re on the expected page.
POST /events/watchStart watching semantic events
Begin capturing high-level user actions. Events are aggregated and meaningful:
Presets control verbosity:
Categories: interaction, navigation, input, hover, scroll, mutation, focus, console
Parameters:
| Name | Type | Description |
|---|---|---|
preset |
string,null | Verbosity: minimal, interactive, detailed, debug |
categories |
array,null | Specific categories to watch |
Examples:
{"preset":"interactive"}
{"preset":"minimal"}
{"categories":["interaction","input","console"]}
POST /events/unwatchStop watching events
Stop capturing semantic events. Events buffer is cleared.
GET /eventsGet captured semantic events
Returns buffered events since watch started.
Response: { events: [{ type, timestamp, category, target?, payload }], since, count }
Event types: interaction:click, input:typed, navigation:navigate, hover:dwell, scroll:stop, etc.
GET /events/statsGet event aggregation statistics
Shows noise reduction metrics.
Response: { rawEvents, semanticEvents, reductionPercent, byCategory: {…} }
Typically see 90%+ reduction (e.g., 2000 raw events → 80 semantic events).
POST /mutations/watchStart watching DOM mutations
Begin capturing DOM changes: elements added/removed, attributes changed, text modified.
Presets filter out framework noise:
Get captured mutations via /mutations/status.
Parameters:
| Name | Type | Description |
|---|---|---|
root |
string,null | Root selector to watch (default body) |
childList |
boolean,null | Watch child additions/removals (default true) |
attributes |
boolean,null | Watch attribute changes (default true) |
characterData |
boolean,null | Watch text content changes (default false) |
subtree |
boolean,null | Watch all descendants (default true) |
debounce |
number,null | Debounce ms (default 100) |
preset |
string,null | Filter preset: smart, xinjs, b8rjs, tailwind, react, minimal, none |
filters |
,null | Custom filter configuration |
pierceShadow |
boolean,null | Watch inside shadow DOM (default false) |
Examples:
{}
{"root":"form","preset":"minimal"}
{"preset":"react"}
POST /mutations/unwatchStop watching mutations
Stop capturing DOM mutations. Call this when done to free resources.
GET /mutations/statusGet mutation watch status
Check if mutation watching is active and get captured mutations.
Response: { watching: boolean, mutations: […], summary: { added, removed, changed } }
POST /selectInteractive element selection
Let user point at elements on the page instead of writing selectors.
Actions:
Workflow: start → (user draws region) → result → use returned selectors
Parameters:
| Name | Type | Description |
|---|---|---|
action |
string,null | Selection action to perform (default: result) |
window |
string,null | Target window ID |
Examples:
{"action":"start"}
{"action":"result"}
{"action":"status"}
POST /select/start[Deprecated] Use /select with action:”start”
Deprecated: Use POST /select {“action”:”start”} instead.
POST /select/cancel[Deprecated] Use /select with action:”cancel”
Deprecated: Use POST /select {“action”:”cancel”} instead.
GET /select/status[Deprecated] Use /select with action:”status”
Deprecated: Use POST /select {“action”:”status”} instead.
GET /select/result[Deprecated] Use /select with action:”result”
Deprecated: Use POST /select {“action”:”result”} instead.
POST /select/clear[Deprecated] Use /select with action:”clear”
Deprecated: Use POST /select {“action”:”clear”} instead.
GET /windowsList connected windows
Returns all connected browser windows/tabs with IDs, URLs, and titles.
Response: { windows: [{ id, url, title, focused }], count, ready, hint }
Use window IDs in other endpoints (e.g., /click, /tree) to target specific tabs.
ready is the signal to gate a test lane on, not “is the server up”. A server can be running
with zero connected tabs — it answers /status 200 but there is nothing to drive, and a lane that
adopts it fails later on a confusing timeout. ready is true when at least one top-level tab is
connected. (Hidden tabs count as ready — they’re reachable, just possibly stale; see the hidden-tab
warning.) hj doctor checks this and exits non-zero, so a lane can fail fast on the real cause.
POST /tabs/openOpen a new tab
Opens a new tab with optional URL. If url is omitted, opens a blank tab.
In the Haltija desktop app the new tab gets the widget auto-injected, so it’s immediately
controllable. Anywhere else (widget injected into a normal browser via bookmarklet/dev-server)
there is no tab API, so this falls back to window.open() — the new tab is a plain browser tab
with NO widget unless its own page injects one. In that case the response has fallback: true
plus a reason, and a top-level warning: the tab will NOT appear in hj tabs and hj commands
cannot reach it (they go to the focused widget tab). To control it, inject the widget on that page
(e.g. HALTIJA_DEV / haltijaDev) or use the desktop app.
Parameters:
| Name | Type | Description |
|---|---|---|
url |
string,null | URL to open |
Examples:
{}
{"url":"https://example.com"}
POST /tabs/closeClose a tab
Desktop app only. Closes the specified tab by window ID.
Get window IDs from /windows endpoint.
Parameters:
| Name | Type | Description |
|---|---|---|
window |
string | Window ID to close (required) |
Examples:
{"window":"window-abc123"}
POST /tabs/focusFocus a tab (route untargeted commands to it)
Make the given tab the target of untargeted commands. This is a server-side
routing change, NOT a browser action: it does not physically raise the tab (a backgrounded browser
tab cannot be raised remotely), and because it never dispatches to the tab it can’t time out — even
if the tab is hidden. After this, commands without a window/--window go to this tab until you
focus another or physically switch tabs in the browser. To pin a single command instead, use
--window <id>. If the tab is hidden the response includes a warning (a backgrounded tab’s results
can be stale). Returns { success, focused, active, title }.
Parameters:
| Name | Type | Description |
|---|---|---|
window |
string | Window ID to focus (required) |
Examples:
{"window":"window-abc123"}
POST /recordingRecord user actions and generate tests
Record user interactions and convert them to runnable tests.
Cross-page recording: Recordings now survive page navigations! The server tracks the recording session by window ID, so you can record a multi-page flow (e.g., login → dashboard).
Actions:
Workflow: start → (user interacts, navigates pages) → stop → list → replay
Parameters:
| Name | Type | Description |
|---|---|---|
action |
string | Recording action to perform (required) |
name |
string,null | Test name (for generate action) |
id |
,null | Recording ID or index number (for replay action) |
window |
string,null | Target window ID |
Examples:
{"action":"start"}
{"action":"stop"}
{"action":"status"}
{"action":"generate","name":"Login flow"}
{"action":"list"}
{"action":"replay","id":"0"}
{"action":"replay","id":"rec_123456_abc"}
POST /recording/start[Deprecated] Use /recording with action:”start”
Deprecated: Use POST /recording {“action”:”start”} instead.
Parameters:
| Name | Type | Description |
|---|---|---|
name |
string,null | Recording/test name |
POST /recording/stop[Deprecated] Use /recording with action:”stop”
Deprecated: Use POST /recording {“action”:”stop”} instead.
POST /recording/generate[Deprecated] Use /recording with action:”generate”
Deprecated: Use POST /recording {“action”:”generate”} instead.
Parameters:
| Name | Type | Description |
|---|---|---|
name |
string,null | Test name |
GET /recordings[Deprecated] Use /recording with action:”list”
Deprecated: Use POST /recording {“action”:”list”} instead.
POST /test/runRun a JSON test
Execute a test defined in Haltija JSON format.
Test structure: { “version”: 1, “name”: “Login flow”, “url”: “http://localhost:3000/login”, “steps”: [ { “action”: “type”, “selector”: “#email”, “text”: “user@example.com” }, { “action”: “type”, “selector”: “#password”, “text”: “secret123” }, { “action”: “click”, “selector”: “button[type=submit]” }, { “action”: “assert”, “assertion”: { “type”: “url”, “pattern”: “/dashboard” } } ] }
Step actions: navigate, click, type, key, wait, assert, eval, verify
Output formats:
Parameters:
| Name | Type | Description |
|---|---|---|
test |
any | Test object with steps (required) |
format |
string,null | Output format: json (structured), github (annotations + summary), human (readable) |
stepDelay |
number,null | Milliseconds between steps (default 100) |
timeout |
number,null | Milliseconds timeout per step (default 5000) |
stopOnFailure |
boolean,null | Stop on first failure (default true) |
patience |
number,null | Total failures allowed before giving up (0 = use stopOnFailure, default 0) |
patienceStreak |
number,null | Consecutive failures to bail immediately (default 2) |
timeoutBonusMs |
number,null | Ms added/removed from timeout on success/failure, capped at initial (default 1000) |
Examples:
{"test":{"version":1,"name":"Click button","url":"http://localhost:3000","steps":[{"action":"click","selector":"#submit"},{"action":"assert","assertion":{"type":"exists","selector":".success"}}]}}
{"test":{"version":1,"name":"Test","url":"http://localhost:3000","steps":[]},"format":"github"}
POST /test/suiteRun multiple tests
Execute a suite of tests, optionally stopping on first failure.
Input: { tests: [test1, test2, …], format?, stopOnFailure? }
Response includes per-test results and overall summary.
Parameters:
| Name | Type | Description |
|---|---|---|
tests |
array | Array of test objects (required) |
format |
string,null | Output format: json (structured), github (annotations + summary), human (readable) |
testDelay |
number,null | Milliseconds between tests (default 500) |
stepDelay |
number,null | Milliseconds between steps (default 100) |
timeout |
number,null | Milliseconds timeout per step (default 5000) |
stopOnFailure |
boolean,null | Stop on first failure (default false for suites) |
patience |
number,null | Total failures allowed per test before giving up (0 = use stopOnFailure, default 0) |
patienceStreak |
number,null | Consecutive failures to bail immediately (default 2) |
timeoutBonusMs |
number,null | Ms added/removed from timeout on success/failure, capped at initial (default 1000) |
Examples:
{"tests":[{"version":1,"name":"Login","url":"http://localhost:3000/login","steps":[]},{"version":1,"name":"Dashboard","url":"http://localhost:3000/dashboard","steps":[]}],"stopOnFailure":false}
POST /test/validateValidate test without running
Check that a test is well-formed and all selectors exist on the current page.
Use this to pre-check tests before running. Returns validation errors without executing steps.
Response: { valid: boolean, errors?: [{ step?, message }] }
Parameters:
| Name | Type | Description |
|---|---|---|
test |
any | Test object to validate (required) |
Examples:
{"test":{"version":1,"name":"Test","url":"http://localhost:3000","steps":[{"action":"click","selector":"#btn"}]}}
GET /consoleGet console output
Returns captured console.log/warn/error/info/debug from the page — AND uncaught exceptions and unhandled promise rejections (as level ‘error’), which never route through console.error. Error objects keep their message and stack (a plain console.error(err) used to serialize to {}).
Response: { entries: [{ level, args, timestamp, stack? }] }
Great for debugging — check for errors after actions fail. Note: capture starts when the widget is injected, so errors thrown BEFORE injection are missed for bookmarklet/dev-server injection; the desktop app injects at document-start and catches them.
POST /evalExecute JavaScript
Run arbitrary JavaScript in the browser context. Returns the result.
The code runs in the page’s context with access to window, document, etc. Return values are JSON-serialized.
Async code is supported: a returned Promise is awaited, and top-level await
works. Multi-statement code needs an explicit return to produce a value.
document.title -> the title await fetch(‘/api’).then(r => r.json()) -> the parsed body const r = await fetch(‘/api’); return r.status -> the status code
Parameters:
| Name | Type | Description |
|---|---|---|
code |
string | JavaScript code to execute (required) |
window |
string,null | Target window ID |
Examples:
{"code":"document.title"}
{"code":"document.querySelectorAll(\".item\").length"}
{"code":"document.querySelector(\"#email\").value"}
{"code":"window.localStorage.getItem(\"token\") !== null"}
{"code":"({ x: window.scrollX, y: window.scrollY })"}
POST /fetchFetch a URL from within the tab context
Fetch a URL from within the browser tab’s context. Essential for accessing blob: URLs which are only valid in the tab that created them.
Returns the content as base64 with MIME type. Works with:
Response: { success: true, data: { mimeType, base64, size, url } }
Use this when you see a blob URL in the DOM and need to access its content.
Parameters:
| Name | Type | Description |
|---|---|---|
url |
string | The URL to fetch (blob:, data:, http:, https:) (required) |
window |
string,null | Target window ID |
Examples:
{"url":"blob:https://example.com/abc-123-def"}
{"url":"https://example.com/image.png"}
POST /screenshotCapture a screenshot
Capture the page or a specific element as PNG/WebP/JPEG.
Works automatically in the Haltija Desktop app. In browser widget mode, captures viewport only.
**canvas — capture a
Caveat it handles for you: a WebGL context clears its drawing buffer after compositing unless
created with { preserveDrawingBuffer: true }, so a naive toDataURL can silently return a BLANK
image. Haltija samples the result and returns a warning explaining that (rather than handing you
an empty picture). A canvas tainted by cross-origin content returns a clear error, not a crash.
When file=true (default from CLI), saves to /tmp/haltija-screenshots/ and returns file path. When file=false, returns base64 data URL in response JSON.
Response: { success, path?, image?, width, height, source, canvas?, warning? }
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string,null | Ref ID from /tree output - capture specific element |
selector |
string,null | Element to capture (omit for full page) |
canvas |
string,null | Selector for a |
format |
string,null | Image format: png (default), webp, or jpeg |
quality |
number,null | Quality 0-100 for lossy formats (webp/jpeg) |
scale |
number,null | Scale factor (default 1) |
maxWidth |
number,null | Max width in pixels |
maxHeight |
number,null | Max height in pixels |
window |
string,null | Target window ID |
chyron |
boolean,null | Burn page title, URL, timestamp into image (default true, set false for clean screenshot) |
delay |
number,null | Wait ms before capturing (e.g. 1000 to let page settle after navigation) |
file |
boolean,null | Save to disk and return file path instead of data URL (default true — pass false for base64) |
schematic |
boolean,null | Return a schematic of the page INSTEAD of pixels, even when real capture is available. Cheaper, deterministic, and carries the contrast audit; canvases are still embedded as real pixels. |
fallback |
boolean,null | When no pixel capture is available, return a labelled SCHEMATIC of the page instead of failing (default true; canvases are embedded as real pixels since they need no permission). Pass false to hard-fail instead. |
Examples:
{}
{"ref":"42"}
{"selector":"#chart"}
{"scale":0.5,"maxWidth":400}
{"chyron":false}
POST /snapshotCapture page snapshot
Capture current page state for debugging.
Includes: DOM tree, console logs, viewport size, scroll position, URL, timestamp.
Response: { snapshot: { url, title, viewport, dom, console, timestamp } }
Great for debugging test failures - call this when something goes wrong.
Parameters:
| Name | Type | Description |
|---|---|---|
trigger |
string,null | What triggered the snapshot (e.g., “manual”, “test-failure”) |
context |
,null | Additional context about the snapshot |
Examples:
{"trigger":"manual"}
{"trigger":"test-failure","context":{"step":3,"error":"Element not found"}}
POST /video/startStart video recording
Start recording the browser tab as WebM video. Requires the Haltija Desktop app.
The recording saves to /tmp/haltija-videos/ when stopped. Max duration is capped to prevent runaway recordings.
Response: { success, recordingId }
Parameters:
| Name | Type | Description |
|---|---|---|
maxDuration |
number,null | Max recording duration in seconds (default 60, max 300) |
window |
string,null | Target window ID |
Examples:
{}
{"maxDuration":120}
POST /video/stopStop video recording
Stop recording and save the video file.
Response: { success, path, duration, size }
Parameters:
| Name | Type | Description |
|---|---|---|
window |
string,null | Target window ID |
Examples:
{}
GET /video/statusCheck video recording status
Check if video recording is active.
Response: { recording, recordingId?, duration?, window? }
Examples:
{}
POST /dialog/configureConfigure native dialog auto-response policy
Set how native browser dialogs (alert, confirm, prompt) are handled.
By default, Haltija intercepts all native dialogs and auto-responds:
Configure the policy before triggering actions that cause dialogs. Each dialog is logged and reported via the dialog/opened push event.
Response: { policy: { alert, confirm, prompt, beforeunload } }
Parameters:
| Name | Type | Description |
|---|---|---|
alert |
string,null | “dismiss” (only option for alerts) |
confirm |
string,null | “accept” or “dismiss” |
prompt |
,null | “dismiss” or { “response”: “text” } to auto-fill |
beforeunload |
string,null | “allow” or “block” — controls page unload |
window |
string,null | Target window ID |
Examples:
{"confirm":"accept"}
{"confirm":"dismiss"}
{"prompt":{"response":"my answer"}}
GET /dialog/historyGet recent dialog history
Returns a list of recently intercepted native dialogs.
Each entry includes: type (alert/confirm/prompt), message, response given, timestamp. Buffer holds the last 50 dialogs.
Response: { history: [{ type, message, defaultValue?, response, timestamp }] }
GET /networkGet captured network requests
Returns buffered network entries in compact format.
Each entry: { m: method, s: status, url: trimmed_url, t: time_ms, sz: human_size, type: resource_type } Status -1 means failed (timeout, CORS, canceled). Summary line always included.
Use preset parameter to override the watch preset for this query.
POST /network/watchStart capturing network traffic
Begin monitoring HTTP requests and responses via Chrome DevTools Protocol. Requires the Haltija Desktop app (uses Electron’s CDP access).
Presets control what’s captured:
Output is token-optimized: ~10 tokens per request vs 200+ for raw HAR.
Parameters:
| Name | Type | Description |
|---|---|---|
preset |
string,null | Filter preset: errors, minimal, standard, verbose |
includePatterns |
array,null | URL regex patterns to always include |
excludePatterns |
array,null | URL regex patterns to exclude |
maxBuffer |
number,null | Max entries to buffer (default 200) |
POST /network/unwatchStop capturing network traffic
Stop monitoring network requests. Clears the capture buffer.
GET /network/statsNetwork traffic summary
Returns: total requests, failures, pending, average latency, total bytes, and a one-line summary string.