# Getting started

> From nothing to a passing flow: start a local hub with the fixture app, record a session, cut four elements, write a flow and run it from the CLI.

This guide takes you from nothing to a flow that passes. You start a local hub with the fixture app (a small web app made for testing), record yourself signing in to it, cut four elements from the recording, write a flow that uses them, and run it from the command line. It takes about fifteen minutes.

## What you need

- A checkout of the attention.run repository. The CLI and the SDK aren't published to npm yet, so everything here runs from it.
- Node.js 24 or newer, Rust 1.85 or newer (to build the native matcher), and Google Chrome. The matcher is built and tested on macOS on Apple Silicon; the hosted app's Linux image builds it too.
- Docker only if you want a desktop over VNC as a second target (`npm run desktop`).

The hosted app at [app.attention.run](https://app.attention.run) is an invite-only preview, and its API can't be reached by the CLI from outside yet. The steps below run the same product on your machine, so the addresses in this guide are local ones.

## 1. Start the demo

In the checkout:

```sh
npm install
npm run tm:build
npm run demo
```

`npm run tm:build` builds the native matcher once. `npm run demo` starts, and stops on Ctrl+C:

| What | Where |
|---|---|
| The hub and a runner (local Chrome) | http://127.0.0.1:7300 |
| The fixture app, the demo's target (sign in with demo / demo) | http://127.0.0.1:7310 |
| The web app: open this | http://localhost:5173 |
| An SSH server for SSH targets | 127.0.0.1:7322 |

It adds a Chrome target named "Fixture app" that opens the fixture's sign-in page. Data is kept in `./attn-data`, so what you make survives a restart.

To use the CLI from the checkout, run it with `npx attn`, or make `attn` an alias for this terminal:

```sh
alias attn='npx attn'
attn status
```

## 2. Record a session

1. In the web app, open **Sessions**, click **New session**, pick the target "Fixture app" (keep **Record** on) and click **Start session**. The live view mirrors its screen; your clicks and typing go to it.
2. Sign in: click the Username field and type `demo`, click the Password field and type `demo`, then click **Sign in**. The page says "Welcome, demo".
3. Click **End session**. The recording keeps every frame and every input on one clock.

## 3. Cut four elements

An element is a named piece of the screen that attention.run finds by its pixels. Open the recording from **Sessions** (**Open the recording**). Its clicks panel lists your clicks: **Cut…** on one opens the cutter on the frame just before it. For a frame without a click, scrub the timeline and use **Cut at this frame…**. Draw a rectangle, name the element and save it. The cutter checks the cut against the whole recording and says whether it is unique.

Cut these four, named exactly like this, because the flow below uses the names:

| Element | What to cut |
|---|---|
| `login/username` | The "Username" label and its empty field together. Both fields look alike while empty; with their labels they don't. |
| `login/password` | The "Password" label and its empty field. |
| `login/sign-in` | The Sign in button. |
| `welcome/heading` | The "Welcome, demo" heading, on a frame after you signed in (**Cut at this frame…**). |

When you cut from a click, the click's position inside the rectangle becomes the element's click point, so a flow that types "into" `login/username` clicks the field, not the label.

## 4. Write a flow

A flow is JSON: a slug, optional parameters, and steps that name elements. Save this as `sign-in.json`:

```json
{
  "flow": "fixture/sign-in",
  "params": { "user": { "type": "string", "default": "demo" } },
  "steps": [
    { "navigate": "http://127.0.0.1:7310/login" },
    { "type": { "param": "user" }, "into": "login/username" },
    { "type": "demo", "into": "login/password" },
    { "click": "login/sign-in" },
    { "waitFor": { "visible": "welcome/heading" }, "timeout": 5000 },
    { "assert": { "gone": "login/sign-in" } }
  ]
}
```

Each step that names an element first waits for it to be on screen (10 seconds unless the step or the flow says otherwise), so there are no sleeps. A `waitFor` after an action only looks at frames from after that action, so a heading that was already there doesn't count. Every field is in the [flow JSON reference](https://attention.run/docs/flow-json).

The recording's **Create flow** button drafts a flow like this one from your clicks and typing, with the elements you cut. This guide writes it by hand so you see every step.

## 5. Run it

Push the flow to the hub, then run it on the target and wait for the result:

```sh
export ATTN_HUB=http://127.0.0.1:7300
attn flows push sign-in.json
attn run fixture/sign-in --target "Fixture app" --wait
```

`attn flows push` checks the file before it sends it, and the hub checks that the elements exist in the library. `attn run --wait` prints a line per step as it ends, then a summary, and exits 0 when the run passed (1 failed, 2 error, 3 cancelled), so the same command works in CI:

```text
fixture/sign-in v1 (new flow)
run_…: fixture/sign-in v1 on Fixture app (tgt_…)
✓ steps[0] navigate http://127.0.0.1:7310/login (25 ms)
✓ steps[1] type params.user into login/username (69 ms)
✓ steps[2] type "demo" into login/password (94 ms)
✓ steps[3] click login/sign-in (34 ms)
✓ steps[4] waitFor visible welcome/heading (55 ms)
✓ steps[5] assert gone login/sign-in (10 ms)
passed: fixture/sign-in v1 in 787 ms (6 passed, 0 failed, 0 skipped)
```

The same flow runs from the web app: **Flows** shows it; open it for the visual editor and the JSON, and **Run…** to run it on a target and watch each step on the live screen. A run that fails shows the frame at the failure and the best candidate it found, so you can see whether the screen changed or the element needs another variant.

## Next

- [Concepts](https://attention.run/docs/concepts): what targets, sessions, elements, flows, runs and triggers are.
- [The attn CLI](https://attention.run/docs/cli) and [the TypeScript SDK](https://attention.run/docs/sdk): drive the hub from scripts and CI.
- [SSH targets](https://attention.run/docs/ssh-targets): your own desktop or Chrome as a target.
- [Webhooks](https://attention.run/docs/webhooks): start this flow from another system, and hear back when it ends.

---

Canonical: https://attention.run/docs/getting-started · Updated 2026-10-15
