API Reference
Complete Rover docs for workspace key management and browser runtime (`/v2/rover/*`) contracts.
Runtime base: https://extensionrouter.rtrvr.ai/v2/rover/*
Auth Modes
Firebase ID tokenfor Workspace management APIs (/generateRoverSiteKey,/listRoverSiteKeys, and related config endpoints).sessionToken (rvrsess_*)for browser runtime calls to/v2/rover/*after session bootstrap.publicKey (pk_site_*)only for bootstrap exchange on/v2/rover/session/open. Do not sendsk_site_*to browser runtime.
Workspace Management APIs
Used by Rover Workspace for key lifecycle and site config management. These remain Firebase-authenticated control-plane endpoints.
/generateRoverSiteKeyCreate a new Rover site key and persisted policy/profile.
Auth: Firebase ID token (Bearer)
Required Request Fields
| Field | Type | Description |
|---|---|---|
siteId | siteName | string | Existing siteId or a siteName for server-generated siteId. |
label | string | Human-readable key label. |
allowedDomains | string[] | Allowed host/domain patterns (`example.com`, `*.example.com`, `=app.example.com`, or URL-shaped entries normalized to host). |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
ttlDays | number | Expiration days, use 0 for no expiry. |
environment | 'production' | 'development' | 'test' | Deployment environment label. |
capabilities | { roverEmbed?, externalWebContextScrape?, cloudAgent?, cloudScrape? } | Capability profile for this key. |
roverPolicy | RoverSitePolicy | Policy persisted and returned in list/rotate APIs. |
/listRoverSiteKeysList all Rover site keys for the authenticated workspace user.
Auth: Firebase ID token (Bearer)
/updateRoverSiteKeyPolicyPatch a key's allowed domains, active status, capabilities, and Rover policy.
Auth: Firebase ID token (Bearer)
Required Request Fields
| Field | Type | Description |
|---|---|---|
keyId | string | Target key identifier. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
siteId | string | Optional site assertion/update. |
allowedDomains | string[] | Replacement domain pattern set (`example.com`, `*.example.com`, `=app.example.com`, or URL-shaped entries). |
active | boolean | Enable/disable key. |
capabilities | ApiKeyCapabilities | Capability patch. |
roverPolicy | Partial<RoverSitePolicy> | Policy patch. |
/rotateRoverSiteKeyRotate a key (invalidate old key and issue a new pk_site_* value).
Auth: Firebase ID token (Bearer)
Required Request Fields
| Field | Type | Description |
|---|---|---|
keyId | string | Key being rotated. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
siteId | string | Optional site assertion/update. |
label | string | Optional new label. |
capabilities | ApiKeyCapabilities | Capability patch applied to rotated key. |
roverPolicy | Partial<RoverSitePolicy> | Policy patch applied to rotated key. |
/getRoverSiteConfigRead cloud journey/shortcut and greeting config for a key/site.
Auth: Firebase ID token (Bearer)
Required Request Fields
| Field | Type | Description |
|---|---|---|
keyId | string | Site key ID. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
siteId | string | Optional site assertion. |
/upsertRoverSiteConfigWrite cloud journey/shortcut and greeting config.
Auth: Firebase ID token (Bearer)
Required Request Fields
| Field | Type | Description |
|---|---|---|
keyId | string | Site key ID. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
siteId | string | Optional site assertion. |
siteConfig.shortcuts | RoverSiteShortcut[] | Persisted shortcuts/journeys. |
siteConfig.greeting | { text?, delay?, duration?, disabled? } | null | Greeting config. |
/updateApiKeyCapabilitiesPatch capability flags for user or site keys.
Auth: Firebase ID token (Bearer)
Required Request Fields
| Field | Type | Description |
|---|---|---|
keyId | string | Target key identifier. |
capabilities | ApiKeyCapabilities | Boolean capability patch fields. |
Rover Runtime APIs (`/v2/rover/*`)
Server-authoritative runtime contract for embedded Rover. All run-state changes are keyed bysessionId + runId + epoch + seq.
/v2/rover/session/openBootstrap or refresh a browser runtime session and return the initial projection.
Auth: Body token. Preferred: bootstrapToken/publicKey (pk_site_*). Optional: sessionToken refresh path.
Required Request Fields
| Field | Type | Description |
|---|---|---|
siteId | string | Workspace site identifier. |
host | url | string | Current host/url for domain and policy checks. |
bootstrapToken | publicKey | string | Public Rover key (pk_site_*). Required when no valid sessionToken is supplied. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Client session identifier (auto-generated if omitted). |
sessionToken | string | Current rvrsess_* token. If invalid/expired and bootstrap token exists, server falls back to bootstrap flow. |
Success Example
{
"success": true,
"data": {
"sessionId": "visitor-123",
"sessionToken": "rvrsess_...",
"sessionTokenExpiresAt": 1771484403000,
"streamToken": "rvrsess_...",
"epoch": 5,
"capabilities": { "roverEmbed": true },
"policy": {
"domainScopeMode": "registrable_domain",
"externalNavigationPolicy": "open_new_tab_notice",
"crossHostPolicy": "same_tab",
"enableExternalWebContext": true,
"externalScrapeMode": "on_demand",
"externalAllowDomains": [],
"externalDenyDomains": []
},
"projection": { "sessionId": "visitor-123", "epoch": 5, "events": [], "tabs": [] },
"siteConfig": { "shortcuts": [], "greeting": { "text": "Welcome" } },
"sseUrl": "https://extensionrouter.rtrvr.ai/v2/rover/stream?..."
}
}Typed Conflict/Auth Errors
Example 1
{
"success": false,
"error": "SESSION_TOKEN_EXPIRED",
"data": {
"code": "SESSION_TOKEN_EXPIRED",
"message": "Token expired",
"retryable": true,
"next_action": "Pass bootstrapToken/publicKey (pk_site_*) to /v2/rover/session/open to re-bootstrap the session."
}
}Example 2
{
"success": false,
"error": "BOOTSTRAP_REQUIRED",
"data": {
"code": "BOOTSTRAP_REQUIRED",
"message": "bootstrapToken/publicKey (pk_site_*) is required.",
"retryable": false,
"next_action": "Provide a valid pk_site_* key in bootstrapToken/publicKey."
}
}Notes
- Server mints short-lived sessionToken (~10 min).
- Browser runtime should not send long-lived bearer keys.
/v2/rover/session/openRefresh an existing rvrsess_* token and SSE URL.
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Session to refresh (defaults to token sid). |
Notes
- Use when token is near expiry or after SSE auth failures.
/v2/rover/commandSubmit user input and create/continue an authoritative run.
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Target session. |
message | string | User message. |
expectedEpoch | number | Required in strict mode for stale-epoch protection. |
clientEventId | string | Idempotency key for retries and dedup. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
continueRun | boolean | Continue active run. Reserved for ask_user answer continuation in the same task boundary. |
forceNewRun | boolean | Cancel active run and create a fresh run. Normal user sends should set this true. |
expectedSeq | number | Required in strict mode when active run exists. |
requestedMode | 'act' | 'planner' | 'auto' | Routing preference. `act`/`planner` force backend mode; `auto` uses heuristic routing. |
taskBoundaryId | string | Optional client task boundary identifier. |
Success Example
{
"success": true,
"data": {
"runId": "run-uuid",
"acceptedMode": "act",
"state": "running",
"continuePrompt": false,
"requestedMode": "act",
"epoch": 5,
"seq": 1
}
}Typed Conflict/Auth Errors
Example 1
{
"success": false,
"error": "stale_epoch",
"data": {
"sessionId": "visitor-123",
"runId": "run-uuid",
"expectedEpoch": 2,
"currentEpoch": 5,
"decisionReason": "stale_epoch_retryable",
"conflict": { "type": "stale_epoch", "currentEpoch": 5, "retryable": true }
}
}Example 2
{
"success": false,
"error": "active_run_exists",
"data": {
"continuePrompt": true,
"runId": "run-uuid",
"state": "running",
"acceptedMode": "act",
"decisionReason": "active_run_exists",
"conflict": { "type": "active_run_exists", "retryable": false }
}
}Notes
- One active run per session is enforced server-side.
- No local silent fallback should run when this call is not accepted.
/v2/rover/commandControl run lifecycle (`cancel`, `end_task`, `new_task`, `continue`).
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Target session. |
action | 'cancel' | 'end_task' | 'new_task' | 'continue' | Requested run control action. |
expectedEpoch | number | Required in strict mode. |
clientEventId | string | Idempotency key. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
runId | string | Required for `cancel` and `end_task` when active run is not implied. |
expectedSeq | number | Strict stale-seq guard for run-scoped actions. |
reason | string | Audit reason for control action. |
Success Example
{
"success": true,
"data": {
"action": "cancel",
"runId": "run-uuid",
"currentSeq": 18,
"projection": { "sessionId": "visitor-123", "epoch": 6, "activeRunId": "", "events": [], "tabs": [] }
}
}Notes
- Use clientEventId for idempotent retries.
- Strict mode requires expectedEpoch and enforces stale rejection before state transitions.
/v2/rover/commandSubmit navigation/tab intent and receive authoritative policy decision.
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Target session. |
targetUrl | url | string | Navigation destination. |
expectedEpoch | number | Required in strict mode. |
clientEventId | string | Idempotency key used for silent retry recovery. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
runId | string | Intended active run (defaults to session active run). |
expectedSeq | number | Strict stale-seq guard for run-scoped events. |
currentUrl | string | Current browser URL. |
currentHost/targetHost/isCrossHost/navigationClass | hints | Client hints. Server computes authoritative class regardless. |
logicalTabId | string | Logical tab identifier for run tab graph. |
message | string | Prompt context used for adversarial scoring. |
adversarialScore | number | Optional explicit score (merged with server score). |
Success Example
{
"success": true,
"data": {
"decision": "allow_same_tab",
"decisionReason": "allow_same_tab",
"decisionHint": "allow_same_tab",
"notice": "In-scope navigation allowed.",
"staleRun": false,
"sessionId": "visitor-123",
"sessionEpoch": 5,
"runId": "run-uuid",
"currentSeq": 3,
"clientEventId": "evt-uuid",
"currentHost": "www.rtrvr.ai",
"targetHost": "rtrvr.ai",
"isCrossHost": true,
"crossDomain": false,
"navigationClass": "cross_host_in_scope",
"adversarial": { "score": 0, "reasons": [] }
}
}Typed Conflict/Auth Errors
Example 1
{
"success": false,
"error": "stale_seq",
"data": {
"sessionId": "visitor-123",
"runId": "run-uuid",
"expectedSeq": 2,
"currentSeq": 3,
"currentEpoch": 5,
"decisionReason": "stale_seq_retryable",
"clientEventId": "evt-uuid",
"conflict": { "type": "stale_seq", "currentSeq": 3, "currentEpoch": 5, "retryable": true }
}
}Example 2
{
"success": true,
"data": {
"decision": "stale_run",
"decisionReason": "stale_run",
"staleRun": true,
"staleRunReason": "run_terminal",
"currentRunId": "new-active-run-id",
"currentActiveRunId": "new-active-run-id",
"sessionEpoch": 6,
"decisionHint": "sync_projection_and_ignore"
}
}Notes
- Server keeps strict stale checks and returns typed 409 envelopes.
- SDK should do one silent retry on stale conflicts with same clientEventId.
- Stale/missing run is non-fatal via `decision=stale_run` (200).
/v2/rover/streamSSE stream for projection updates and keepalive pings.
Auth: sessionToken (query/body)
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | query string | Session to stream. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
seqAfter | number | Start events after this sequence. |
Notes
- SSE events: `ready`, `projection`, `ping`, `error`.
- Use GET /v2/rover/state as polling fallback.
/v2/rover/stateFetch latest projection (session/run/tabs/events/snapshot metadata).
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | query/body | Session to fetch. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
seqAfter | number | Return events after this sequence only. |
/v2/rover/snapshotUpsert compacted checkpoint/snapshot for resume.
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Target session. |
checkpoint | payload | object | Compacted snapshot payload. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
visitorId | string | Visitor ID for checkpoint ownership. |
updatedAt | number | Client timestamp in ms. |
version | number | Snapshot schema version. |
seq | number | Last stable seq captured in snapshot. |
compactedPrevSteps | unknown[] | Compacted execution history. |
chatSummary | string | Conversation summary text. |
ttlHours | number | TTL for snapshot document. |
/v2/rover/context/externalFetch/act on external context through policy-scoped cloud integrations.
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Target session. |
runId | string | Active run ID. |
url | string | External target URL. |
intent | 'open_only' | 'read_context' | 'act' | External action intent. |
expectedEpoch | number | Strict stale-epoch guard. |
expectedSeq | number | Strict stale-seq guard. |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
logicalTabId | string | Logical external tab id. |
message | userInput | string | Used for adversarial gating and context. |
timeoutMs | number | External fetch timeout. |
Notes
- Policy/capability checks are enforced server-side for each call.
- High adversarial score can block external context/action even when policy allows it.
/v2/rover/telemetry/ingestIngest runtime telemetry batches (diagnostics/observability).
Auth: sessionToken
Required Request Fields
| Field | Type | Description |
|---|---|---|
sessionId | string | Target session. |
events | unknown[] | Telemetry event batch (server truncates to safe limits). |
Optional Request Fields
| Field | Type | Description |
|---|---|---|
runId | string | Active run association. |
flushReason | string | Flush reason (`interval`, `manual`, etc.). |
pageUrl | string | Current page url. |
sampleRate | number | Client telemetry sample rate. |
sdkVersion | string | SDK version marker. |
Notes
- This endpoint is automatic; users do not need manual setup for normal onboarding.
- Telemetry ingestion does not replace run/state APIs.
Canonical Runtime Envelopes
RoverSuccessResponse<T>
type RoverSuccessResponse<T> = {
success: true
data: T
}RoverErrorResponse
type RoverErrorResponse = {
success: false
error: string
data?: {
code?: string
message?: string
retryable?: boolean
next_action?: string
decisionReason?: string
conflict?: {
type: 'stale_seq' | 'stale_epoch' | 'active_run_exists'
currentSeq?: number
currentEpoch?: number
retryable: boolean
}
}
}TabEventDecisionResponse
type TabEventDecisionResponse = RoverSuccessResponse<{
decision: 'allow_same_tab' | 'open_new_tab' | 'block' | 'stale_run'
decisionReason: 'allow_same_tab' | 'open_new_tab' | 'policy_blocked' | 'stale_run'
decisionHint: string
notice?: string
staleRun: boolean
staleRunReason?: string
currentRunId?: string
currentActiveRunId?: string
sessionId: string
sessionEpoch: number
runId?: string
currentSeq?: number
clientEventId?: string
currentHost?: string
targetHost?: string
isCrossHost?: boolean
crossDomain?: boolean
navigationClass?: 'same_host_in_scope' | 'cross_host_in_scope' | 'cross_registrable_external'
adversarial?: { score: number; reasons: string[] }
}>RunInputResponse
type RunInputResponse = RoverSuccessResponse<{
runId?: string
acceptedMode?: 'act' | 'planner'
state?: 'queued' | 'running' | 'awaiting_user' | 'cancel_requested' | 'cancelled' | 'completed' | 'failed'
continuePrompt?: boolean
requestedMode?: 'act' | 'planner' | 'auto'
epoch?: number
seq?: number
routing?: { score: number; reason: string }
}>RunControlResponse
type RunControlResponse = RoverSuccessResponse<{
action: 'cancel' | 'end_task' | 'new_task' | 'continue'
runId?: string
currentSeq?: number
projection: SessionProjectionResponse
}>RunTransitionPayload
type RunTransitionPayload = {
runId: string
status: 'queued' | 'running' | 'awaiting_user' | 'cancel_requested' | 'cancelled' | 'completed' | 'failed'
continuationReason?: 'loop_continue' | 'same_tab_navigation_handoff' | 'awaiting_user'
}SessionProjectionResponse
type SessionProjectionResponse = {
sessionId: string
epoch: number
activeRunId?: string
runStatus?: 'queued' | 'running' | 'awaiting_user' | 'cancel_requested' | 'cancelled' | 'completed' | 'failed'
runMode?: 'act' | 'planner'
events: Array<{ seq: number; type: string; ts: { _seconds: number; _nanoseconds: number }; data?: Record<string, unknown> }>
tabs: Array<{ logicalTabId: string; parentLogicalTabId?: string; scope: 'in_scope' | 'external'; status: 'open' | 'blocked' | 'scraped' | 'acted' | 'closed'; url?: string; reason?: string; updatedAt: number }>
snapshot?: Record<string, unknown>
snapshotUpdatedAt?: number
}SSE Event Shape
// GET /v2/rover/stream emits:
// event: ready data: { sessionId, ts }
// event: projection data: SessionProjectionResponse
// event: ping data: { ts }
// event: error data: { message }
Conflict and Auth Semantics
409 stale_seqwhenexpectedSeqis stale/missing (strict mode) for mutable run-scoped calls.409 stale_epochwhenexpectedEpochis stale/missing (strict mode) for mutable calls.409 active_run_existson concurrent run creation races (typed conflict, no generic unknown error).200 decision='stale_run'for non-fatal stale/missing run inPOST /commandwithtype='TAB_EVENT'.- If command-based tab preflight is temporarily unavailable, SDK falls back to local navigation policy instead of hard-blocking navigation.
401 SESSION_TOKEN_EXPIRED,401 SESSION_TOKEN_INVALID, and401 BOOTSTRAP_REQUIREDfor typed auth flows.
Run Lifecycle and Continuity Semantics
- Hard invariant: one active run per session, enforced in backend store/ledger.
requestedModeinPOST /commandpayloads withtype='RUN_INPUT':actandplannerforce backend mode;autouses heuristic routing.- SDK default: normal user sends are fresh task boundaries with
forceNewRun=true(freshprevSteps+ tab scope). continueRun=trueis used only forask_useranswer submission on the active boundary.- Heuristic follow-up chat carryover is cue-only (
followupChatLogin worker payloads), and never carries prior task state. - Worker-to-SDK continuation semantics:
loop_continue,same_tab_navigation_handoff,awaiting_user. - Navigation outcome semantics:
same_tab_scheduled,new_tab_opened,blocked. - Stale seq/epoch races are expected under concurrent navigation/events; SDK should perform one silent retry for command-based tab stale conflicts.