Automation Guide

An automation is a script that does work for you — on a website, against an API, or in a Windows application. Every automation is written in the same language, NAL (Nocarta Automation Language), and comes in one of three kinds, chosen when you create it:

  • Desktop — the Nocarta desktop app drives a web page in its own browser: the page commands, plus js/pw when you need them
  • Browser — runs in your own browser tab, no desktop app needed: http requests and api calls, for API work and data pipelines
  • Windows — the Nocarta desktop app drives a native Windows application

Requirements

Desktop and Windows automations need the Nocarta desktop app. They drive a real browser or a real application, so for security and functionality they run on your computer, not on the server. Browser automations run in the tab you are reading this in, on any device.

Download Nocarta Desktop | Setup Guide


Automation Assistant

The Automation Assistant is your conversational helper for creating and testing automations. Open it from any automation page or the automations list.

Two Ways to Use It

Mode What It Does AI Required?
Direct Commands Execute immediately - open pages, navigate, record actions, take screenshots No
Smart Commands Understand natural language - find elements, click buttons by description, answer questions Yes

Nocarta AI is always available — smart commands work out of the box with no configuration. You can also bring your own LLM provider for additional flexibility. Direct commands always work, even without any AI.

Browser Commands

Control the browser with simple commands:

Command What It Does
open "google.com" Opens a URL in the browser
back Go back to the previous page
forward Go forward in history
refresh Reload the current page
screenshot Capture the current page
close browser Close the browser window
browser status Check if the browser is running

Recording Commands

Record your actions to create automations automatically:

Command What It Does
start recording Begin capturing your clicks, typing, and navigation
stop recording Stop capturing actions
show recorded actions See what you've recorded so far
save as automation Save your recording as a reusable automation

Tip: When saving, you can select specific actions to include. Don't worry if you made mistakes during recording - just pick the steps you want!

Element Selector Tool

Need to find a CSS selector for an element? Use the selector picker:

Command What It Does
get selector Click any element to get its selector

After typing the command, hover over elements in the browser - they'll highlight in blue. Click to capture the selector.

Direct Click with CSS Selectors (No AI needed)

When you know the CSS selector, you can click elements directly without AI:

Command What It Does
click "#submit-btn" Clicks element by ID
click ".login-button" Clicks element by class
click "input[type=\"submit\"]" Clicks element by attribute selector
click "table > tbody > tr:nth-of-type(2)" Clicks with CSS combinators and pseudo-selectors
click "#row-9" holding "Shift" Clicks with keys held down — Shift extends a selection to that row, Control adds it without losing the rest. Combine them with +, as in holding "Control+Shift"
if exists("#cookie-banner .accept") … click "#cookie-banner .accept" … end Clicks the element only if it exists — exists() tests a CSS selector in any condition, so a missing element is skipped and the run continues (a plain click fails and stops the run when the element is absent)

Tip: Use get selector to pick an element visually, then copy the selector directly into a click command!

Optional steps: Wrap a click in if exists("...") … end for UI that may or may not appear (cookie banners, "maybe-open" dialogs) so a missing element never stops the automation.

Direct Fill with CSS Selectors (No AI needed)

When you know the CSS selector, you can fill fields directly without AI:

Command What It Does
fill "input[name=\"email\"]" with "[email protected]" Fills the email input directly
fill "{{SECRET.PASSWORD}}" into "#password" Fills the password field with a secret
fill ".search-input" with "hello" Fills element by class selector

Tip: CSS selectors are detected automatically when they contain [], start with #, or start with .. This is faster than AI-powered fill and works without any AI configuration!

Keys and Focus

Command What It Does
press "Tab" Sends the key itself, wherever the focus currently is (type "Tab" would type the letters T-a-b)
press "Enter" in "#search" Focuses that element first, then sends the key
press "Control+A" Chords work; so do Escape, Backspace, Delete, the arrows, Home/End/PageUp/PageDown and F1–F24
read focus Reads back whatever element has the focus — its value, or its text, or its label

Note: a word that is not a key name is still a button label, so press "Cancel" keeps clicking the Cancel button.

Click by Text (No AI needed)

Click the first element on the page that contains specific text. This works without AI and is useful when elements don't have stable CSS selectors:

Command What It Does
click "Sign In" Clicks the first element containing "Sign In" (direct text match, no AI)
click text "Submit Order" Same as above, explicit syntax

How it works: The command searches all visible elements on the page. It prioritizes exact matches, interactive elements (buttons, links), and shorter text. If multiple elements match, the best candidate is clicked automatically.

Attribute fallback: If no element is found by visible text, the command also searches name, id, and alt attributes. This is useful for elements without visible text like <input type="image" name="Search">. When matched by an attribute, the assistant tells you where it found the match (e.g., "matched by attribute: name").

Find Elements by Text (No AI needed)

Search for elements by their visible text content without clicking them:

Command What It Does
find by text "Dashboard" Lists all elements containing "Dashboard" with their selectors
find on selected Extracts interactive elements from your current text selection in the browser

Select Dropdown Options (No AI needed)

Pick an option from a <select> dropdown by its visible text:

Command What It Does
select "January" from "select[name=\"month\"]" Selects "January" from the month dropdown
pick "United States" from #country Selects "United States" from the country dropdown
choose "High" from .priority-select Selects "High" from a priority dropdown

Tip: The option is matched by visible text first, then by value attribute. If no match is found, the assistant shows all available options so you can pick the right one.

Frames, Iframes & Link Discovery (No AI needed)

Some websites use <iframe> or legacy <frame>/<frameset> elements to embed content. The assistant can navigate into these frames so you can interact with their content.

Command What It Does
list iframes Show all iframes and frames in the current context (works at any nesting level)
list frames Same as above
list links List all links in the current frame with text, href, class, target, and CSS selectors
list links "<keyword>" Filter links by keyword (searches text, href, class, name, id, title)
switch to iframe "#payment-frame" Enter an iframe by CSS selector
switch to frame "Menu" Enter a frame by name (bare name resolves automatically)
exit iframe Return to the previous level (or main page)
exit frame Same as above
highlight "<selector>" Highlight an element with a red outline (default). Use to visually identify frames, iframes, or any element
highlight "#total" blue Highlight with a specific color: red, blue, green, black, white, orange
clear highlights Remove all highlights from the page

How frame context works: Once you switch into a frame, all subsequent commands (js, click, fill, list links, list iframes, show elements, etc.) execute inside that frame. Use exit iframe to go back one level.

Understanding Nested Frames

Many enterprise applications have multiple levels of frames: a top-level frameset with named frames, and inside each frame there may be additional iframes. The key is to navigate one level at a time:

Page (top level)
├── frame "Header"           ← top-level frame
├── frame "Menu"             ← top-level frame
├── frame "Body"             ← top-level frame
│   └── iframe #iFrame_Tab   ← NESTED iframe inside Body
│       └── (your links, tables, forms are here!)
└── frame "hiddenFrame"      ← hidden utility frame

To reach content inside a nested iframe, you switch into each level:

  1. switch to frame "Body" — enter the Body frame
  2. list iframes — now shows iframes inside Body, not the top-level ones
  3. switch to iframe "#iFrame_Tab" — enter the nested iframe
  4. All commands now operate on the actual content inside that iframe

Example: Finding Where an Element Lives

Can't find an element? It's probably inside a nested frame. Follow this discovery automation:

# 1. List the top-level frames
list frames
#   Frame 0: name="Header" (986x40)
#   Frame 1: name="Menu" (184x832)
#   Frame 2: name="Body" (802x832)

# 2. Switch to the most likely frame (usually "Body" or "Content")
switch to frame "Body"

# 3. Check for nested iframes inside this frame
list iframes
#   iFrame 0: id="iFrame_Content" src=".../search.jsp" (800x600)

# 4. Switch into the nested iframe
switch to iframe "#iFrame_Content"

# 5. Now search for your element using list links
list links
#   1. "View Details" class="action_link" target="Body"
#      href: /app/actions?id=123&operation=view...
#      selector: a.action_link

# 6. Filter links by keyword for faster results
list links "employee"
#   1. "Employee 12345" class="action_link"
#      href: /app/actions?matricola=12345...

# 7. Click the link you found
click "a.action_link[href*=\"matricola=12345\"]"

# 8. Exit back when done: iframe → frame → main
exit iframe       # back to Body frame
exit iframe       # back to main page

Example: Simple Frameset Navigation

# 1. See all frames on the page
list frames
#   Frame 0: name="Header" src=".../header.jsp"
#   Frame 1: name="Navigation" src=".../nav.jsp"
#   Frame 2: name="Content" src=".../main.jsp"

# 2. Switch into the Navigation frame
switch to frame "Navigation"

# 3. Now all commands run inside the Navigation frame
click "Reports"
click "Monthly Summary"

# 4. Switch to the Content frame to see results
exit frame
switch to frame "Content"

# 5. Interact with the content
list links "report"
read table
click "#export-button"

# 6. Return to main page when done
exit frame

Example: Embedded iFrame (Payment Form)

# 1. On a checkout page with an embedded payment form
list iframes
#   iFrame 0: id="stripe-frame" src="https://js.stripe.com/..."

# 2. Switch into the payment iframe
switch to iframe "#stripe-frame"

# 3. Fill the payment form inside the iframe
fill "input[name=\"cardnumber\"]" with "4242424242424242"
fill "input[name=\"exp-date\"]" with "12/28"

# 4. Return to the main page to click submit
exit iframe
click "#place-order"

Tips:

  • Start with list frames to see the top-level layout
  • Use list iframes inside a frame to discover nested iframes (it always shows what's inside the current context)
  • Use list links "<keyword>" to quickly find a specific link by any attribute (text, href, class, name, id)
  • For named frames, you can use just the name: switch to frame "Content"
  • For iframes with IDs, use CSS selectors: switch to iframe "#my-iframe"
  • Both <iframe> and legacy <frame> elements are fully supported
  • Use exit iframe once per level to go back (iframe → frame → main page)

Page/Window Navigation (No AI needed)

Some websites open popups or new windows (via window.open(), target="_blank", or JavaScript calls like onclick="openWindow()"). The assistant can switch between these windows so you can interact with the popup content.

Command What It Does
list pages Show all open windows/pages with their index, URL, title, and which one is active
switch to page "last" Switch to the most recently opened window (the popup)
switch to page 0 Switch to a window by index (0-based)
switch to page "causale" Switch to a window whose URL contains the keyword

Example: Interacting with a Popup

# 1. Click a button that opens a popup window
js scegliCausale(5)

# 2. List all open pages to find the popup
list pages
#   [0] https://app.example.com/main (active)
#   [1] https://app.example.com/popup/causale

# 3. Switch to the popup
switch to page "last"

# 4. Interact with the popup content
click "Approve"
fill "#note" with "Approved"

# 5. Switch back to the main window
switch to page 0

Tips:

  • Use list pages to see all windows after a popup opens
  • switch to page "last" is the quickest way to jump to a newly opened popup
  • Frame context is reset when switching pages — you start at the main frame of the new page
  • You can switch back and forth between windows as many times as needed

Table Read & Fill (No AI needed)

Read HTML tables and fill their input fields by row and column index. Useful for data entry forms, timesheets, attendance tables, and similar grid-based interfaces.

Command What It Does
read table Read the first table on the page, showing all rows, columns, and editable fields
read table "#my-table" Read a specific table by CSS selector
fill table row 0 col 2 with 8 Fill the input at row 0, column 2 with "8" (0-indexed)
read table pw Playwright-based table read — better for tables inside frames or complex pages
read table pw "#my-table" Playwright-based read of a specific table
fill table pw row 0 col 2 with 8 Playwright-based table fill — better for frames or complex pages

Example: Filling a Timesheet Table

# 1. Read the table to see its structure
read table

# Output:
#   Table: #timesheet (5 rows)
#   row\col  [0] Employee   [1] Date        [2] Hours   [3] Notes
#   0        John Smith     2026-01-15      [text] 8    [text]
#   1        Jane Doe       2026-01-15      [text]      [text]
#   2        Bob Wilson     2026-01-15      [text] 7.5  [text]

# 2. Fill empty cells
fill table row 1 col 2 with 8
fill table row 1 col 3 with "Remote work"

# 3. For tables inside frames, switch to the frame first
switch to frame "Content"
read table
fill table row 0 col 2 with 8

Tips:

  • Row and column indices are 0-based
  • Editable cells are highlighted with their input type: [text], [select], [checkbox]
  • For dropdowns, use the visible option text: fill table row 0 col 1 with "January"
  • Checkboxes accept true/yes/1 or false/no/0
  • Secrets are supported: fill table row 0 col 2 with "{{SECRET.VALUE}}"
  • Playwright variants: read table pw and fill table pw use Playwright instead of raw JS — better for tables inside frames or complex pages

Page Analysis (AI-Powered)

Discover what's on a page before building your automation:

Command What It Does
show elements Analyze the page and list all interactive elements with their CSS selectors
analyze the page Same as above - get a structured breakdown of the page

The show elements command returns a categorized list of:

  • Forms - with all their fields and submit buttons
  • Buttons - clickable actions with their purpose
  • Input fields - text boxes, checkboxes, dropdowns
  • Links - navigation and action links
  • Interactive elements - modals, dropdowns, accordions
  • Data displays - tables, lists, cards

Each element includes its CSS selector, making it easy to use in subsequent commands or automation steps.

Smart Commands (AI-Powered)

When AI is configured, you can describe what you want in plain language:

Example What Happens
search and click "the login button" AI finds and clicks the login button
find "the search box" AI locates the element and shows its selector
fill "hello" into "the message field" AI finds the field and types "hello"
ask "how do I read a table?" AI answers your question

Using Secrets in Commands

When your automation has bound secrets, you can use them in commands:

Example What Happens
fill "{{MYSITE.USERNAME}}" into "the email field" Fills the username from your bound secret
type "{{MYSITE.PASSWORD}}" Types the password into the focused field
open "https://api.example.com?key={{API.VALUE}}" Opens URL with your API key injected
js fetch('/api?token={{AUTH.VALUE}}') Runs JavaScript with secret injected

Security: Secrets are resolved on your desktop - they never go through the server during command execution. Tier 2 secrets require your vault to be unlocked.

AI Configuration

Smart commands work immediately with Nocarta AI — no setup needed. If you want to use your own LLM provider instead, configure it in Settings:

  1. Go to Settings
  2. Scroll to LLM Configuration
  3. Click Add LLM and choose a provider
  4. Enter your API key and select a model

Supported providers:

  • Ollama - Free, runs locally on your computer
  • OpenAI - GPT models (requires API key)
  • Anthropic - Claude models (requires API key)
  • Google Gemini - Gemini models (requires API key)
  • Custom (OpenAI-compatible) - vLLM, LM Studio, Together AI, Groq, and more

Your own LLMs take priority when configured. Nocarta AI remains available as a fallback. See the AI Assistant &amp; LLM Configuration guide for details.


How Automations Work

  1. Create: Choose a kind (Desktop, Browser or Windows) and write the script
  2. Bind: Connect secrets for credential injection (optional step)
  3. Execute: Run on your desktop with a live browser or as a script

Creating an Automation

Automation creation happens in 2 steps in the new wizard:

Step 1: Automation Description

  1. Navigate to New Automation
  2. Fill in the details:
    • Name — a unique name to identify the automation
    • Description — instructions or context about what the automation does
    • Browser Type — choose between Chromium (recommended), Firefox, or WebKit
    • Timeout — max time in seconds for the entire execution (default: 300s)
    • Headless Mode — run the browser without visual interface (faster)
  3. Click Next to go to Step 2

Step 2: Choose the kind

Choose where the automation runs and what it drives. The language is the same in all three — see NAL:

Kind Description Best For
Desktop The desktop app drives a web page in its own browser: page commands, js/pw, plus http and api. Websites, forms, portals, tables
Browser Runs in your own browser tab, no desktop app: http requests and api calls, with the whole core language. API-based automation, webhooks, data pipelines, anything you want to run from a phone
Windows The desktop app drives a native Windows application: the Windows commands, plus http and api. Legacy desktop software with no web interface

Creating a Desktop automation

  1. After choosing Desktop in Step 2, you'll see the script editor
  2. Write the script — page commands, variables, loops, conditions, functions, instance data
  3. Click Create Automation to save

Quick Example:

url = "https://example.com"
open url
fill "#email" with "[email protected]"
click "#login-button"
validate exists("#dashboard")

Creating a Browser automation

  1. After choosing Browser in Step 2, you'll see the script editor
  2. Write the script: http requests and api calls plus variables, loops, and conditions
  3. Use Check syntax to validate before saving — it also names any command that needs the desktop app
  4. List the hosts the script may contact on the Settings tab
  5. Click Create Automation to save

Quick Example:

r = http get "https://api.example.com/users?page=1", {
  headers: {Authorization: "Bearer {{API.TOKEN}}"}
}
validate r.status == 200

Creating a Windows automation

  1. After choosing Windows in Step 2, you'll see the script editor
  2. Write the script with the Windows commands (launch, set value of, read grid, click button … in …, …) — see the NAL reference
  3. Click Create Automation to save, then run it from the desktop app on a Windows machine

Working in the Script Editor

The editor on an automation's Script tab colours the script as you type, numbers the lines, counts the commands, and completes verbs: start typing cl and pick click "#<id>" with the arrow keys, then Tab or Enter.

Right-click a line for the things you do most often:

  • Run this line in the assistant — sends just that line (or every line the selection touches) to the assistant chat, so you can try one command without running the whole script
  • Comment out / Uncomment, Duplicate line, Delete line, Copy line

Every item works on whole lines, and blank lines and comments are dropped before anything is sent to the assistant.

When a command fails, the message appears in the chat and as a red notice in the corner. The notices stack, and they stay for 5 seconds after the last one — so a run that keeps failing keeps its errors on screen instead of each one vanishing before you read it. Click the × to dismiss one.


Selectors

Each command that interacts with the page needs a selector. Use the get selector command to pick elements visually, or use these formats:

Type Syntax Best For
CSS "#login-button", ".submit-btn" IDs and classes
XPath "//button[@type='submit']" Complex queries
Text "Sign In" Button/link text

Tips:

  • Use get selector in the assistant to pick elements visually
  • CSS is fastest and most reliable
  • IDs (like #login-btn) are the most stable
  • Text selectors are readable but may break if text changes

Binding Secrets

After creating your automation, you can bind credentials stored in the Vault for secure injection in your steps or scripts:

  1. Create your secrets in the Vault first
  2. Open the created automation
  3. Go to the Bound Secrets tab
  4. Click Bind Secret
  5. Map a secret to a placeholder: {{ALIAS.FIELD}}
    • Use the syntax: {{STORE_NAME.VALUE}}, {{STORE.USERNAME}}, {{STORE.PASSWORD}}, {{STORE.URL}}
    • In a Desktop or Windows automation: fill "#email" with "{{MYSITE.USERNAME}}"
    • In an http request, any kind: headers: {Authorization: "Bearer {{API.VALUE}}"}

Security: Credentials are injected on your device at runtime — on the desktop app for Desktop and Windows automations, inside the browser sandbox for Browser automations. The server never sees the actual values during execution. Tier 2 secrets require your vault to be unlocked.


Executing Automations

From Web Interface

  1. Open the automation
  2. Click Execute
  3. If not on desktop: You'll be prompted to open the desktop app

From Desktop App

  1. Open Nocarta Desktop
  2. Navigate to Automations
  3. Click Run on any automation
  4. Watch the browser automation live

From Mobile (Dispatch)

  1. Open a Notebook on your phone
  2. Click Dispatch to Desktop
  3. Select the automation
  4. Execution starts on your desktop

From the API or an AI assistant

Your own code, or an AI assistant connected to your account, can start a run with POST /api/v1/automations/{id}/runs — the API page has the request and everything that comes back. The run still executes on a desktop: yours when the desktop app is connected, otherwise one Nocarta starts for you, which takes up to a couple of minutes.

Two things have to be true first:

  • Remote execution is on — turn on desktop execution and browser dispatch under Settings → Remote Dispatch.
  • The token may run, not only read — starting a run is its own permission, separate from editing a script.

Some runs are refused straight away rather than left waiting: an automation that uses vault credentials (only you can unlock the vault, at a desktop), a signed automation whose signature no longer verifies, an input that is missing or of the wrong type, or a second run while one is already going — one automation runs at a time per account. And because nobody is at the screen, ask fails the run instead of waiting for an answer.


Batches

For running the same automation with different inputs:

  1. Create an Automation Batch
  2. Add multiple runs with different parameters
  3. Execute the batch
  4. Each run executes sequentially with its own data

Use cases:

  • Process multiple invoices
  • Fill multiple forms
  • Batch data entry

Writing the script

Every automation, whatever its kind, is written in NAL. The language itself — variables, conditions, loops, functions, the page commands, http and api requests, credentials, the data a run is given — is documented in one place: NAL — Nocarta Automation Language. This section covers only how the editor and the assistant work with a script.

The script is interpreted statement by statement by the same engine that powers the assistant chat — one language, one live variable environment shared by the chat, script runs, and the debugger. Anything you can type in the chat you can write in a script, and the other way round.

Editing a Desktop or Windows automation

  1. Navigate to New Automation or edit an existing one
  2. On the Description tab, the kind is Desktop or Windows
  3. Switch to the Editor tab — write your script there
  4. Click Run in Assistant to execute, or Update Automation to save

Browser automations

A Browser automation runs in an isolated sandbox inside your own browser tab — no desktop app, so it is the one kind that works on a phone. It uses the core language with http requests and api calls, exactly as described on the NAL page. Requests go straight from your browser to the target — never through Nocarta’s servers, and never carrying your Nocarta session. Page-driving verbs like open and click need the desktop app; using one here gives you a clear error saying exactly that.

You must list the hosts a script may contact, on the automation’s Settings tab. The list is deliberately strict: an empty list reaches nothing at all, so a script with no configured hosts cannot make a single request. Use exact hostnames (api.example.com) or a wildcard (*.example.com).

Creating a Browser automation

  1. Navigate to New Automation or edit an existing one
  2. On the Description tab, set the kind to Browser
  3. A script editor appears — write your script there (the Check syntax button also flags verbs that need the desktop app)
  4. Click Update Automation to save

Running from Diagram Output

Browser automations can be linked to Notebooks for diagram-to-API automations:

  1. Analyze a diagram in a Notebook (Drawing Translator)
  2. On the diagram output page, link a Browser automation via settings
  3. Open the Convert dropdown and click Run Script
  4. The Execution Monitor opens, showing real-time progress

The script receives the diagram context (nodes, edges, type) automatically, so you can iterate over diagram data and make API calls for each element.

Diagram Context

When running from a Notebook diagram, the script gets a diagram variable with the analysis data:

Expression Returns
diagram.nodes All diagram nodes
diagram.edges All diagram edges
diagram.type Diagram type (flowchart, mind_map, etc.)

Example — create a Trello card for each node in a flowchart:

# Create a Trello card for each diagram node
list_id = "your-trello-list-id"
diagram.nodes.each do |node|
  http post "https://api.trello.com/1/cards?key={{TRELLO.VALUE}}&token={{TRELLO_TOKEN.VALUE}}", {
    body: {name: node.label, idList: list_id}
  }
  log "Created card: #{node.label}"
end

Security: Secret values ({{ALIAS.VALUE}}) are resolved inside the sandbox at the moment the request is sent — they are never stored in the script and never pass through Nocarta’s servers in resolved form. Tier 2 secrets require your vault to be unlocked. A missing credential stops the run with the placeholder’s name; an empty value is never silently sent.

Execution Monitor

When a script runs, the Execution Monitor shows real-time progress:

  • Step list (left panel) — each command with status icon
  • Detail panel (right panel) — request/response details, variables, errors
  • Progress bar — completed vs. total steps
  • Pause — pause before the next step
  • Stop — abort execution
  • Copy the record — export full execution data as JSON

Signing an automation

You can sign an automation with a keypair from your Vault, and Nocarta will tell you if it is ever altered. Create the keypair once (Vault → New Secret → type Keypair), then use Sign on the automation’s Settings tab.

What gets signed is more than the script: it also covers the allowed hosts and the credentials bound to the automation. That matters, because the dangerous change is not always in the code — someone who could add a host to your allow-list could send your credentials somewhere new without touching a line of the script.

  • Signed — it matches what you approved.
  • Changed since signing — something has been altered. Review it and sign again.
  • Signature invalid — the signature no longer belongs to the key it claims. Treat this as suspicious.

Tick Refuse to run if altered and the automation will not run at all while it does not match. Without that, signing still tells you — it just does not stop you.

The private half of your keypair is encrypted with your master password and never leaves your browser, so Nocarta cannot sign anything on your behalf. That is exactly what makes the signature worth something. Signing therefore needs your vault unlocked.

Limitations

  • No page interaction — open, click, fill and the other page verbs need the desktop app; using one here gives a clear error saying so
  • Allowed hosts only — a script reaches nothing until you list its hosts, and nothing outside that list
  • The target must permit browser access — a service that does not send Access-Control-Allow-Origin cannot be read by any browser script. You can still send the request; you just cannot read the reply.
  • 5-minute timeout — scripts must complete within 5 minutes
  • The assistant is not reachable — a Browser automation that uses ask stops with a message saying so; run it as a Desktop automation when you need the assistant. Everything else in the language works here, including checkpoint and on stuck restart (only its with fresh browser option needs the desktop app)

Troubleshooting

Issue Solution
Automation doesn't start Ensure Desktop app is running
Element not found Use get selector to verify the element, or add a sleep before it
Timeout error Increase step/automation timeout
Login fails Verify secret binding and values
Page changed Update selectors using get selector
AI not responding Check AI connection status in the assistant (click the CPU icon)
Recording doesn't capture actions Make sure recording is started before interacting with the page

Quick Reference: Assistant Commands

All commands you can use in the Automation Assistant:

Browser (No AI needed)

  • open "<url>" - Open a webpage
  • back - Go back
  • forward - Go forward
  • refresh - Reload page
  • screenshot - Capture page
  • browser status - Check if browser is open
  • close browser - Close browser window
  • press "Tab" - Send a key where the focus is; press "Enter" in "#search" focuses that element first. Chords (press "Control+A") and Escape/Delete/arrows/F1-F24 included. A word that is not a key name still clicks that button
  • read focus - Read the focused element's text; capture it with x = read focus

Recording (No AI needed)

  • start recording - Begin recording
  • stop recording - Stop recording
  • show recorded actions - List captured actions
  • save as automation - Save recording

Element Selection & Text Search (No AI needed)

  • get selector - Click to pick element
  • click "Submit" - Click first element matching visible text, name, id, or alt (explicit form: click text "Submit")
  • find by text "Dashboard" - Find elements containing text and show their selectors
  • find on selected - Extract elements from your current text selection
  • find input near "Password" - Proximity finder: locate the element (input/button/select/textarea/link/checkbox/radio/field) nearest a landmark (quoted text or a CSS selector) and return its selector. Capture and reuse: x = find input near "Password", then click "#{x}"
  • if exists("#selector") … click "#selector" … end - Click only if the element exists; a missing element is skipped and the run continues (exists() works in any condition)

Find never stops the run on no-match. find column, find by text, find on selected, and find … near … return nil (or an empty array) when nothing matches — guard the result with if, e.g. if x != nil. (Action verbs — click, click text, fill — still fail if their target is missing; wrap a click in if exists("...") … end to make it optional.)

Select Dropdown Options (No AI needed)

  • select "January" from "select[name=\"month\"]" - Pick dropdown option by visible text

Frames, Iframes & Link Discovery (No AI needed)

  • list iframes - Show all iframes/frames in the current context
  • list links - List all links in the current frame with selectors and attributes
  • list links "<keyword>" - Filter links by keyword (text, href, class, name, id)
  • switch to iframe "#id" - Enter an iframe by CSS selector
  • switch to frame "Name" - Enter a frame by name
  • exit iframe - Return one level up
  • highlight "<selector>" - Highlight an element with a red outline
  • highlight "#total" blue - Highlight with a color (red, blue, green, black, white, orange)
  • clear highlights - Remove all highlights

Table Read & Fill (No AI needed)

  • read table - Read table contents with row/col indices
  • read table "#selector" - Read a specific table by CSS selector
  • fill table row 0 col 2 with "value" - Fill input at row 0, col 2 (0-indexed)
  • read table pw / read table pw "#selector" - Playwright-based table read (better for frames)
  • fill table pw row 0 col 2 with "value" - Playwright-based table fill

Direct Click with Selectors (No AI needed)

  • click "#myId" - Click by ID selector
  • click ".myClass" - Click by class selector
  • click "input[type=\"submit\"]" - Click by attribute selector
  • click "div > span" - Click with CSS combinators
  • click "tr:nth-of-type(2)" - Click with pseudo-selectors

Direct Fill with Selectors (No AI needed)

  • fill "input[name=\"x\"]" with "value" - Fill by attribute selector
  • fill "#myId" with "value" - Fill by ID selector
  • fill ".myClass" with "value" - Fill by class selector
  • fill "value" into "input[name=\"x\"]" - Alternative syntax

Secrets (No AI needed)

  • test secret {{ALIAS.VALUE}} - Test secret placeholder resolution
  • fill "{{ALIAS.VALUE}}" into "#password" - Fill with secret placeholder
  • open "https://api.example.com?key={{API.VALUE}}" - Use secrets in URLs

Security: Secrets are resolved on your desktop - they never go through the server during command execution. Tier 2 secrets require your vault to be unlocked.

JavaScript Execution

All JS commands run directly in the browser page — like typing in the Developer Console. Full access to document, window, fetch, and all browser APIs. If you used switch to iframe, JS runs inside that iframe automatically.

  • js [code] - Execute JavaScript in the page
  • js document.title - Single-line JS
  • js fetch('/api?token={{AUTH.VALUE}}') - JS with secret injection
  • js ... end - Multi-line JavaScript block (Shift+Enter for newlines, end to finish)
  • js from ~/script.js - Execute JavaScript from a local file (file stays on your machine)
  • return - Needed as soon as the code is more than a single expression. A one-liner like js document.title gives its value back on its own; two statements without return stop with a SyntaxError

Capture the value with an assignment: title = js document.title. Secrets like {{AUTH.TOKEN}} are resolved locally on your desktop before injection.

Passing your variables into the code

Your script's variables are not visible inside js. The code runs in the page, so it sees the page's own globals and nothing of your script. Writing a variable's name in there sends the name, never the value — and the page answers name is not defined. Open a with block to hand the value over:

name = "Ada"

js with name
  document.querySelector("#greeting").textContent = name
end

List as many as you need: js with first, last. Each one becomes an ordinary JavaScript variable at the top of your code, with its value already in place. with needs the block form — names and code cannot share one line.

Playwright Commands

Try type, fill, press and click first — they cover most typing and clicking, and they take your variables directly (type "#{name}"). Reach for pw when you need something those commands do not offer: it gives you the Playwright page object itself, the same API they use internally.

  • pw page.click('#btn') - Single-line Playwright command
  • pw await page.waitForSelector('.loaded') - Wait for an element
  • pw ... end - Multi-line Nocarta Assistant script block
  • pw from ~/script.js - Execute Nocarta Assistant script from a local file
  • return - Same rule as js: needed as soon as the script is more than a single expression. pw page.title() gives its value back on its own; two statements without return stop with a SyntaxError

The script receives a page argument with the full Playwright Page API. Secrets are resolved before execution.

Your script's variables are not visible inside pw either — the body sees only page and frame. Pass a value in with a with block, exactly as for js:

def type_code(arg)
  pw with arg
    return await page.keyboard.type(arg)
  end
end

Without with, pw page.keyboard.type(arg) fails with arg is not defined: the name reached the page, the value did not. For this particular example type "#{arg}" is simpler — plain commands read your variables without any of this.

Result Variables

  • employees = read table - Capture a command's result in a variable (any command works as the right-hand side of an assignment)
  • show variables - List all variables in the environment
  • clear variables - Reset the variable environment

Reference a captured result by its bare name in expressions (employees.length, cells.includes("Total")) or with #{...} inside command text. Structured results support dot-path and index access: employees.items[0].name. The same variables are live in the chat, script runs, and the debugger.

Page Analysis (AI required)

  • show elements - List all interactive elements with selectors
  • analyze the page - Analyze page structure

Smart Commands (AI required)

  • search and click "<description>" - AI finds and clicks element by description
  • find "the search box" - Find element and show selector
  • fill "<value>" into "<field description>" - Fill a field by description
  • type "<value>" - Type into the focused field
  • Any question or request in natural language

Note: click is always a direct text match (no AI needed). Use search and click for AI-powered element matching. click with CSS selectors (like click "#id" or click ".class") also works without AI.


Tips & Shortcuts

Command History (Arrow Keys)

The assistant remembers the last 50 commands in each chat session. Navigate through them with your keyboard:

Key What It Does
Arrow Up Go to the previous (older) command
Arrow Down Go to the next (newer) command, or return to what you were typing

Tips:

  • Your current draft is preserved - pressing Up then Down returns to what you were typing
  • Consecutive duplicate commands are not stored twice
  • In multi-line inputs, arrow keys only navigate history when the cursor is at the first or last line

Smart Quotes Auto-Correction

When you paste commands from word processors (Microsoft Word, Google Docs, macOS TextEdit), curly "smart" quotes are automatically replaced with straight quotes. This prevents selector and string errors.

If smart quotes are detected, you'll see a notice:

Smart quotes detected and auto-corrected to straight quotes.
Tip: disable smart quotes in your text editor to avoid this.

Characters corrected: \u201C \u201D (double), \u2018 \u2019 (single), \u201E \u201A (low), \u00AB \u00BB (guillemets) - all replaced with standard ASCII quotes.

Multi-Command Execution (Batch Mode)

Send multiple commands at once, separated by ; (semicolon) or newline (Shift+Enter). They execute sequentially, one after another.

Separator How to Type Example
; Type directly on one line switch to frame "Body"; list iframes; list links
Newline Press Shift+Enter Each line becomes a separate command

Example: Navigate into nested frames in one command

switch to frame "Body"; list iframes; switch to iframe "#iFrame_Tab"; list links "employee"

Example: Multi-line batch

switch to frame "Body"
list iframes
switch to iframe "#iFrame_Tab"
list links "employee"
click "a[href*=\"matricola=12345\"]"

How it works:

  • Commands run one at a time, in order, with results shown after each
  • A progress indicator shows [1/4], [2/4], etc.
  • If a command fails, the run stops there and the remaining commands do not run
  • js commands are not split by ; (semicolons are valid JavaScript)

Example: Auto-Login Automation

open "https://app.example.com/login"
fill "#username" with "{{PORTAL.USERNAME}}"
fill "#password" with "{{PORTAL.PASSWORD}}"
click "button[type=\"submit\"]"
validate exists(".dashboard")
screenshot

Or record it:

  1. Type open "app.example.com/login"
  2. Type start recording
  3. Fill in username and password, click login
  4. Type stop recording
  5. Type save as automation
  6. Edit the saved automation to bind secrets for credentials

When an Automation Gets Stuck

Scripts can hit a wall — a page under maintenance, a renamed button, a slow load. Instead of a cryptic failure, you can make automations explain themselves, recover on their own, and ask for help. These are all optional; leave them out and nothing changes.

Checkpoints, stuck, and fail messages

  • checkpoint "login" — mark a point you trust. It shows in the run log and becomes the anchor an auto-restart resumes from. Put checkpoints at the top level (not inside an each/while loop or a def).
  • stuck "Portal is under maintenance", category: "external_outage" — stop the run on purpose with your own message. That message is what the user sees, never a raw error. The category is an optional label.
  • on fail message "Could not open the invoices list" — from this line on, if a command fails the user sees your message instead of the technical error. on fail message off clears it.
  • finish "Nothing to do for this employee" — end the run early as a success, with your message in the run log. Use it for instances the script has nothing to do for (e.g. an empty month): test the precondition at the top, finish, and the rest of the script never runs. The run reports completed, never failed.

Automatic restart

Give a stuck run a second chance without a person watching:

on stuck restart from checkpoint max 2           # resume from the last checkpoint, up to twice
on stuck restart from start max 3 with fresh browser  # start over with a clean browser
on stuck restart from command max 1              # retry just the failing command

Restarts run on your desktop. The automation only reports a final failure once the attempts are used up, and the run log shows each ↻ restart attempt. The built-in restart_attempt variable (0 on the first run, then 1, 2, …) lets a script behave differently on a retry.

Ask the assistant

When a run is stuck, open it and use Ask the assistant for a diagnosis and a suggested fix. You can pick your own configured AI, Nocarta AI, or — in the desktop app — the local Claude Code agent (which runs entirely on your machine and can look at the page screenshot without it ever leaving your computer). If the assistant proposes a change to the script, you can review the diff and apply it; a revertible version of the previous script is saved first.

Recipes: needs-help

When an automation runs as a step inside a Recipe and gets stuck, the whole recipe pauses in a needs help state instead of failing, and you (the owner) get an email. Open the run and choose Retry (run the step again with the same input), Skip (pass its input through unchanged), or Abort (cancel the run) — the same Ask the assistant diagnosis is available there too.

Turning that email off

On the automation’s Settings tab, Email me when a run needs my attention controls that message. It is on for every new automation. Untick it for an automation you watch yourself, or one that stops often enough that the mail stops being useful.

The setting changes who gets told, never what the run does: the recipe still pauses at needs help, the run page still shows the panel, and Retry, Skip and Abort still wait for you. It applies to one automation at a time, so silencing a noisy one leaves the rest alone.

Need More Help?

Ask AI Assistant