# Stepshots > Record interactive product demos with a browser extension or CLI, edit them visually, then share, embed, and track viewer engagement. --- ## [Introduction](https://stepshots.com/docs/getting-started/introduction) ## What is Stepshots? Stepshots is a platform for creating interactive product demos from real browser recordings. Record your app's UI flow with the CLI, upload the result, annotate it with the visual editor, then share or embed it anywhere. > **Tip:** Stepshots captures actual screenshots of your product — not mockups. Your demos always reflect the real UI. ## How It Works 1. **Record** — Capture a flow with the browser extension, or define stable tutorial steps in a config file for headless CLI recording. 2. **Upload** — Push `.stepshot` bundles to the Stepshots dashboard via the CLI or drag-and-drop. 3. **Edit** — Add highlights, blur regions, arrows, hotspots, zooms, and popup presets in the overlay editor. 4. **Share** — Toggle visibility to public and share the viewer link, or embed demos directly in your site. 5. **Analyze** — Track views, step completion, and average time per step with built-in analytics. ## Feature Highlights **CLI Recording** Headless browser recording for stable, scripted flows like docs, marketing pages, and controlled internal demos. **Overlay Editor** Overlay tools for highlight, blur, arrow, hotspot, zoom, and popup presets with full property controls and undo/redo. **Embeddable Player** Embed demos as JS snippets, iframes, React components, or Web Components with theme and autoplay options. **Analytics** View counts, unique visitors, step completion funnels, and per-step duration charts with 7/30/90-day ranges. ## Next Steps **Quickstart** Go from zero to a published demo in 5 minutes. **CLI Installation** Install the Stepshots CLI and prerequisites. **CLI Commands** Full command reference — record, verify, upload, and more. **Embedding** Embed demos in your website or documentation. --- ## [Quickstart](https://stepshots.com/docs/getting-started/quickstart) ## Quickstart This guide walks you through recording your first demo, uploading it, and sharing it. At the end you'll have a published, shareable demo: a sequence of annotated screenshots recorded from your own app, viewable at a public link and ready to embed in your docs or landing page. > **Tip:** There are two ways to record: the **CLI** (this guide) scripts the flow in a config file and replays it in headless Chrome — ideal for stable flows you'll re-record as your UI evolves. The **browser extension** records as you click through your app manually — ideal for quick captures. If you'd rather start with the extension, see [Browser Extension Recording](/docs/guides/browser-extension-demo-recording). ### Prerequisites - Google Chrome or Chromium installed - A Stepshots account - A [Rust toolchain](https://rustup.rs/) — only if you install with `cargo install`; the prebuilt binary needs none ### Steps 1. **Install the CLI** Install the prebuilt binary (macOS Apple Silicon, Linux x86_64/aarch64): ```bash curl -sSL https://raw.githubusercontent.com/hauju/stepshots/main/install.sh | sh ``` Or install with Cargo (any platform with a Rust toolchain): ```bash cargo install stepshots-cli ``` Verify the installation: ```bash stepshots --version ``` 2. **Initialize a config** In your project directory, run: ```bash stepshots init ``` This creates a `stepshots.config.json` with a sample tutorial and a `$schema` reference so your editor autocompletes and validates it. Each step is one action followed by a screenshot. Steps can `click`, `type`, `hover`, `select`, `scroll`, `scroll-to`, `navigate`, `wait`, or press a `key` — enough to script most product flows. Edit the config to point at your app: ```json { "$schema": "https://raw.githubusercontent.com/hauju/stepshots/main/schema/stepshots.config.schema.json", "baseUrl": "https://your-app.com", "viewport": { "width": 1280, "height": 800 }, "format": "desktop", "defaultDelay": 500, "tutorials": { "onboarding": { "url": "/", "title": "Onboarding Flow", "steps": [ { "action": "click", "selector": "button.get-started" }, { "action": "type", "selector": "input[name='email']", "text": "user@example.com" }, { "action": "click", "selector": "button[type='submit']" } ] } } } ``` 3. **Record the demo** ```bash stepshots record ``` The CLI launches headless Chrome, runs the configured flow, and saves screenshots into a `.stepshot` bundle in the `output/` directory. This works best for stable, predictable steps. > **Tip:** Use `stepshots preview onboarding` to watch the recording in a visible browser window before committing. If anything misbehaves, run `stepshots doctor` to check your browser, config, server, and login in one pass. 4. **Log in** ```bash stepshots login ``` This opens your browser to authorize the CLI and stores a token locally. Run `stepshots whoami` to confirm which account you're logged in as. > **Tip:** For CI or scripts, skip `login` and set `STEPSHOTS_TOKEN` instead — see [API Keys](/docs/guides/api-keys) to generate one. 5. **Upload to Stepshots** ```bash stepshots upload output/onboarding.stepshot ``` Add `--public` to publish the demo immediately instead of uploading it as a private draft: ```bash stepshots upload output/onboarding.stepshot --public ``` 6. **Edit and share** Open the Stepshots dashboard, find your demo, and use the overlay editor to add highlights, callouts, or blur regions. When you're ready, toggle visibility to **Public** and copy the viewer link to share it. ## Troubleshooting **A step fails with "selector not found"** — the element wasn't on the page when the step ran. Run `stepshots preview ` to watch the flow in a visible browser and see where it diverges. Slow-loading UI usually needs a `wait` step or a higher `defaultDelay`. **Chrome isn't found** — the CLI looks for Chrome/Chromium at its default install location. If yours lives elsewhere, point at it with `export CHROME_PATH=/path/to/chrome`. **The flow needs a logged-in session** — record against an authenticated app with a persistent browser profile. Log in once with `stepshots browser https://your-app.com --profile-dir .stepshots-profile`, then pass the same `--profile-dir` (or set `STEPSHOTS_PROFILE_DIR`) to `record`, and every recording starts from that session. **Anything else** — `stepshots doctor` checks your browser, config, server reachability, and login in one pass and tells you what's broken. ## What's Next - [Configuration](/docs/cli/configuration) — Full config file reference - [How CLI Recording Works](/docs/cli/recording) — What happens under the hood - [Embedding](/docs/guides/embedding) — Embed your published demo anywhere - [Guided Tours](/docs/guides/live-tours) — Ship in-app onboarding as a text file in your repo, played as an overlay on your real app - [CI Automation](/docs/guides/ci-automation) — Re-record demos automatically in your pipeline --- ## [Installation](https://stepshots.com/docs/getting-started/installation) ## Install the CLI The Stepshots CLI is a single binary. Install the prebuilt binary with the install script, from [crates.io](https://crates.io/crates/stepshots-cli), or from source. ### Quick install (macOS Apple Silicon, Linux x86_64/aarch64) ```bash curl -sSL https://raw.githubusercontent.com/hauju/stepshots/main/install.sh | sh ``` This downloads the latest release binary into `~/.local/bin` (override with `STEPSHOTS_INSTALL_DIR`) and needs no Rust toolchain. Pin a version with `STEPSHOTS_VERSION=v1.0.1`. > **Note:** The install script covers macOS Apple Silicon and Linux (x86_64 / aarch64) only. On macOS Intel or Windows, install with `cargo install stepshots-cli` instead. ### From crates.io ```bash cargo install stepshots-cli ``` If you don't have Rust installed, get it from [rustup.rs](https://rustup.rs/). Rust 1.80+ is required. ### From source ```bash git clone https://github.com/hauju/stepshots.git cd stepshots cargo install --path crates/cli ``` ### Verify ```bash stepshots --version ``` Then run `stepshots doctor` to confirm your browser, config, server, and login are all set: ```bash stepshots doctor ``` ## Prerequisites The CLI uses headless Chrome to record demos, so you need **Google Chrome** or **Chromium** installed on your system. The CLI auto-detects Chrome at its default install location. If Chrome is installed elsewhere, set the `CHROME_PATH` environment variable: ```bash export CHROME_PATH=/path/to/chrome ``` ## Authenticate Log in through your browser — this stores an API token locally: ```bash stepshots login ``` Run `stepshots whoami` to confirm which account you're logged in as. For CI or scripts, set `STEPSHOTS_TOKEN` instead — see [API Keys](/docs/guides/api-keys). ## Upgrade Update to the latest version at any time: ```bash stepshots upgrade ``` `stepshots upgrade` detects how you installed and upgrades the same way — it downloads a fresh prebuilt binary for `install.sh` installs, or runs `cargo install` for Cargo installs. Pass `--check` to only check whether a newer version is available. ## Shell Completion Generate shell completions for `bash`, `zsh`, `fish`, `powershell`, or `elvish` with `stepshots completions `. For example, for fish: ```bash stepshots completions fish > ~/.config/fish/completions/stepshots.fish ``` ## Browser Extension For manual recording (instead of config-based CLI recording), install the Stepshots Chrome extension. ### From the Chrome Web Store [**Add Stepshots Recorder to Chrome**](https://chromewebstore.google.com/detail/stepshots-recorder/jgpahfgfkmojklbiphnfpklnpchkjhpd) — one click, then pin it from the extensions menu. Works in Chrome 114+ and Chromium-based browsers like Edge, Brave, and Arc. ### From source Prefer to build it yourself, or want the latest unreleased changes? 1. Clone the repository and build the extension: ```bash git clone https://github.com/hauju/stepshots.git cd stepshots/extension bun install bun run build ``` 2. Open `chrome://extensions` in Chrome 3. Enable **Developer mode** 4. Click **Load unpacked** and select the `extension/` folder > **Tip:** The CLI is best for stable, scripted recordings. The browser extension is best for quick captures and UI flows you want to record directly from the live product. ## Next Steps - [Quickstart](/docs/getting-started/quickstart) — Record your first demo in 5 minutes - [Configuration](/docs/cli/configuration) — Customize your `stepshots.config.json` --- ## [Recording with the CLI](https://stepshots.com/docs/guides/cli-recording) > **Tip:** The CLI works best for stable, predictable flows like docs, marketing pages, and controlled internal demos. For fast-changing product UI, the browser extension is usually the better starting point. ## Prerequisites Before you start, make sure you have: - Chrome or Chromium browser - A Stepshots account - A Rust toolchain — only if you install with `cargo install`; the prebuilt binary needs none ## Installation Install the prebuilt binary (macOS Apple Silicon, Linux x86_64/aarch64): ```bash curl -sSL https://raw.githubusercontent.com/hauju/stepshots/main/install.sh | sh ``` Or install with Cargo (any platform with a Rust toolchain): ```bash cargo install stepshots-cli ``` See [Installation](/docs/getting-started/installation) for other platforms and the browser prerequisite. ## Initialize Your Project Create a configuration file in your project directory: ```bash stepshots init ``` This generates a `stepshots.config.json` file where you can configure the target URL, viewport size, and other settings. ## Recording a Demo Start a recording session: ```bash stepshots record ``` > **Tip:** The CLI opens a headless Chrome instance and replays the steps from your config. Use stable selectors and explicit waits where needed. The CLI captures screenshots at each step and bundles them into a `.stepshot` file. ## Preview Before Recording Before recording, preview a tutorial to check that every step works. Pass the tutorial key — `preview` replays that tutorial in a **visible** (non-headless) browser so you can watch each action and catch selector issues early: ```bash stepshots preview my-tutorial ``` ## Upload to Stepshots Log in once — this opens your browser and stores a token locally: ```bash stepshots login ``` Then upload your recording, passing the bundle file: ```bash stepshots upload output/my-tutorial.stepshot ``` The CLI uploads the `.stepshot` bundle to your Stepshots account. You'll get a link to view and share your demo. Add `--public` to publish it immediately, or `--demo-id ` to replace an existing demo. In CI, set `STEPSHOTS_TOKEN` instead of running `login`. ## Generate the config from a description (AI-assisted) You don't have to hand-write steps. With an AI coding agent — [Claude Code](https://claude.ai/code) or Codex — and the Stepshots skill, you describe the flow in plain language and the agent inspects the page, picks stable selectors, and writes `stepshots.config.json` for you. Install the skill from the repo: ```bash cp -r skills/stepshots-cli-record ~/.claude/skills/ ``` Then describe what you want to capture, for example: > Record a demo of signing up for my app at `app.example.com`: land on the homepage, click **Get Started**, enter an email, submit, and end on the dashboard. The agent runs `stepshots inspect ` to discover selectors and produces a config like this: ```json { "baseUrl": "https://app.example.com", "viewport": { "width": 1280, "height": 800 }, "tutorials": { "signup-flow": { "url": "/", "title": "Sign up for Example App", "steps": [ { "action": "click", "name": "Get started", "selector": "[data-testid='get-started-btn']", "highlights": [{ "callout": "Start the signup flow", "position": "bottom" }] }, { "action": "type", "name": "Enter email", "selector": "#email", "text": "demo@example.com" }, { "action": "click", "name": "Submit", "selector": "button[type='submit']" }, { "action": "wait", "name": "Dashboard", "selector": ".dashboard", "delay": 1500 } ] } } } ``` Review it, then record as usual: ```bash stepshots record -t signup-flow ``` You stay in control: the agent writes the config, you edit anything you want, and the CLI does the capture — no manual scripting from scratch. ## Record logged-in flows Recordings run in a fresh browser, so sites you're normally signed in to appear logged out. To record an authenticated flow, log in once inside a persistent browser profile, then point recordings at the same profile: ```bash # One-time: opens a visible browser — log in, then press Ctrl+C stepshots browser https://example.com/login --profile-dir ~/.stepshots/profile # Recordings (and preview/inspect) reuse the saved session stepshots record --tutorial my-tutorial --profile-dir ~/.stepshots/profile ``` Set `STEPSHOTS_PROFILE_DIR` to avoid repeating the flag. > **Warning:** Use a **dedicated** profile directory that only Stepshots touches — never point `--profile-dir` at your regular Chrome profile. ## Keep demos fresh with verify Apps drift over time — a redesign can break the selectors your demo relies on. Run `stepshots verify` to replay your tutorials against the live app and report which steps or annotations no longer match, without writing a new bundle: ```bash stepshots verify ``` It's built for CI: exit code 0 means everything is fresh, and `--json` emits a machine-readable report with a repair hint per failure. See the [CI & Automation guide](/docs/guides/ci-automation). ## Tips for Great Demos 1. **Keep the flow stable** — Prefer selectors and states that are unlikely to change often 2. **Keep it focused** — 5-10 steps is the sweet spot for engagement 3. **Use annotations** — Add highlights and callouts in the web editor after uploading 4. **Test before automating** — Preview locally before relying on CI or scripts 5. **Test on mobile** — Preview your demo at different viewport sizes --- ## [Browser Extension Recording](https://stepshots.com/docs/guides/browser-extension-demo-recording) ## Overview Use the Stepshots browser extension to capture a product flow directly from Chrome, then upload the finished recording to Stepshots for editing and sharing. This workflow is best when you want to record a real walkthrough manually instead of defining steps in a CLI config. ## Before You Start - Google Chrome 114 or later - A Stepshots account - The Stepshots browser extension installed from the Chrome Web Store - A Stepshots API key for direct upload > **Note:** Install **Stepshots Recorder** from the [Chrome Web Store](https://chromewebstore.google.com/detail/stepshots-recorder/jgpahfgfkmojklbiphnfpklnpchkjhpd) in one click. Prefer to build it from source? See [Installation](/docs/getting-started/installation#browser-extension) for the unpacked-extension steps. ### Generate an API key Open **Settings** in Stepshots and generate an API key. The browser extension uses this key to upload demos directly to your workspace. > **Tip:** Copy the key when you generate it. The Settings screen warns that it will not be shown again. ## Record a Demo 1. **Open the page you want to record** Navigate to the starting page of your product flow in Chrome. Recording starts from the currently active tab, so make sure you are already on the correct page before you begin. 2. **Open the Stepshots extension** Click the Stepshots extension icon to open the side panel. Enter a title for the demo and, optionally, a short description. 3. **Start recording** Click **Start Recording**. Stepshots begins capturing your interactions on the current site as discrete steps: - Clicks — including clicks on links that navigate to a new page - Typing - Keyboard shortcuts - Dropdown selections The extension doesn't record scrolling as its own step. Instead, it stores the page's scroll position as scene metadata on each click and type step, so the screenshot is framed exactly where you were. 4. **Perform the product flow** Walk through the flow exactly as you want viewers to see it. As you interact with the page, the extension records each action as a separate step and captures screenshots in the background. Need a screenshot without an action — for example a menu you opened by hovering, or a confirmation screen? Click **Capture Screen** in the side panel, or press **Alt+Shift+S** while the page has focus, to add a screenshot-only step. 5. **Pause or stop when needed** Use either the extension side panel or the in-page recording HUD to control the session. You can: - Pause and resume recording - Stop the recording when the flow is complete 6. **Review the recorded steps** After you stop recording, review the captured steps in the extension. For each step you can edit the action, selector, and callout text, drag steps to reorder them, and delete steps you don't want — a deleted step can be brought back with undo. Make these edits before uploading. > **Tip:** Keep the recording focused. Start from the exact page you want, avoid unnecessary clicks, and pause if you need to prepare the next part of the flow. ## Upload to Stepshots ### Add your API key in the extension If you have not already configured the extension, open **Settings** inside the side panel and paste: - Your Stepshots URL - Your Stepshots API key Then save the settings and return to the recording. ### Upload the recording From the finalization screen, click **Upload to Stepshots**. If the upload succeeds, the extension shows a link to open the demo directly in the editor. > **Warning:** Direct upload requires a valid API key. If no API key is configured, the extension will prompt you to open Settings first. ## Export a config for CLI re-recording On the finalization screen, expand **Advanced options** to download a `stepshots.config.json` built from your recorded steps. This turns a one-off manual capture into a repeatable [CLI recording](/docs/guides/cli-recording) — commit the config, then re-record the same flow headlessly whenever your product changes. When the config is generated, link-click steps that changed the page are turned into `navigate` steps automatically. ## Export a guided tour The same panel can download a `*.tour.json` file — a [guided tour](/docs/guides/live-tours) built from your recording, ready to keep in your repo, validate with `stepshots tour check`, and host with `stepshots tour push`. Steps with callout text become the tour's instructions, and the captured element identity becomes each step's drift anchors. The extension is a great tour recorder: it captures selectors from your real, logged-in app — exactly the DOM your users' tours will run on. For the best results, pick **Recording for: Guided tour** on the start screen. In tour mode the step list flags what would weaken the tour while you can still fix it: interactive steps without callout text get a `callout?` chip (they'd be left out of the tour), and steps whose captured selector is fragile get a `fragile` chip with a hint to add an id or `data-testid`. The finalize screen then leads with **Download tour file** and shows exactly how many steps make the tour. Recording a demo instead? The tour download stays available under **Advanced options** either way. ## Finish in the Editor After upload, open the demo in the Stepshots editor to refine it before sharing. Typical finishing steps: - Add callouts and highlights - Blur sensitive UI areas - Adjust the demo title and description - Publish the demo and copy the share link ## Troubleshooting ### Chrome says the page cannot be recorded The extension cannot record Chrome internal pages such as `chrome://`, extension pages, or other browser-managed screens. Navigate to a normal website and start again. ### Sensitive fields are missing That is expected. Password fields and sensitive inputs are intentionally not recorded. ### Upload fails Check that: - Your Stepshots URL is correct - Your API key is valid - You are signed in to the correct Stepshots workspace If the key is invalid, regenerate one in Stepshots Settings and update the extension. ### No screenshots were captured Start a new recording and repeat the flow. The extension only uploads recordings that contain captured screenshots. ## Next Steps - Use the dashboard editor to add overlays and polish the demo - Publish the demo and share the viewer link with your team or customers --- ## [CI & Automation](https://stepshots.com/docs/guides/ci-automation) ## Overview Every Stepshots command runs headless and non-interactive, so the CLI fits cleanly into CI pipelines and agent workflows. This guide covers the pieces that matter for automation: machine-readable output, exit codes, drift checks, and the ready-made GitHub Action. ## Machine-readable output Pass `--json` to any command to get structured output on stdout instead of human-formatted text. It's the same flag everywhere — for AI agents and automation: ```bash stepshots verify --json > report.json stepshots list --json stepshots doctor --json ``` Errors are emitted as JSON too, with an error category, so a pipeline can branch on what went wrong. ## Exit codes The CLI exits `0` on success and maps each failure category to a stable code, so a pipeline can react without parsing text: | Code | Category | Meaning | |------|----------|---------| | `0` | — | Success. | | `1` | config | Config, I/O, upgrade, or other general error. | | `2` | browser | Chrome failed to launch or a CDP call failed. | | `3` | action | A step couldn't execute (e.g. a selector no longer matches). | | `4` | bundle | Failed to build or read the `.stepshot` bundle. | | `5` | upload / auth | Upload failed or authentication was rejected. | > **Note:** These five categories are the whole set — there is no exit code `10` or higher. `stepshots verify` additionally exits non-zero when it finds drift, governed by `--fail-on` (see below). ## Validate before recording `record --dry-run` parses and validates the config and reports what would be recorded without launching a browser — a fast config check for pull requests: ```bash stepshots record --dry-run ``` ## Detect drift with verify `stepshots verify` replays your tutorials against the live app and reports which steps or annotations no longer match, without writing a bundle. It's the check to run on a schedule so a redesign never silently breaks your demos: ```bash stepshots verify --fail-on warn --save-failures ./drift ``` - `--fail-on fail` (default) exits non-zero only when a step can no longer execute. - `--fail-on warn` also fails on annotation drift (an annotation that lost its anchor). - `--save-failures ` writes a screenshot of the page at each failure (defaults to `output/`). Combine it with `--json` for a report carrying a repair hint per failure. ## Keep guided tours honest with tour check `verify` guards your demos; [`stepshots tour check`](/docs/cli/commands#stepshots-tour-check) guards your [guided tours](/docs/guides/live-tours). It replays each `tours/*.tour.json` headless against a deploy, resolving every step the way the player does — **ok** (selector matched), **drift** (only a fallback anchor matched), **fail** (neither): ```bash stepshots tour validate stepshots tour check --url https://staging.example.com --fail-on warn ``` Run both in the same pipeline and selector drift is caught before your users meet it. `--update-fallbacks` refreshes each step's text/aria anchors from the live DOM, so a green run also keeps the tour's drift resilience fresh. ## Check the environment `stepshots doctor` verifies the browser, config, server reachability, and login in one pass. Run it early in a job to fail fast with a clear message when something is misconfigured: ```bash stepshots doctor --json ``` ## Authenticate and publish In CI you can't complete a browser login, so authenticate with an API key via `STEPSHOTS_TOKEN` (generate one from [API Keys](/docs/guides/api-keys)): ```bash export STEPSHOTS_TOKEN=**** stepshots upload output/onboarding.stepshot --public ``` Add `--public` to publish uploaded demos immediately, or `--demo-id ` to replace an existing demo in place. ## Chrome sandbox in CI When the `CI` environment variable is set, the CLI launches Chrome with `--no-sandbox`, `--disable-gpu`, and `--disable-dev-shm-usage`, which is required in most containerized runners. GitHub Actions sets `CI=true` automatically. If you run `stepshots` in another CI system or a container that doesn't set it, export it yourself: ```bash export CI=true ``` ## GitHub Action The repo ships a composite GitHub Action, [`hauju/stepshots`](https://github.com/hauju/stepshots), that installs the CLI, sets up Chrome, and runs `record`, `verify`, or `tour check` for you. Record and publish on every push to `main`: ```yaml - uses: hauju/stepshots@main with: command: record config: demo/stepshots.config.json upload: "true" token: ${{ secrets.STEPSHOTS_TOKEN }} ``` Check demo freshness on a schedule: ```yaml - uses: hauju/stepshots@main with: command: verify config: demo/stepshots.config.json fail-on: warn ``` The `verify` run fails the job when drift is found, writes a freshness summary to the job summary, and leaves `output/verify-report.json` for tooling. Check [guided tours](/docs/guides/live-tours) against a deploy: ```yaml - uses: hauju/stepshots@main with: command: tour-check url: https://staging.example.com fail-on: warn ``` The `tour-check` run replays every `tours/*.tour.json` against `url`, fails the job when a tour breaks (with `fail-on: warn`, also on selector drift), writes a tour freshness summary to the job summary, and leaves `output/tour-check-report.json` for tooling. Add `update-fallbacks: "true"` to refresh each step's text/aria anchors from the live DOM — the rewritten tour files stay in the workspace, so a later step can commit them. ### Inputs | Input | Default | Description | |-------|---------|-------------| | `command` | `record` | `record` (capture bundles), `verify` (drift check, writes no bundles), or `tour-check` (replay tour files against a live deploy). | | `config` | `stepshots.config.json` | Path to the config file. | | `tutorials` | all | Comma-separated tutorial keys to record or verify. | | `output` | `output` | Output directory for bundles (record) or failure screenshots and the report (verify, tour-check). | | `fail-on` | `fail` | verify/tour-check: fail on `fail` (broken steps) or `warn` (also annotation or fallback-anchor drift). | | `url` | — | tour-check only: base URL of the deploy to replay tours against (required). | | `tours` | `tours/` | tour-check only: comma-separated tour files or directories to check. | | `update-fallbacks` | `false` | tour-check only: refresh fallback anchors from the live DOM and rewrite the tour files. | | `upload` | `false` | Upload bundles after recording. | | `token` | — | Stepshots API token (required when `upload` is `true`). | | `server` | `https://stepshots.com` | Server URL. | | `demo-id` | — | Replace an existing demo instead of creating a new one. | | `title` | — | Override the demo title on upload. | | `version` | `main` | Stepshots CLI version (git ref) to install. | ### Outputs | Output | Description | |--------|-------------| | `bundles` | Newline-separated list of recorded `.stepshot` files (record command only). | | `report` | Path to the JSON report (verify and tour-check commands only). | --- ## [MCP — Drive Stepshots from AI Agents](https://stepshots.com/docs/guides/mcp) ## Overview Stepshots speaks the [Model Context Protocol](https://modelcontextprotocol.io) on both ends of the workflow, so an AI agent can go from "record a demo of the signup flow" to a published, analyzed demo without you touching a terminal: | | Local server (`stepshots mcp`) | Hosted server (`stepshots.com/mcp`) | |---|---|---| | Runs | on your machine, inside the open-source CLI | on Stepshots | | Covers | recording, verifying, uploading — needs your repo's config and a local browser | your hosted demos, analytics, tours | | Auth | your existing `stepshots login` | OAuth in the browser, or an API token header | | Plan | free (the CLI is open source) | all plans | Use both together: the local server produces and publishes demos, the hosted one reads and manages what's published. The [MCP launch post](/blog/mcp-server) explains why the workflow has two servers and what shipped in the first release. ## Local server: recording tools The CLI ships an MCP server over stdio. With Claude Code: ```sh claude mcp add stepshots -- stepshots mcp ``` Any MCP client works — configure the command `stepshots mcp` with no arguments: ```json { "mcpServers": { "stepshots": { "command": "stepshots", "args": ["mcp"] } } } ``` Run it from the project directory containing `stepshots.config.json`, or pass `--config`. Tools: | Tool | What it does | |---|---| | `get_schema` | JSON Schema for `stepshots.config.json` (or `*.tour.json` with `kind: "tour"`) — lets the agent write a valid config | | `list_tutorials` | The tutorials defined in the config: key, title, step count | | `record` | Record tutorials into `.stepshot` bundles with headless Chrome | | `verify` | Replay tutorials against the live app and report drift, with a repair hint per failure | | `upload` | Publish bundles to the dashboard, or update an existing demo in place via `demo_id` | `upload` needs a stored login (`stepshots login`) or `STEPSHOTS_TOKEN`. Everything else runs without an account. ## Hosted server: dashboard tools The hosted endpoint lives at `https://stepshots.com/mcp` and authenticates with OAuth — no token pasting: - **claude.ai / Claude desktop**: add a custom connector with the URL `https://stepshots.com/mcp`. Claude discovers the OAuth endpoints, sends you to a consent page, and you approve in the browser. - **Claude Code**: ```sh claude mcp add --transport http stepshots-dashboard https://stepshots.com/mcp ``` The OAuth flow opens in your browser on first use. Alternatively, skip OAuth and pass a token header: `--header "Authorization: Bearer "` with a key from [Settings](/settings) or `stepshots login`. Authorizing an MCP client issues its own access token — your CLI login stays valid. | Tool | What it does | |---|---| | `list_demos` | Your demos: id, title, publish state, views, folder | | `get_demo` | One demo in full, including every step | | `get_demo_analytics` | Views, unique visitors, per-step completion and average duration for one demo | | `get_workspace_analytics` | The dashboard overview as data: totals and per-demo performance | | `set_demo_public` | Publish or unpublish a demo | | `list_tours` | Your [guided tours](/docs/guides/live-tours) | ## What agents can do with this - *"Record the onboarding flow and publish it"* — the agent writes the config (`get_schema`), records (`record`), and uploads (`upload`), all locally. - *"Did the release break any demos?"* — `verify` replays every tutorial and returns exactly which selector drifted and how to fix it. The same check runs in CI — see [CI & Automation](/docs/guides/ci-automation). - *"Where do viewers drop off in the pricing demo?"* — `get_demo_analytics` returns per-step completion; the agent reads the funnel and suggests which step to cut. - *"Unpublish the old demo and publish the new one"* — `list_demos` + `set_demo_public`. --- ## [Embedding Demos](https://stepshots.com/docs/guides/embedding) ## Overview Once you've published a demo, you can embed it anywhere. Stepshots supports four embedding methods — pick the one that fits your stack. ## Embedding Options **React Component** First-class React integration with typed props. **JS Snippet** Drop a script tag and a data attribute. Works anywhere. **Web Component** Native custom element. No framework needed. **iframe** Zero dependencies. Works in any HTML page. > **Tip:** Want to guide users through your **own live app** instead of playing back screenshots? See [Guided Tours](/docs/guides/live-tours) — a tour file scaffolded from the same kind of recording, played as an interactive overlay on your real UI. ## Configuration Options All embedding methods support the same core options: | Option | Type | Default | Description | |--------|------|---------|-------------| | `demoId` | string | required | The unique demo identifier | | `autoplay` | boolean | `false` | Auto-play on load | | `theme` | `"light"` \| `"dark"` | — | Visual theme | | `start` | number | — | Step number to start from (1-indexed) | | `hideControls` | boolean | `false` | Hide playback controls | --- ## React Component For React and Next.js apps, use the **`@stepshots/react`** component — a typed wrapper around the same player: ```bash bun add @stepshots/react ``` ```tsx import { StepshotsDemo } from "@stepshots/react"; function App() { return ; } ``` See the [React SDK](/docs/guides/react-sdk) page for the full prop reference, Next.js setup, theming, and TypeScript types. --- ## JS Snippet Add the script tag to your page and mark containers with a `data-stepshots-demo` attribute: ```html
``` The script auto-discovers all `data-stepshots-demo` elements on the page and renders the player in each one. On the free plan, a small "Made with Stepshots" link is rendered below the player. Upgrading to Pro (custom branding) removes it; if it conflicts with your layout, you can also opt out with `data-attribution="false"` on the container. This attribution link and its opt-out apply only to the JS snippet — the web component and iframe embeds don't inject it. > **Tip:** The JS snippet is ideal for marketing pages, blogs, and documentation sites where you want a quick drop-in embed. --- ## Web Component Load the web component script and use the `` custom element: ```html ``` Set `aspect-ratio` to any CSS `aspect-ratio` value to control the player's shape (defaults to `16/9`). You can style the element like any HTML element: ```html ``` --- ## iframe No scripts required — construct the URL manually: ```html ``` Add query parameters for options: ```html ``` ### Query Parameters | Parameter | Example | |-----------|---------| | `autoplay` | `?autoplay=true` | | `theme` | `?theme=light` | | `start` | `?start=3` | | `hide_controls` | `?hide_controls=true` | --- ## Which Method Should I Use? | Scenario | Recommended | |----------|-------------| | React / Next.js app | React component | | Static marketing site | JS snippet | | Any HTML page, no framework | Web component or iframe | | CMS or restricted HTML (e.g., email) | iframe | | Documentation site (Docusaurus, Mintlify, etc.) | JS snippet or React component | --- ## [Guided Tours](https://stepshots.com/docs/guides/live-tours) ## Overview A **guided tour** is an interactive overlay on your **own running app** — it spotlights the next element and waits for the user to actually click or type it on their own account. Unlike an [embedded demo](/docs/guides/embedding), which plays back screenshots inside a player, a tour drives your real UI. A tour is its own asset with a text-only source format: a `tours/.tour.json` file you keep **in your repo**, next to the UI it targets. A recording is the scaffold, not the source — it contributes the selectors and drift anchors once, then the file is yours to edit, review, and version like any other code. **Runs on your app** Spotlights real elements on your live page — not a screenshot player. **Lives in git** A plain JSON file with a published schema — diffable, reviewable, validated in CI. **Show once** Auto-runs once per visitor by default — ideal for first-run onboarding. **Survives UI drift** Falls back to a step's recorded text when its selector changes. ## Author a tour Scaffold from a recording (the CLI projects the recorded selectors, copy, and fallback anchors into a starting file), or start blank: ```sh stepshots tour init onboarding --from output/onboarding.stepshot # or, from scratch: stepshots tour init onboarding ``` No CLI recording handy? The [browser extension](/docs/guides/browser-extension-demo-recording#export-a-guided-tour) can download a tour file straight from a recording made in your real, logged-in app. Either way you get `tours/onboarding.tour.json` — with editor autocomplete and validation via its `$schema` entry: ```json { "$schema": "https://raw.githubusercontent.com/hauju/stepshots/main/schema/tour.schema.json", "schema": "1", "key": "onboarding", "title": "Getting started", "steps": [ { "selector": "#new-project", "title": "Create your first project", "body": "Everything starts with a project — click here.", "advance": { "type": "click" }, "fallback": { "text": "New project" } } ] } ``` Each step spotlights `selector`, shows `title`/`body` in a callout, and advances on the user's real interaction (`click`, `input`, or `change`). The optional `fallback` anchors let the player find the element by its text or `aria-label` when the selector drifts. Three things make a good tour: - **Record against a fresh account.** Tours run for brand-new users on empty accounts — record the flow in the state your users will be in. - **Keep it to the activation path.** Get the user to their first real win in a handful of steps. Long tours get skipped. - **Write callouts as instructions.** Demo captions describe ("See how easy it is to…"); tour callouts tell the user what to do ("Click **New project**"). > **Tip:** Tag a tutorial with `"target": "tour"` in `stepshots.config.json` and `stepshots record` warns about interactive steps without a callout (they'd be dropped from the tour) and scaffolds the tour file for you after recording. ## Validate it — including in CI ```sh # Static: strict schema check + lints, CI-friendly exit codes stepshots tour validate # Live: replay the tour headless against a real deploy stepshots tour check --url https://staging.example.com ``` `tour check` resolves each step exactly like the player does — selector first, then the fallback anchors — performs the step's action, and moves on. Three outcomes per step: **ok** (selector matched), **drift** (only a fallback anchor matched — fix the selector soon), **fail** (nothing matched). Run it in CI and selector drift is caught before your users meet it. ```sh # Refresh the text/aria fallback anchors from the live DOM stepshots tour check --url https://staging.example.com --update-fallbacks ``` ## Serve it **Hosted (recommended):** push the tour and paste one script tag. Hosting is free with unlimited tours; pushing again overwrites the hosted copy — your git file stays the source of truth. ```sh stepshots tour push ``` ```html ``` On the free plan, hosted tours show a small "Powered by Stepshots" link at the bottom of the callout card; Pro removes it. **Self-hosted:** no account needed. Import the `.tour.json` directly with your bundler and call the [`@stepshots/tour`](https://www.npmjs.com/package/@stepshots/tour) player yourself, or emit a registry script: ```sh stepshots tour build -o public/tours.js ``` Add the snippet to the page(s) where the flow starts — for example, your empty-state dashboard for a "create your first project" tour. ## Localize a tour A tour is a text file in your repo, so translating it is a pull request, not a dashboard project. A translated variant sits next to its base file, shares its `key`, and declares its language: ```sh stepshots tour init onboarding --locale de # → tours/onboarding.de.tour.json, copied from tours/onboarding.tour.json ``` The scaffold copies the base file's structure — selectors, advance rules, check hints — so the only thing to change is the strings: each step's `title` and `body`, and the file's `title`. Then build one registry per language and load the one matching your app's UI language, exactly how the rest of your frontend does i18n: ```sh stepshots tour build -o public/tours.js # default locale stepshots tour build --locale de -o public/tours.de.js ``` A localized build prefers each tour's `de` variant and falls back to the base for tours you haven't translated yet — shipping partially translated is fine. Tour keys stay identical across languages, so `?tour=` links, show-me triggers, and checklist items need no per-locale changes. Two things keep translations honest: - **`tour validate` fails CI when a translation goes stale.** A variant must track its base's structure; when the base gains or loses a step, validation errors on the out-of-sync variant — you find out in the pull request, not from a German user seeing last quarter's onboarding. - **Fallback anchors are per-language.** The drift anchors are the target's visible text — "New project" in English, "Neues Projekt" in German — so refresh each variant's anchors from the deploy it will run on: `stepshots tour check --url --update-fallbacks tours/onboarding.de.tour.json`. Hosted tours currently serve the default locale — `tour push` skips locale variants; serve localized registries yourself with `tour build --locale`. ## Answer your FAQ with tours Tours aren't only for first-run onboarding — any element can become a "show me" launcher. Mark it with `data-stepshots-tour-trigger` and clicking it starts that tour, fresh from step 0: ```html
How do I invite a teammate?

Members live under Settings → Team.

``` With hosted tours the value is the tour id from `stepshots tour push` (one `tour.js` tag serves any number of triggers). Self-hosted, it's the tour `key` from your `window.__STEPSHOTS_TOURS` registry. Triggers are delegated, so FAQ items rendered later — accordions, SPA views — work without extra wiring. A clicked trigger counts as a real run in your analytics; use `?tour=` links to preview without counting. When the flow starts on a different page than the FAQ, add `data-stepshots-tour-url`: the click navigates there first, and the tour starts on arrival (install the snippet globally so the destination page has it too): ```html ``` This turns a help center into self-serve support: instead of describing the flow, the answer walks the user through it in their own account. See [`examples/faq-show-me.html`](https://github.com/hauju/stepshots/blob/main/examples/faq-show-me.html) for a complete page. ## Ship an onboarding checklist Bundle your activation tours into a persistent "Getting started · 2/5" launcher: a corner chip that expands into a checklist, where each item runs a tour and checks off when the user completes it. Progress persists per browser, and once everything is done the panel offers a dismissal that's remembered. With hosted tours it's declarative — list your tour ids on the same script tag: ```html ``` Give an item a `url` when its flow starts on a different page: clicking navigates there first and the tour starts on arrival — so install the snippet globally, the same rule as jump triggers. Checklist runs count as real usage in each tour's analytics funnel. On the free plan the panel and its tours carry the "Powered by Stepshots" link; Pro removes it — same mechanic as everywhere else, decided server-side per response. Self-hosting instead? The player package exports the same widget as [`createChecklist`](https://github.com/hauju/stepshots/tree/main/packages/tour#onboarding-checklist) — you pass the items and your registry directly. ## Converting an existing demo Already have a demo whose steps carry callouts? Open it, choose **Embed → Guided tour → Convert to guided tour**. This materializes the demo's projected tour (including any copy overrides) into a standalone tour you can manage like any other — the demo itself is unchanged. Demo-hosted tours that were enabled before tours became their own asset keep working unchanged. ## Options Set these as attributes on the ` ``` The link is that page's URL plus `?tour=1` (or `?tour=` — equivalent, just explicit). The `?tour=` parameter launches the tour named on the page's script tag, so give each linkable flow's start page a tag naming its own tour. **Self-hosted tours:** build your registry and boot the player globally: ```sh stepshots tour build -o public/tours.js ``` With the registry loaded on every page, `?tour=` launches **any** tour by its key, from any page — `/dashboard?tour=upload-demo`, `/settings?tour=create-api-key`, and so on. If you're deep-linking to many different flows, this is the more flexible setup. ## What the recipient sees A tour link is built to behave well in a support context: - **It always runs.** The once-per-browser flag and any `data-when` gate are ignored — a returning user who saw the onboarding tour months ago still gets the walkthrough. - **It starts fresh at step 0** and advances only on the user's real clicks and typing, so they end up having *done* the thing, not watched it. - **It survives the flow.** Progress is kept per-tab, so page loads and SPA navigations mid-flow resume at the current step instead of starting over. - **It's dismissable.** Skip or Escape ends it — the link never traps anyone. If the target element genuinely isn't there (a permissions gap, a plan limit), the tour shows a friendly "lost the trail" card instead of hanging — a useful signal in itself when a user reports it back. > **Note:** On hosted tours, `?tour=` runs are treated as previews and are **excluded from your tour analytics** — the funnel keeps measuring organic runs only, and support-driven walkthroughs don't inflate it. ## Write the macro Add the link to your canned responses (Zendesk macros, Intercom saved replies, Front templates, plain-text snippets — anywhere). A shape that works: > Happy to help! The quickest way is to let the app walk you through it — open this link and follow the highlights: > > **[Invite a teammate →](https://app.yourproduct.com/settings/team?tour=1)** > > It runs right in your own account, and you can press Escape at any point. If a highlighted button doesn't appear for you, let me know — that usually means a permissions issue on our end. Three guidelines for the tours behind the macros: - **One tour per top ticket driver.** Pull your five most common "how do I…?" tickets and author a `tours/.tour.json` for each — that's an afternoon of work that answers tickets forever after. - **Instruct, don't describe.** Callouts in a support tour are instructions: "Click **Invite member**", not "Here you can see the invite button". - **Keep it to the fix.** Start the link on the page where the flow begins and end the tour at the moment the user's problem is solved. The same tours double as ["Show me" buttons](/docs/guides/live-tours#answer-your-faq-with-tours) in your FAQ and help center — author once, deflect twice. ## Keep the links from rotting A support macro lives in your helpdesk for months; the UI under it keeps changing. Two mechanisms keep old links working: - **Fallback anchors.** Each step carries the target's recorded text and `aria-label`, so a renamed class or reshuffled DOM doesn't break the walkthrough. - **`tour check` in CI.** Replay every tour headless against staging on each deploy, and a selector that stopped resolving fails the build before a customer clicks a dead link: ```sh stepshots tour check --url https://staging.example.com --fail-on warn ``` See [CI & Automation](/docs/guides/ci-automation#keep-guided-tours-honest-with-tour-check) for wiring this into a pipeline (there's a ready-made GitHub Action), and [Guided Tours](/docs/guides/live-tours#tour-analytics) for the Pro drift alerts that flag a tour whose runs keep getting lost in production. --- ## [React SDK](https://stepshots.com/docs/guides/react-sdk) ## Overview `@stepshots/react` is the first-class way to embed a published demo in a React or Next.js app. It's a small, typed component that renders the Stepshots player in a responsive container — the same player every other [embedding method](/docs/guides/embedding) uses, wrapped in a React-friendly API. > **Tip:** The SDK is a thin wrapper around the embed iframe, so it has no heavy runtime dependencies and works with any React 18+ setup. ## Installation ```bash bun add @stepshots/react ``` It also installs cleanly with npm, pnpm, or yarn: ```bash npm install @stepshots/react ``` The package ships ESM and CommonJS builds plus TypeScript types, and declares `react >= 18` as a peer dependency. ## Quick Start Pass the `demoId` of a published demo: ```tsx import { StepshotsDemo } from "@stepshots/react"; function App() { return ; } ``` That's it — the component renders a responsive 16:9 player that fills its container width. ## With Options ```tsx ``` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | `demoId` | `string` | **required** | The ID of the demo to embed. | | `baseUrl` | `string` | `"https://stepshots.com"` | Base URL of the Stepshots app. Override it to point at a self-hosted instance. | | `autoplay` | `boolean` | `false` | Auto-play the demo on load. | | `theme` | `"light" \| "dark"` | — | Force a color theme. Omit to inherit the demo's default. | | `start` | `number` | — | Start at a specific step (1-indexed). Values `≤ 0` are ignored. | | `hideControls` | `boolean` | `false` | Hide the playback controls. | | `width` | `string \| number` | `"100%"` | Container width — any CSS length or a number of pixels. | | `aspectRatio` | `string` | `"16/9"` | Container aspect ratio, as a CSS `aspect-ratio` value. | | `style` | `React.CSSProperties` | — | Extra inline styles merged onto the container. | | `className` | `string` | — | CSS class for the container. | ## Next.js `StepshotsDemo` uses React hooks, so in the Next.js App Router it must run in a Client Component. Add the `"use client"` directive to the file that renders it: ```tsx "use client"; import { StepshotsDemo } from "@stepshots/react"; export function ProductTour() { return ; } ``` You can then drop `` into any Server Component — pages, layouts, or MDX. ## Sizing and styling The component renders the player inside a relatively-positioned container with rounded corners. Control its footprint with `width` and `aspectRatio`, and use `style` or `className` for anything else: ```tsx ``` ## Deep-linking to a step Use `start` to open the demo on a particular step — handy for docs that reference a specific moment in a flow. Steps are 1-indexed: ```tsx ``` ## Self-hosted instances If you run Stepshots on your own domain, point the component at it with `baseUrl`: ```tsx ``` ## TypeScript The props interface is exported alongside the component: ```tsx import { StepshotsDemo, type StepshotsDemoProps } from "@stepshots/react"; const props: StepshotsDemoProps = { demoId: "your-demo-id", theme: "dark" }; ``` ## How it works Under the hood the component builds an embed URL — `{baseUrl}/embed/{demoId}` with `autoplay`, `theme`, `start`, and `hide_controls` query parameters — and renders it in a lazy-loaded, full-screen-capable iframe. It's the same player as the [JS snippet, web component, and iframe](/docs/guides/embedding) embeds, so demos look and behave identically across every method. ## Related - [Embedding Demos](/docs/guides/embedding) — all four embedding methods and when to use each. - [API Keys](/docs/guides/api-keys) — authenticate uploads and access. --- ## [API Keys](https://stepshots.com/docs/guides/api-keys) ## Overview API keys let the Stepshots CLI and browser extension talk to your workspace without an interactive browser login — uploading demos and pushing [guided tours](/docs/guides/live-tours) (`stepshots tour push`). Reach for a key when you're: - Uploading from CI or another headless/automated environment - Uploading demos from the browser extension - Integrating with external tools and workflows > **Tip:** On your own machine you don't need an API key — run `stepshots login` once and the CLI stores a token for you. API keys are for environments where you can't complete a browser login. ## Generate an API Key 1. Sign in to your Stepshots dashboard 2. Open **Settings** 3. Click **Generate API Key** 4. Copy the key immediately > **Warning:** The API key is only shown once. If you lose it, you'll need to generate a new one. ## How Keys Work - Keys start with the `oat_` prefix, so you can recognize them in CI logs and secret stores. - Stepshots stores only a SHA-256 hash of your key, never the plaintext. That's why it can't be shown again after creation. - Each account has **one active API key**. Generating a new key immediately replaces the previous one — any pipeline or extension still using the old key stops working. Treat "Generate API Key" as a rotation, and update your secrets in the same sitting. ## Verify a Key Check that a key is valid and see which account it belongs to: ```bash STEPSHOTS_TOKEN=oat_... stepshots whoami ``` `whoami` calls the server with your token and prints the account it resolves to, along with where the token came from (`--token` / `STEPSHOTS_TOKEN`, or your stored login). This is the fastest way to debug a failing CI upload — run it with the exact token your pipeline uses. ### Token precedence The CLI resolves credentials in this order: 1. `--token` flag 2. `STEPSHOTS_TOKEN` environment variable 3. The token stored by `stepshots login` So a locally logged-in machine and a CI runner with `STEPSHOTS_TOKEN` behave the same way — you never need `login` where a key is set. ## Using Your API Key ### CLI Pass the key directly: ```bash stepshots upload output/my-demo.stepshot --token YOUR_API_KEY ``` Or set it as an environment variable (recommended): ```bash export STEPSHOTS_TOKEN=YOUR_API_KEY stepshots upload output/my-demo.stepshot ``` In a CI pipeline, add `--public` to publish uploaded demos immediately instead of leaving them as private drafts: ```bash stepshots upload output/my-demo.stepshot --public ``` ### Browser Extension 1. Open the Stepshots extension side panel 2. Go to **Settings** 3. Paste your **Stepshots URL** and **API Key** 4. Save The extension uses the key to upload recordings directly to your workspace. ## Environment Variables The CLI recognizes these environment variables: | Variable | Description | |----------|-------------| | `STEPSHOTS_TOKEN` | API key for uploads in CI/headless environments. Optional interactively — the CLI falls back to your `stepshots login` token. | | `STEPSHOTS_SERVER` | Override the server URL (default: `https://stepshots.com`) | | `STEPSHOTS_CONFIG` | Path to the `stepshots.config.json` file (same as `--config`) | | `STEPSHOTS_PROFILE_DIR` | Persistent browser profile directory for authenticated recordings (same as `--profile-dir`) | | `CHROME_PATH` | Path to Chrome/Chromium binary (if not in the default location) | ## Using Keys in CI Store the key as a secret in your CI provider (e.g. a GitHub Actions repository secret named `STEPSHOTS_TOKEN`) and expose it to the upload step as an environment variable. Never commit a key to the repository or echo it in logs. For the full pipeline — validating configs, recording headlessly, drift detection with `verify`, and the ready-made GitHub Action — see [CI Automation](/docs/guides/ci-automation). ## Security Best Practices - **Scope by environment**: use your personal login on your laptop and reserve the API key for CI and the extension, so revoking the key never locks you out locally. - **Rotate after exposure**: if a key lands in a log, a screenshot, or a shared terminal, generate a new one — rotation is instant and the old key dies with it. - **Prefer secrets over env files**: `.env` files get committed by accident; CI secret stores and password managers don't. ## Revoking a Key To revoke an API key, open **Settings** in the dashboard and delete the key. Any CLI or extension using that key will immediately lose upload access. Generate a new key and update your CI secrets or extension settings. ## Troubleshooting **Upload fails with 401 Unauthorized** — the key was revoked or replaced by a newer one. Run `STEPSHOTS_TOKEN=... stepshots whoami` with the exact token from your secret store; if it fails, generate a fresh key and update the secret. **`Not logged in` error despite setting a key** — the variable isn't reaching the CLI process. Confirm it's exported in the same shell (`echo ${STEPSHOTS_TOKEN:+set}`) or passed into the CI step's `env:` block. **Extension uploads fail after key rotation** — the extension keeps its own copy of the key. Open the side panel **Settings** and paste the new key there too. --- ## [Configuration](https://stepshots.com/docs/cli/configuration) ## Configuration The CLI reads its settings from a `stepshots.config.json` file. Run `stepshots init` to generate a starter config, then customize it for your app. > **Tip:** The generated config carries a `$schema` reference, so editors with JSON Schema support (VS Code, JetBrains, Zed, …) autocomplete and validate every field as you type. Print the schema directly with `stepshots schema`. ### Config File Location The CLI searches for the config file in this order: 1. Explicit `--config path/to/config.json` flag 2. `STEPSHOTS_CONFIG` environment variable 3. Walk up from the current directory looking for `stepshots.config.json` ### Config Structure ```json { "$schema": "https://raw.githubusercontent.com/hauju/stepshots/main/schema/stepshots.config.schema.json", "baseUrl": "https://your-app.com", "viewport": { "width": 1280, "height": 800 }, "format": "desktop", "defaultDelay": 500, "tutorials": { "tutorial-key": { "url": "/start-page", "title": "Tutorial Title", "description": "Optional description", "steps": [ { "action": "click", "name": "Start the flow", "selector": "button.cta", "highlights": [ { "showBorder": true, "callout": "Click here to begin", "position": "bottom" } ] } ] } } } ``` ### Top-Level Fields | Field | Type | Default | Description | |-------|------|---------|-------------| | `$schema` | `string` | — | JSON Schema reference for editor autocomplete and validation. `stepshots init` writes this for you. | | `baseUrl` | `string` | — | Base URL prepended to every relative tutorial and step URL. Required. | | `viewport` | `object` | `{ width: 1280, height: 800 }` | Browser viewport in CSS pixels. Ignored when `format` names a preset. | | `format` | `string` | — | Named viewport preset (see below). Overrides `viewport` unless set to `custom`. | | `theme` | `string` | browser default | Color scheme for recording: `light` or `dark`. | | `defaultDelay` | `number` | `500` | Milliseconds to wait after each action before capturing, unless a step sets its own `delay`. | | `tutorials` | `object` | — | Map of tutorial key → tutorial config. Required. | #### Format presets `format` sets the viewport to a named preset and takes precedence over `viewport` (unless `custom`): `desktop-hd`, `desktop`, `tablet-landscape`, `tablet-portrait`, `mobile`, `mobile-landscape`, `square`, `custom`. #### Viewport | Field | Type | Description | |-------|------|-------------| | `width` | `number` | Width in CSS pixels. | | `height` | `number` | Height in CSS pixels. | | `deviceScaleFactor` | `number` | Device pixel ratio, e.g. `2.0` for retina-quality screenshots. | ### Tutorial Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `url` | `string` | Yes | Start page — absolute, or relative to `baseUrl`. | | `title` | `string` | Yes | Display title, also used as the demo title on upload. | | `description` | `string` | No | Longer description shown in the dashboard. | | `steps` | `array` | Yes | Ordered actions; each step becomes one screenshot. | ### Step Fields | Field | Type | Applies to | Description | |-------|------|------------|-------------| | `action` | `string` | all | One of `click`, `type`, `key`, `scroll`, `scroll-to`, `hover`, `navigate`, `wait`, `select`. Required. | | `name` | `string` | any | Optional label shown in progress output and the dashboard. | | `selector` | `string` | most | CSS selector for the target element. Required for `click`, `type`, `hover`, `select`. | | `text` | `string` | `type` | Text to enter. Supports `${ENV_VAR}` interpolation. | | `url` | `string` | `navigate` | Target URL — absolute or relative to `baseUrl`. | | `key` | `string` | `key` | Key to press, e.g. `"Enter"` or `"Escape"`. | | `value` | `string` | `select` | Option value to choose. | | `delay` | `number` | any | Milliseconds to wait after this action, overriding `defaultDelay`. | | `scrollX` / `scrollY` | `number` | `scroll` | Scroll distance in pixels. | | `sceneScrollX` / `sceneScrollY` | `number` | any | Scroll position the page is restored to **before** this step's capture — needed for below-the-fold click scenes. | | `highlightSelector` | `string` | any | Selector used only to resolve highlight bounds, so an action step can target one element while highlighting another. | | `highlights` | `array` | any | Highlight annotations (see below). | | `hotspots` | `array` | any | Pulsing hotspot markers. | | `popups` | `array` | any | Popup cards anchored to elements. | | `arrows` | `array` | any | Arrows drawn between two elements. | | `blurRegions` | `array` | any | Regions blurred to hide sensitive data. | | `zoomRegions` | `array` | any | Regions the viewer zooms into. | > **Tip:** `${ENV_VAR}` interpolation in `text` lets you fill forms from environment variables so you never commit credentials to the config. ### Annotations Annotations in the config are hints applied during recording. You can further edit them in the dashboard editor. #### Highlights | Field | Type | Description | |-------|------|-------------| | `callout` | `string` | Callout text next to the highlight. | | `position` | `string` | Callout position: `top`, `bottom`, `left`, `right`. | | `color` | `string` | Border/callout color as a CSS color. | | `showBorder` | `boolean` | Draw a border around the element (default `true`). | | `arrow` | `boolean` | Draw an arrow from the callout to the element. | | `bounds` | `object` | Explicit pixel bounds (`x`, `y`, `width`, `height`) — wins over selector resolution when set. | #### Hotspots | Field | Type | Description | |-------|------|-------------| | `selector` | `string` | Element the hotspot is centered on. Required. | | `callout` | `string` | Callout text next to the hotspot. | | `position` | `string` | Callout position: `top`, `bottom`, `left`, `right`. | | `color` | `string` | Hotspot color. | | `size` | `number` | Hotspot diameter in pixels. | | `isClickTarget` | `boolean` | Whether clicking the hotspot advances the demo. | #### Popups | Field | Type | Description | |-------|------|-------------| | `selector` | `string` | Element the popup is anchored to. Required. | | `body` | `string` | Popup body text. Required. | | `title` | `string` | Popup heading. | | `buttonText` / `buttonUrl` | `string` | Optional button label and link. | | `openInNewTab` | `boolean` | Whether the button opens in a new tab. | | `color` / `textColor` | `string` | Background and text colors. | | `size` | `string` | Size token: `xs`, `sm`, `md`, `lg`. | | `style` | `string` | `card` (default) or `button`. | | `variant` | `string` | Visual variant: `primary`, `secondary`, `ghost`, etc. | | `width` | `number` | Popup width in pixels. | #### Arrows | Field | Type | Description | |-------|------|-------------| | `fromSelector` | `string` | Element the arrow starts from. Required. | | `toSelector` | `string` | Element the arrow points to. Required. | | `color` | `string` | Arrow color. | | `curvature` | `number` | Curve amount from `-1.0` (bend left) to `1.0` (bend right); `0` is straight. | | `strokeWidth` | `number` | Stroke width in pixels. | #### Blur regions | Field | Type | Description | |-------|------|-------------| | `selector` | `string` | Element to blur. Required. | #### Zoom regions | Field | Type | Description | |-------|------|-------------| | `selector` | `string` | Element to zoom into. Required. | | `magnification` | `number` | Zoom factor, e.g. `2.0` for 2x. | | `duration` | `number` | Zoom animation duration in milliseconds. | | `delay` | `number` | Zoom animation start delay in milliseconds. | ### Print the schema `stepshots schema` prints the complete JSON Schema — the source of truth for every field above. Pipe it to a file if you want to point tooling at a local copy: ```bash stepshots schema > stepshots.schema.json ``` --- ## [How CLI Recording Works](https://stepshots.com/docs/cli/recording) ## How CLI Recording Works The `stepshots record` command automates browser interaction to capture screenshots for each step in your tutorials. ### How It Works 1. The CLI launches **headless Chrome** via the Chrome DevTools Protocol (CDP). 2. It sets the viewport to the dimensions specified in your config (default: 1280×800). 3. For each tutorial, it navigates to the starting URL. 4. For each step, it executes the action (click, type, navigate, etc.), waits for the configured delay, then captures a screenshot of the viewport. For content below the fold, set `sceneScrollY` on the step (see [Configuration](/docs/cli/configuration)). 5. If a click opens a link in a new tab (`target="_blank"`), the recorder follows it automatically and continues the flow there. 6. Screenshots are bundled into a `.stepshot` file (a zip archive with a `manifest.json` and WebP images). ### Basic Usage Record all tutorials: ```bash stepshots record ``` Record specific tutorials by key (positional or repeatable `--tutorial`): ```bash stepshots record onboarding checkout stepshots record --tutorial onboarding --tutorial checkout ``` Change the output directory: ```bash stepshots record --output ./recordings ``` ### Dry Run See what would be recorded without launching Chrome: ```bash stepshots record --dry-run ``` ### Previewing Preview a tutorial in a visible (non-headless) browser window to verify your steps work correctly: ```bash stepshots preview my-tutorial ``` > **Tip:** Always preview before recording. This lets you see exactly what Chrome does at each step and catch selector issues early. ### Recording Logged-In Flows Recordings run in a fresh headless browser, so sites you're normally signed in to appear logged out. Log in once inside a persistent browser profile, then point recordings at it: ```bash # One-time: opens a visible browser — log in, then press Ctrl+C stepshots browser https://example.com/login --profile-dir ~/.stepshots/profile # Recordings reuse the saved session stepshots record my-tutorial --profile-dir ~/.stepshots/profile ``` Set `STEPSHOTS_PROFILE_DIR` to avoid repeating the flag. Use a dedicated profile directory — never your regular Chrome profile. See the [CLI recording guide](/docs/guides/cli-recording) for the full workflow. ### Keeping Demos Fresh When the recorded app's UI changes, `stepshots verify` replays your tutorials against the live pages without writing bundles and reports broken steps — useful locally or [in CI](/docs/guides/ci-automation). ### Troubleshooting **Chrome not found** — Make sure Google Chrome or Chromium is installed and on your `PATH`. The CLI looks for Chrome at its default install location for your OS. **Selector not found** — The step will fail with an error message showing the selector that couldn't be found, and a debug screenshot of the failing state is saved next to the would-be bundle (`.failed-step-.png`). Use `stepshots preview` to debug interactively. **Timeout waiting for selector** — The `wait` action waits up to 10 seconds for an element to appear. If your page takes longer to load, add a `delay` to the previous step. **Screenshots look wrong** — Check your `viewport` dimensions. Some responsive layouts behave differently at different sizes. --- ## [Commands](https://stepshots.com/docs/cli/commands) ## Commands The Stepshots CLI (`stepshots`) groups its commands into authentication, config, recording, publishing, and utilities. Run `stepshots help ` (or `stepshots --help`) for the authoritative flag list of any command. ### Global Flags Every command accepts these flags: | Flag | Description | |------|-------------| | `--config ` | Path to the config file (default: auto-detect `stepshots.config.json`). Also `STEPSHOTS_CONFIG`. | | `--json` | Output results as JSON to stdout — for AI agents and automation. | | `--verbose`, `-v` | Enable verbose/debug logging. | | `--version`, `-V` | Print the version. | | `--help`, `-h` | Print help. | > **Tip:** Pass `--json` to any command for machine-readable output in CI pipelines and AI agent workflows. See the [CI & Automation guide](/docs/guides/ci-automation). --- ## Authentication ### `stepshots login` Log in via your browser and store an API token locally. ```bash stepshots login ``` | Flag | Default | Description | |------|---------|-------------| | `--server` | `https://stepshots.com` | Server URL. Also `STEPSHOTS_SERVER`. | ### `stepshots logout` Remove the locally stored Stepshots credentials. ```bash stepshots logout ``` ### `stepshots whoami` Show which account you're logged in as. ```bash stepshots whoami ``` | Flag | Default | Description | |------|---------|-------------| | `--server` | `https://stepshots.com` | Server URL. Also `STEPSHOTS_SERVER`. | | `--token` | Stored login token | API token. Also `STEPSHOTS_TOKEN`. | --- ## Config ### `stepshots init` Generate a sample `stepshots.config.json` in the current directory. ```bash stepshots init ``` | Flag | Description | |------|-------------| | `--force` | Overwrite an existing config file. | ### `stepshots schema` Print the JSON Schema for `stepshots.config.json` — useful for validation tooling or wiring editor autocomplete. ```bash stepshots schema > stepshots.schema.json ``` ### `stepshots list` List the tutorials defined in the config. ```bash stepshots list ``` --- ## Recording ### `stepshots record` Record tutorials into `.stepshot` bundles. Records all tutorials if none are named. ```bash stepshots record onboarding checkout ``` | Flag | Default | Description | |------|---------|-------------| | `[TUTORIAL]...` | All | Positional tutorial keys to record. | | `--tutorial`, `-t` | All | Tutorial to record (same as the positional argument). Repeatable. | | `--output`, `-o` | `output/` | Output directory for `.stepshot` files. | | `--dry-run` | `false` | Validate and show what would be recorded without launching a browser. | | `--profile-dir` | — | Persistent browser profile directory (for authenticated recordings). Also `STEPSHOTS_PROFILE_DIR`. | ### `stepshots preview` Replay a tutorial in a visible (non-headless) browser to check your steps. Takes the tutorial key as an argument. ```bash stepshots preview onboarding ``` | Flag | Description | |------|-------------| | `--profile-dir` | Persistent browser profile directory (for authenticated recordings). Also `STEPSHOTS_PROFILE_DIR`. | ### `stepshots verify` Replay tutorials against the live app and report drift, without writing any bundle. Verifies all tutorials if none are named. ```bash stepshots verify --fail-on warn ``` | Flag | Default | Description | |------|---------|-------------| | `[TUTORIAL]...` | All | Positional tutorial keys to verify. | | `--tutorial`, `-t` | All | Tutorial to verify (same as the positional argument). Repeatable. | | `--fail-on` | `fail` | Exit non-zero on `fail` (broken steps) or `warn` (also annotation drift). | | `--save-failures` | `output/` | Directory for failure screenshots. | | `--profile-dir` | — | Persistent browser profile directory (for authenticated flows). Also `STEPSHOTS_PROFILE_DIR`. | ### `stepshots inspect` Inspect a page to discover interactive elements and CSS selectors. Defaults to the config `baseUrl` if no URL is given. ```bash stepshots inspect https://example.com/pricing ``` | Flag | Default | Description | |------|---------|-------------| | `--width` | `1280` | Viewport width. | | `--height` | `800` | Viewport height. | | `--profile-dir` | — | Persistent browser profile directory (for authenticated pages). Also `STEPSHOTS_PROFILE_DIR`. | ### `stepshots browser` Open a visible browser with a saved profile so you can log in to sites used by authenticated recordings. The `--profile-dir` flag is required; the URL is optional. ```bash stepshots browser https://example.com/login --profile-dir ~/.stepshots/profile ``` | Flag | Description | |------|-------------| | `--profile-dir` | Persistent browser profile directory to create or reuse (required). Also `STEPSHOTS_PROFILE_DIR`. | --- ## Publishing ### `stepshots upload` Upload `.stepshot` bundles to the Stepshots API. ```bash stepshots upload output/onboarding.stepshot --public ``` | Flag | Default | Description | |------|---------|-------------| | `[FILES]...` | — | `.stepshot` files to upload. | | `--title` | — | Override the demo title. | | `--demo-id` | — | Replace an existing demo instead of creating a new one. | | `--public` | `false` | Make new demos publicly viewable immediately (ignored with `--demo-id`). | | `--server` | `https://stepshots.com` | Server URL. Also `STEPSHOTS_SERVER`. | | `--token` | Stored login token | API token. Also `STEPSHOTS_TOKEN`. | > **Note:** `--token` is **not** required. When it's omitted, `upload` falls back to the token stored by `stepshots login`. Supply `--token` (or `STEPSHOTS_TOKEN`) only for CI or headless environments where you haven't run `login`. --- ## Guided tours Work with guided-tour source files (`*.tour.json`) — the git-versioned asset behind [Guided Tours](/docs/guides/live-tours). Paths default to the `tours/` directory when omitted. ### `stepshots tour init` Scaffold a tour source file at `tours/.tour.json` — blank, or projected once from a recorded bundle (selectors and fallback anchors carry over). ```bash stepshots tour init onboarding --from output/onboarding.stepshot ``` | Flag | Default | Description | |------|---------|-------------| | `` | — | Tour key: the `?tour=` URL parameter and registry key (required). | | `--from` | — | Project a recorded `.stepshot` bundle into the scaffold. | | `--output`, `-o` | `tours/.tour.json` | Output file. | | `--force` | `false` | Overwrite an existing tour file. | ### `stepshots tour validate` Statically validate tour files: strict schema parse plus lints (empty copy, duplicate keys, misplaced check hints). Exits non-zero on errors — CI-friendly. ```bash stepshots tour validate ``` ### `stepshots tour check` Replay tour files headless against a live app and report selector drift. Each step resolves exactly like the player (selector first, then fallback anchors) and its advance action is performed: **ok** = selector matched, **drift** = only a fallback anchor matched, **fail** = neither. ```bash stepshots tour check --url https://staging.example.com --fail-on warn ``` | Flag | Default | Description | |------|---------|-------------| | `[PATH]...` | `tours/` | Tour files or directories. | | `--url` | — | Base URL of the app to replay against (required). | | `--fail-on` | `fail` | Exit non-zero on `fail` (broken steps) or `warn` (also fallback drift). | | `--update-fallbacks` | `false` | Rewrite each selector-resolved step's fallback anchors from the live DOM. | | `--profile-dir` | — | Persistent browser profile directory (for authenticated apps). Also `STEPSHOTS_PROFILE_DIR`. | ### `stepshots tour build` Merge tour files into a `window.__STEPSHOTS_TOURS` registry script for script-tag installs. Bundler users can import the `.tour.json` files directly instead. ```bash stepshots tour build -o public/tours.js ``` ### `stepshots tour push` Push tour files to Stepshots hosting: upserts by key, prints the track URL and embed snippet. One-way sync — your git files stay the source of truth; pushing overwrites the hosted copy. ```bash stepshots tour push ``` | Flag | Default | Description | |------|---------|-------------| | `[PATH]...` | `tours/` | Tour files or directories. | | `--server` | `https://stepshots.com` | Server URL. Also `STEPSHOTS_SERVER`. | | `--token` | Stored login token | API token. Also `STEPSHOTS_TOKEN`. | ### `stepshots tour schema` Print the JSON Schema for `*.tour.json` files (the one `$schema` entries point at). > **Note:** `stepshots tour export` is deprecated: use `tour init --from ` to scaffold a source file, or `tour build` for the registry script. --- ## Utilities ### `stepshots doctor` Check your setup — browser, config, server reachability, and login — in one pass. Run it first when something misbehaves. ```bash stepshots doctor ``` | Flag | Default | Description | |------|---------|-------------| | `--server` | `https://stepshots.com` | Server URL. Also `STEPSHOTS_SERVER`. | ### `stepshots completions` Generate shell completions. ```bash stepshots completions fish > ~/.config/fish/completions/stepshots.fish ``` | Argument | Description | |----------|-------------| | `` | One of `bash`, `zsh`, `fish`, `powershell`, `elvish`. | ### `stepshots upgrade` Upgrade `stepshots` in place using however you installed it. ```bash stepshots upgrade --check ``` | Flag | Description | |------|-------------| | `--check` | Only check for updates without installing. | | `--force` | Force reinstall even if already on the latest version. | ### `stepshots serve` Start a local HTTP server for browser extension integration. ```bash stepshots serve --port 8124 ``` | Flag | Default | Description | |------|---------|-------------| | `--port`, `-p` | `8124` | Port to listen on. | | `--output`, `-o` | `output/` | Output directory for recorded bundles. |