loading

Getting Started

  • Overview
  • Quick Start
  • Instant Preview
  • Try on Other Sites
  • Workspace

Integration

  • Configuration
  • Security & Policies
  • AI Discovery
  • Agent-to-Web (A2W)

Reference

  • API Reference
  • Instant Preview API
  • Examples
  • Roadmap

Platforms

  • Webflow
  • Wix

[ documentation ]

Examples

Canonical install plus advanced runtime override examples for specialized sites.

The generated production install is the single script tag. Inline rover('boot') snippets below are advanced/legacy overrides for teams that intentionally manage config in code. Rover v2 task boundaries are strict: normal sends always start a fresh task boundary. Only ask_user answers continue the current boundary.

Canonical Setup

Default production install. Runtime policy, Activity, discovery, and billing config come from the Rover site record.

<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="my-site"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Journeys + Async Identify

Blend workspace journeys with runtime overrides and personalize greeting after login.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'my-app',
    publicKey: 'pk_site_your_public_key',
    siteKeyId: 'your-site-key-id',
    allowedDomains: ['app.example.com'],
    // Workspace journeys are fetched by default via /v2/rover/session/open.
    // Optional override in boot config:
    ui: {
      shortcuts: [
        { id: 'onboard', label: 'Onboard me', prompt: 'Guide me through onboarding', routing: 'planner', enabled: true }
      ],
      greeting: { text: 'Hey {name}! Need setup help?', delay: 2000, duration: 8000 }
    },
  });

  // Later, when auth completes:
  rover('identify', { name: 'Alex', email: 'alex@example.com' });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Voice Dictation

Enable browser dictation so visitors can speak requests, edit the transcript, and manually send.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'my-app',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['app.example.com'],
    ui: {
      voice: {
        enabled: true,
        language: 'en-US',
        autoStopMs: 2600
      }
    }
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Steering an In-Flight Task

While a task is running, the visitor's composer routes mid-run guidance through sendFeedback instead of starting a new task. Type or dictate guidance into the composer (placeholder switches to 'Guide Rover…'), or trigger steering programmatically from your own button.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'my-app',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['app.example.com'],
  });

  // Custom Steer button — only meaningful while a task is in flight.
  document.querySelector('#steer-button')?.addEventListener('click', () => {
    const ack = rover.sendFeedback('Actually use the search bar, not the menu');
    if (ack === null) {
      // No run in flight — sendFeedback is a no-op outside a run.
      console.log('No active task to steer.');
    }
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Onboarding Flow

Auto-routing setup with planner fallback only when ACT has no usable outcome.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  const isNewUser = !localStorage.getItem('onboarded');

  rover('boot', {
    siteId: 'my-app',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['app.example.com'],
    openOnInit: isNewUser,
    taskRouting: {
      mode: 'auto',
      plannerOnActError: true, // Planner fallback runs only if ACT has no usable result
      actHeuristicThreshold: 5
    },
    checkpointing: { enabled: true, autoVisitorId: true },
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Checkout Assistant

Help users complete checkout by filling addresses, applying promos, and handling payment flows.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'my-store',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['shop.example.com', 'checkout.example.com'],
    domainScopeMode: 'registrable_domain',
    allowActions: true,
    taskRouting: { mode: 'auto', plannerOnActError: true },
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Form Assistance

Guide users through complex multi-step forms with validation and auto-fill.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'my-portal',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['portal.example.com'],
    sessionScope: 'tab',
    taskRouting: { mode: 'planner' },
    checkpointing: { enabled: true, autoVisitorId: true },
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Read-Only Support Agent

Rover helps users find information and navigate, but doesn't take actions.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'my-help',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['help.example.com'],
    allowActions: false,
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

Strict Host Scope

Host-only install example for tightly controlled sites. Navigation behavior stays managed by Rover runtime.

<script>
  (function(){ var r = window.rover = window.rover || function(){ (r.q = r.q || []).push(arguments); }; r.l = +new Date(); })();
  rover('boot', {
    siteId: 'secure-app',
    publicKey: 'pk_site_your_public_key',
    allowedDomains: ['app.example.com'],
    domainScopeMode: 'host_only',
    sessionScope: 'tab',
  });
</script>
<script
  src="https://rover.rtrvr.ai/embed.js?v=your_site_key_id"
  async
  data-site-id="your_site_id"
  data-public-key="pk_site_your_public_key"
  data-site-key-id="your_site_key_id"></script>

A2W Run Examples

Use strict A2W POST when you can send JSON. Give URL-fetch chatbots a compact A2W GET link. Keep ?rover=..., ?rover_shortcut=..., and ?rover_playbook=... as visitor browser links.

Browser-first query param

Fastest way to make Rover run in the page UI on first-party rtrvr.ai. Other Rover installs expose `?rover=` when AI launch is enabled for the site.

https://www.rtrvr.ai/?rover=get%20me%20the%20latest%20blog%20post

Create an A2W run

Machine path for any AI, CLI, or agent that needs progress and a final result back.

curl -X POST 'https://agent.rtrvr.ai/v2/a2w/runs' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "url": "https://www.rtrvr.ai",
    "prompt": "get me the latest blog post"
  }'

Cloud-hosted execution

Use cloud execution explicitly when the caller cannot open a real browser. The create call can return 202 before the run is finished; follow the returned links until terminal or input_required.

curl -X POST 'https://agent.rtrvr.ai/v2/a2w/runs' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Prefer: execution=cloud, wait=10' \
  -d '{
    "url": "https://www.rtrvr.ai",
    "prompt": "get me the latest blog post"
	  }'

Chatbot GET execution

Use this when the caller can only fetch a URL. Rover opens the target in the hosted cloud browser and returns markdown with a self-contained poll link.

https://agent.rtrvr.ai/v2/a2w/runs?url=https%3A%2F%2Fwww.rtrvr.ai&prompt=get%20me%20the%20latest%20blog%20post&execution=cloud&wait=25&format=markdown

Create a run from a Rover deep link

If a discovered URL already contains rover or rover_shortcut, pass that full URL as url to GET /v2/a2w/runs.

https://agent.rtrvr.ai/v2/a2w/runs?url=https%3A%2F%2Fexample.com%2F%3Frover_shortcut%3Dcheckout_flow&execution=cloud&wait=25&format=markdown

Stream or continue the run

Use the canonical run URL for SSE, NDJSON, polling, continuation, and cancel.

# SSE
curl -N 'https://agent.rtrvr.ai/v2/a2w/runs/a2w_run_123?access=a2w_access_...' \
  -H 'Accept: text/event-stream'

# Continue input_required
curl -X POST 'https://agent.rtrvr.ai/v2/a2w/runs/a2w_run_123?access=a2w_access_...' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{ "input": "Use the newest post from the blog index page." }'

# Cancel
curl -X DELETE 'https://agent.rtrvr.ai/v2/a2w/runs/a2w_run_123?access=a2w_access_...' \
  -H 'Accept: application/json'

Cross-site handoff

Delegate a child task to another Rover-enabled site and keep one shared workflow lineage.

# Create the root run first
curl -X POST 'https://agent.rtrvr.ai/v2/a2w/runs' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "url": "https://x.com",
    "prompt": "Start checkout and hand off shipping selection when needed"
  }'

# Later, delegate from the parent run
curl -X POST 'https://agent.rtrvr.ai/v2/a2w/runs/a2w_run_parent/handoffs?access=a2w_access_parent' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "url": "https://y.com",
    "instruction": "Continue the checkout flow and return the shipping options.",
    "contextSummary": "User already selected product and entered address on x.com.",
    "expectedOutput": "Return the cheapest valid shipping option and ETA."
  }'

# Read the shared workflow
curl 'https://agent.rtrvr.ai/v2/a2w/workflows/a2w_wf_456?access=a2w_access_...' \
  -H 'Accept: application/json'

Node fetch

Self-contained hosted cloud example for agents running in Node 18+. On input_required it relays confirm/verify links to the human, posts sign-in links to auth-input, and answers ordinary questions — instead of giving up.

// askHuman(prompt) is your own human-in-the-loop prompt.
const createResponse = await fetch('https://agent.rtrvr.ai/v2/a2w/runs', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'accept': 'application/json',
    'prefer': 'execution=cloud, wait=10',
  },
  body: JSON.stringify({
    url: 'https://www.rtrvr.ai',
    prompt: 'get me the latest blog post',
  }),
});

const created = await createResponse.json();
const runUrl = created.run;
let run = created;

while (!['completed', 'failed', 'cancelled', 'expired'].includes(run.status)) {
  if (run.status === 'input_required') {
    const links = run.input?.links ?? {};
    if (links.confirm || links.verify) {
      // Payment/verification/sensitive-action gates: relay the link and poll.
      console.log(`Ask the human to open: ${links.confirm ?? links.verify}`);
    } else if (run.input?.reason === 'sign_in_link_required') {
      // Human pastes the magic link / OTP (secret: one-shot, never echoed).
      const value = await askHuman(run.authInput?.prompt ?? 'Paste the sign-in link or code:');
      const authInputUrl = new URL(runUrl);
      authInputUrl.pathname += '/auth-input';
      await fetch(authInputUrl, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ value }),
      });
    } else {
      // Ordinary question — or { answers: { key: value } } for multi-question parks.
      const answer = await askHuman(run.input?.message ?? 'Rover needs input:');
      await fetch(runUrl, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ input: answer }),
      });
    }
  }
  const runResponse = await fetch(runUrl, {
    headers: { accept: 'application/json', prefer: 'wait=10' },
  });
  run = await runResponse.json();
  console.log(run.status, run.result?.text ?? '');
}

if (run.status !== 'completed') {
  throw new Error(run.result?.error || `A2W run ended with ${run.status}`);
}

Python

Simple requests-based cloud execution loop that long-polls the returned run URL and handles input_required with the relay-or-continue pattern.

import requests

create = requests.post(
    "https://agent.rtrvr.ai/v2/a2w/runs",
    headers={
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Prefer": "execution=cloud, wait=10",
    },
    json={
        "url": "https://www.rtrvr.ai",
        "prompt": "get me the latest blog post",
    },
)
create.raise_for_status()
created = create.json()
run_url = created["run"]
payload = created

while payload["status"] not in {"completed", "failed", "cancelled", "expired"}:
    if payload["status"] == "input_required":
        req = payload.get("input") or {}
        links = req.get("links") or {}
        if links.get("confirm") or links.get("verify"):
            # Payment/verification/sensitive-action gates: relay the link and poll.
            print("Ask the human to open:", links.get("confirm") or links.get("verify"))
        elif req.get("reason") == "sign_in_link_required":
            # Human pastes the magic link / OTP (secret: one-shot, never echoed).
            value = input((payload.get("authInput") or {}).get("prompt") or "Paste the sign-in link or code: ")
            base, _, query = run_url.partition("?")
            requests.post(f"{base}/auth-input?{query}", json={"value": value}, timeout=30)
        else:
            # Ordinary question — or {"answers": {key: value}} for multi-question parks.
            answer = input(req.get("message") or "Rover needs input: ")
            requests.post(run_url, json={"input": answer}, timeout=30)
    current = requests.get(
        run_url,
        headers={"Accept": "application/json", "Prefer": "wait=10"},
        timeout=30,
    )
    current.raise_for_status()
    payload = current.json()
    print(payload["status"], payload.get("result", {}).get("text", ""))
if payload["status"] != "completed":
    raise RuntimeError(payload.get("result", {}).get("error") or f"A2W run ended with {payload['status']}")

Shell helper

Create a cloud A2W run and stream NDJSON events. Requires jq.

rover_run() {
  local url="$1"
  local prompt="$2"
  local created run_url

  created="$(curl -sS -X POST 'https://agent.rtrvr.ai/v2/a2w/runs' \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -H 'Prefer: execution=cloud, wait=10' \
    -d "$(jq -nc --arg url "$url" --arg prompt "$prompt" '{url:$url,prompt:$prompt}')")" || return 1

  run_url="$(printf '%s' "$created" | jq -r '.run')"
  curl -sS "$run_url" -H 'Accept: application/x-ndjson'
}

rover_run "https://www.rtrvr.ai" "get me the latest blog post"

Need a pattern not listed here? Check the Configuration Reference for all available options, or reach out on Discord.

Quick StartAPI Reference