Getting Started

OverviewQuick StartInstant PreviewTry on Other SitesWorkspace

Integration

ConfigurationSecurity & PoliciesAI DiscoveryAgent-to-Web (A2W)

Reference

API ReferenceInstant Preview APIExamplesRoadmap

Agent-to-Web Protocol (A2W)

Rover-enabled sites expose a neutral A2W run resource at https://agent.rtrvr.ai/v1/a2w/runs.

Use this machine path when you need structured progress, continuation, or a final result. Keep ?rover=... and ?rover_shortcut=... for browser-first convenience.

Discovery

Rover can expose a source-visible marker that plain HTML fetches can discover:

HTML
<script type="application/agent+json">{"a2w":"https://agent.rtrvr.ai/v1/a2w/runs","run":"https://agent.rtrvr.ai/v1/a2w/runs"}</script>

The marker is optional but recommended. For stronger discovery, also check /.well-known/rover-site.json and /.well-known/agent-card.json. In the live browser, the minimized Rover seed/presence is the visible Rover cue. Host-only run creation still works even if the caller skips HTML discovery and posts directly to https://agent.rtrvr.ai/v1/a2w/runs.

Browser-first URL

Use query params when you only need Rover to run in the browser UI. First-party rtrvr.ai keeps AI launch enabled, and other Rover installs expose the same browser entrypoints when siteConfig.aiAccess.enabled is on. Query params by themselves are not the machine-readable result channel.

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

Create a run

Canonical request: url plus prompt or shortcutId, with optional capabilityId, args, target, identity, execution, accept, and policy. goal remains accepted as a compatibility alias.

curl
curl -X POST 'https://agent.rtrvr.ai/v1/a2w/runs' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "url": "https://www.rtrvr.ai",
    "prompt": "Get me the latest blog post",
    "capabilityId": "latest_blog_post",
    "identity": { "userPresent": true },
    "accept": { "modes": ["text", "json"] }
  }'
curl with exact shortcut
curl -X POST 'https://agent.rtrvr.ai/v1/a2w/runs' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "url": "https://www.rtrvr.ai",
    "shortcutId": "setup_rover",
    "target": { "pageId": "rover_workspace" },
    "accept": { "modes": ["markdown"] }
  }'
curl with self-reported agent identity
curl -X POST 'https://agent.rtrvr.ai/v1/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",
    "agent": {
      "key": "gpt-5.4-demo-agent",
      "name": "GPT-5.4 Demo Agent",
      "vendor": "OpenAI",
      "model": "gpt-5.4",
      "version": "2026-03"
    }
  }'
202 Accepted
{
  "id": "a2w_run_123",
  "protocol": "a2w",
  "runId": "a2w_run_123",
  "run": "https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...",
  "workflow": "https://agent.rtrvr.ai/v1/a2w/workflows/a2w_wf_456?access=a2w_access_...",
  "status": "running",
  "retryAfterMs": 2000,
  "terminalStatuses": ["completed", "failed", "cancelled", "expired"],
  "interactiveStatuses": ["input_required"],
  "links": {
    "poll": { "href": "https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...", "method": "GET", "headers": { "Accept": "application/json", "Prefer": "wait=10" } },
    "stream": { "href": "https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...", "method": "GET", "headers": { "Accept": "text/event-stream" } },
    "ndjson": { "href": "https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...", "method": "GET", "headers": { "Accept": "application/x-ndjson" } }
  },
  "open": "https://www.rtrvr.ai/#rover_receipt=a2w_receipt_...",
  "browserLink": "https://www.rtrvr.ai/?rover=get+me+the+latest+blog+post#rover_receipt=a2w_receipt_..."
}

The returned run URL is the canonical A2W resource. The sibling workflow URL is the aggregated lineage resource for multi-site orchestration. Active responses include links.poll, links.stream, and links.ndjson. The optional open URL is the clean receipt-based browser handoff, and browserLink is an optional readable alias when the prompt or shortcut fits safely in the visible URL. Explicit cloud runs usually omit both.

Agent identity attribution

The best path is to send an explicit agent object when your caller knows its own identity.

Heuristic fallback headers
# If you cannot send an explicit "agent" object,
# Rover can still attribute the caller heuristically from headers like:
User-Agent: ExampleBot/1.0
Signature-Agent: ExampleBot
X-RTRVR-Client-Id: caller-123

If you omit agent, Rover may still classify the caller from User-Agent, Signature-Agent, Signature, Signature-Input, and X-RTRVR-Client-Id. The stored agentKey comes from explicit key fields first, then clientId, then Signature-Agent. Signature-Agent directory/signature-envelope evidence without full request verification can land in signed_directory_only; full HTTP Message Signature verification lands in verified_signed; loose headers remain heuristic.

A stable agent.key makes RoverBook revisit analytics and private memory much more useful because the same agent can load its notes on the next run.

Execution modes

Default behavior prefers browser attach first. Use headers to request a specific mode:

  • Prefer: execution=browser keeps execution browser-first.
  • Prefer: execution=cloud, wait=10 is the explicit browserless path with a short initial wait.
  • Prefer: execution=auto currently prefers browser attach first; delayed cloud auto-promotion is a follow-up robustness phase.
  • Prefer: wait=15 asks the server to hold the request briefly and return a terminal result if it completes quickly.
Cloud-first curl
curl -X POST 'https://agent.rtrvr.ai/v1/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"
  }'
Wait briefly for final JSON
curl -X POST 'https://agent.rtrvr.ai/v1/a2w/runs' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Prefer: wait=15' \
  -d '{
    "url": "https://www.rtrvr.ai",
    "prompt": "Get me the latest blog post"
  }'

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

Read the run resource

JSON poll / final result
curl 'https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...' \
  -H 'Accept: application/json'
SSE stream
curl -N 'https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...'
  -H 'Accept: text/event-stream'
NDJSON stream
curl 'https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...' \
  -H 'Accept: application/x-ndjson'

Public event names are ready, status, step, tool, message, observation, input, done, and error.

Workflows and cross-site handoffs

Every A2W run belongs to a workflow. Root runs create a new workflow automatically, and delegated child runs inherit that workflow so you can track one end-to-end lineage across multiple Rover-enabled sites.

Read the aggregated workflow resource
curl 'https://agent.rtrvr.ai/v1/a2w/workflows/a2w_wf_456?access=a2w_access_...' \
  -H 'Accept: application/json'
Delegate a child run to another Rover site
curl -X POST 'https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123/handoffs?access=a2w_access_...' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "url": "https://y.com",
    "prompt": "Continue the checkout flow and return the shipping options.",
    "capabilityId": "shipping_options",
    "agent": {
      "key": "gpt-5.4-demo-agent",
      "name": "GPT-5.4 Demo Agent"
    },
    "contextSummary": "User already selected product and entered address on x.com.",
    "expectedOutput": "Return the cheapest valid shipping option and ETA."
  }'

Receiving sites must opt in with siteConfig.aiAccess.allowDelegatedHandoffs=true. Handoffs reuse the same receipt-link browser flow and the same A2W run contract on the child site.

Copy-paste examples

Exact Codex / external-agent prompt
If a website may be Rover-enabled, optionally fetch its HTML and look for <script type="application/agent+json">, /.well-known/rover-site.json, or /.well-known/agent-card.json.

To run A2W on a Rover-enabled site:
1. Prefer the Agent-to-Web Protocol (A2W) before generic DOM automation. Create a run with POST https://agent.rtrvr.ai/v1/a2w/runs and JSON like { "url": "<site>", "prompt": "<instruction>", "capabilityId": "<optional-capability>", "accept": { "modes": ["text", "json"] } }. The older "goal" field is still accepted as an alias. For repeatable flows, use { "url": "<site>", "shortcutId": "<id>" }. Optionally include { "agent": { ... } } when you want Rover and RoverBook to retain stable visiting-agent attribution.
2. If you can open a real browser with the user's session and the response includes "open", open that URL. If the response includes "browserLink" and you want a readable URL, you may open it instead.
3. Only click or open the visible Rover seed/presence UI when browser interaction is actually needed. It is the page-level Rover entry cue.
4. If you need guaranteed browserless execution, create the run with header Prefer: execution=cloud, wait=10.
5. Follow links.stream, links.ndjson, or links.poll until the run is completed, failed, cancelled, expired, or input_required.
6. If the run enters input_required, POST { "input": "..." } to the same run URL.
7. If the run response includes "workflow", treat it as the aggregated lineage handle for multi-site orchestration.
8. Use POST /v1/a2w/runs/{id}/handoffs only when delegating from one Rover-enabled site to another.
9. Fall back to generic DOM automation only when the site exposes no Rover entrypoint or the Rover path is unavailable.
10. Return the final result text plus any important observations.

Do not treat ?rover= or ?rover_shortcut= alone as the result channel. They only run Rover in the browser unless they were issued from an A2W run and paired with a receipt link.
Node fetch
const createResponse = await fetch('https://agent.rtrvr.ai/v1/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',
    accept: { modes: ['text', 'json'] },
  }),
});

if (!createResponse.ok) {
  throw new Error(`A2W run create failed: ${createResponse.status}`);
}

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') {
    throw new Error(`A2W run needs input: ${JSON.stringify(run.input ?? {})}`);
  }
  const runResponse = await fetch(runUrl, {
    headers: { accept: 'application/json', prefer: 'wait=10' },
  });

  if (!runResponse.ok) {
    throw new Error(`A2W run read failed: ${runResponse.status}`);
  }

  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
import requests

create = requests.post(
    "https://agent.rtrvr.ai/v1/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",
        "accept": {"modes": ["text", "json"]},
    },
    timeout=30,
)
create.raise_for_status()
run = create.json()
run_url = run["run"]
payload = run

while payload["status"] not in {"completed", "failed", "cancelled", "expired"}:
    if payload["status"] == "input_required":
        raise RuntimeError(f"A2W run needs input: {payload.get('input')}")
    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 (requires jq)
rover_run() {
  local url="$1"
  local prompt="$2"
  local created run_url

  created="$(curl -sS -X POST 'https://agent.rtrvr.ai/v1/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,accept:{modes:["text"]}}')")" || 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"

Continuation and cancel

When the run reaches input_required, send continuation input to the same run URL. Use DELETE to cancel.

Continue a run
curl -X POST 'https://agent.rtrvr.ai/v1/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 main blog index page." }'
Cancel a run
curl -X DELETE 'https://agent.rtrvr.ai/v1/a2w/runs/a2w_run_123?access=a2w_access_...' \
  -H 'Accept: application/json'

Site owner vs caller credentials

Site owners install Rover with siteId, publicKey, and optional siteKeyId from Workspace.

External AI callers do not need those values. They only need the public website URL plus a prompt or shortcut.

Open WorkspaceAPI Reference
rtrvr.ai logo
Rover

Browser-native execution for websites and interfaces, plus analytics for the owners improving them.

Product

  • Overview
  • Get Started
  • Live Test
  • Pricing
  • Sweet Shop

Developers

  • GitHub
  • Preview Helper
  • Quick Start
  • Instant Preview
  • Try on Other Sites
  • Instant Preview API
  • Configuration
  • Agent-to-Web (A2W)
  • API Reference
  • Security
  • Examples

Resources

  • Blog
  • Videos
  • SDK Preview Helpers
  • OpenAPI Spec
  • rtrvr.ai Docs
  • rtrvr.ai Cloud

© 2026 rtrvr.ai. All rights reserved.

PrivacyTerms