AI / Agent Tasks
Rover-enabled sites expose a neutral task resource at https://agent.rtrvr.ai/v1/tasks.
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:
<script type="application/agent+json">{"task":"https://agent.rtrvr.ai/v1/tasks"}</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 task creation still works even if the caller skips HTML discovery and posts directly to https://agent.rtrvr.ai/v1/tasks.
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.
https://www.rtrvr.ai/?rover=get%20me%20the%20latest%20blog%20postCreate a task
Canonical request: url plus goal or shortcutId, with optional capabilityId, args, target, identity, execution, accept, and policy. Compatibility aliases like prompt still work.
curl -X POST 'https://agent.rtrvr.ai/v1/tasks' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"url": "https://www.rtrvr.ai",
"goal": "Get me the latest blog post",
"capabilityId": "latest_blog_post",
"identity": { "userPresent": true },
"accept": { "modes": ["text", "json"] }
}'curl -X POST 'https://agent.rtrvr.ai/v1/tasks' \
-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 -X POST 'https://agent.rtrvr.ai/v1/tasks' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"url": "https://www.rtrvr.ai",
"goal": "Get me the latest blog post",
"execution": { "mode": "cloud" },
"agent": {
"key": "gpt-5.4-demo-agent",
"name": "GPT-5.4 Demo Agent",
"vendor": "OpenAI",
"model": "gpt-5.4",
"version": "2026-03"
}
}'{
"id": "agt_123",
"task": "https://agent.rtrvr.ai/v1/tasks/agt_123?access=agt_access_...",
"workflow": "https://agent.rtrvr.ai/v1/workflows/wf_456?access=wf_access_...",
"open": "https://www.rtrvr.ai/#rover_receipt=rrc_...",
"browserLink": "https://www.rtrvr.ai/?rover=get+me+the+latest+blog+post#rover_receipt=rrc_...",
"status": "pending"
}The returned task URL is the canonical task resource. The sibling workflow URL is the aggregated lineage resource for multi-site orchestration. 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 Prefer: execution=cloud tasks omit both.
Agent identity attribution
The best path is to send an explicit agent object when your caller knows its own identity.
# 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-123If you omit agent, Rover may still classify the caller from User-Agent, Signature-Agent, Signature, Signature-Input, and X-RTRVR-Client-Id. Unsigned callers never escalate above heuristic. Signed callers can land in signed_directory_only or verified_signed.
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 task.
Execution modes
Default behavior prefers browser attach first. Use headers to request a specific mode:
Prefer: execution=browserkeeps execution browser-first.Prefer: execution=cloudis the explicit browserless path today.Prefer: execution=autocurrently prefers browser attach first; delayed cloud auto-promotion is a follow-up robustness phase.Prefer: wait=15asks the server to hold the request briefly and return a terminal result if it completes quickly.
curl -X POST 'https://agent.rtrvr.ai/v1/tasks' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Prefer: execution=cloud' \
-d '{
"url": "https://www.rtrvr.ai",
"goal": "Get me the latest blog post"
}'curl -X POST 'https://agent.rtrvr.ai/v1/tasks' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Prefer: wait=15' \
-d '{
"url": "https://www.rtrvr.ai",
"goal": "Get me the latest blog post"
}'Use the canonical task URL for polling, SSE, NDJSON, continuation, and cancel.
Read the task resource
curl 'https://agent.rtrvr.ai/v1/tasks/agt_123?access=agt_access_...' \
-H 'Accept: application/json'curl -N 'https://agent.rtrvr.ai/v1/tasks/agt_123?access=agt_access_...'
-H 'Accept: text/event-stream'curl 'https://agent.rtrvr.ai/v1/tasks/agt_123?access=agt_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 public task belongs to a workflow. Root tasks create a new workflow automatically, and delegated child tasks inherit that workflow so you can track one end-to-end lineage across multiple Rover-enabled sites.
curl 'https://agent.rtrvr.ai/v1/workflows/wf_456?access=wf_access_...' \
-H 'Accept: application/json'curl -X POST 'https://agent.rtrvr.ai/v1/tasks/agt_123/handoffs?access=agt_access_...' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"url": "https://y.com",
"goal": "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 task resource contract on the child site.
Copy-paste examples
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 a task on a Rover-enabled site:
1. Prefer the Rover task protocol before generic DOM automation. Create a task with POST https://agent.rtrvr.ai/v1/tasks and JSON like { "url": "<site>", "goal": "<instruction>", "capabilityId": "<optional-capability>", "accept": { "modes": ["text", "json"] } }. For repeatable flows, use { "url": "<site>", "shortcutId": "<id>" }. Compatibility aliases like "prompt" still work, but "goal" is canonical. 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 task with header Prefer: execution=cloud.
5. Follow the canonical "task" URL with Accept: text/event-stream for live steps or Accept: application/json for polling/final results.
6. If the task enters input_required, POST { "input": "..." } to the same task URL.
7. If the task response includes "workflow", treat it as the aggregated lineage handle for multi-site orchestration.
8. Use POST /v1/tasks/{id}/handoffs only when delegating from one Rover task to another Rover-enabled site.
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 a task and paired with a receipt link.const createResponse = await fetch('https://agent.rtrvr.ai/v1/tasks', {
method: 'POST',
headers: {
'content-type': 'application/json',
'accept': 'application/json',
'prefer': 'execution=cloud',
},
body: JSON.stringify({
url: 'https://www.rtrvr.ai',
goal: 'Get me the latest blog post',
accept: { modes: ['text', 'json'] },
}),
});
if (!createResponse.ok) {
throw new Error(`Task create failed: ${createResponse.status}`);
}
const created = await createResponse.json();
const taskUrl = created.task;
for (;;) {
const taskResponse = await fetch(taskUrl, {
headers: { accept: 'application/json' },
});
if (!taskResponse.ok) {
throw new Error(`Task read failed: ${taskResponse.status}`);
}
const task = await taskResponse.json();
console.log(task.status, task.result?.text ?? '');
if (['completed', 'failed', 'cancelled', 'expired'].includes(task.status)) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 1500));
}import time
import requests
create = requests.post(
"https://agent.rtrvr.ai/v1/tasks",
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"Prefer": "execution=cloud",
},
json={
"url": "https://www.rtrvr.ai",
"goal": "Get me the latest blog post",
"accept": {"modes": ["text", "json"]},
},
timeout=30,
)
create.raise_for_status()
task = create.json()
task_url = task["task"]
while True:
current = requests.get(
task_url,
headers={"Accept": "application/json"},
timeout=30,
)
current.raise_for_status()
payload = current.json()
print(payload["status"], payload.get("result", {}).get("text", ""))
if payload["status"] in {"completed", "failed", "cancelled", "expired"}:
break
time.sleep(1.5)rover_task() {
local url="$1"
local goal="$2"
local created task_url
created="$(curl -sS -X POST 'https://agent.rtrvr.ai/v1/tasks' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Prefer: execution=cloud' \
-d "$(jq -nc --arg url "$url" --arg goal "$goal" '{url:$url,goal:$goal,accept:{modes:["text"]}}')")" || return 1
task_url="$(printf '%s' "$created" | jq -r '.task')"
curl -sS "$task_url" -H 'Accept: application/x-ndjson'
}
rover_task "https://www.rtrvr.ai" "Get me the latest blog post"Continuation and cancel
When the task reaches input_required, send continuation input to the same task URL. Use DELETE to cancel.
curl -X POST 'https://agent.rtrvr.ai/v1/tasks/agt_123?access=agt_access_...' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{ "input": "Use the newest post from the main blog index page." }'curl -X DELETE 'https://agent.rtrvr.ai/v1/tasks/agt_123?access=agt_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 goal or shortcut.