{
  "openapi": "3.1.0",
  "info": {
    "title": "Cloudbrowser REST API",
    "version": "0.6.0",
    "description": "Next.js API with Sandbox0 Chrome, PostgreSQL and Auth0. Billing endpoints require configured Stripe; check billingEnabled. API-key authentication remains supported. Go CLI x-cli entries are design examples only. Fork requires a paused source."
  },
  "servers": [
    {
      "url": "http://localhost:3000/api/v1",
      "description": "Local development server"
    },
    {
      "url": "https://cloudbrowser.tv/api/v1",
      "description": "Production origin (private preview)"
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "paths": {
    "/browsers": {
      "post": {
        "operationId": "createBrowser",
        "summary": "Create a browser",
        "description": "Reserves a concurrent slot atomically. Returns an operation; connection grants require running status.",
        "x-cli": "cloudbrowser create --name research --wait",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Replays with the same payload return the same operation. Different payload returns 409."
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBrowser"
              }
            }
          }
        },
        "x-implementation": "local"
      },
      "get": {
        "operationId": "listBrowsers",
        "summary": "List browsers",
        "description": "",
        "x-cli": "cloudbrowser list --output json",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Browser"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}": {
      "get": {
        "operationId": "getBrowser",
        "summary": "Get a browser",
        "description": "",
        "x-cli": "cloudbrowser get br_123",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Browser"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      },
      "delete": {
        "operationId": "deleteBrowser",
        "summary": "Delete a browser",
        "description": "Revoke all grants and stop connections. Deletion is terminal.",
        "x-cli": "cloudbrowser delete br_123 --yes",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/pause": {
      "post": {
        "operationId": "pauseBrowser",
        "summary": "Pause a browser",
        "description": "Disconnect transports on pause. Resume checks quota and requires fresh CDP grants or new viewer sessions obtained through existing share URLs. Profile persists; live page memory is not guaranteed. Pause/runtime replacement ends viewer sessions and outstanding tickets and invalidates CDP grants; reusable Live View shares remain configured. After resume, reopen the same share URL for a new runtime-bound session. No implicit resume for viewers. Application pause first asks Chrome to exit gracefully and waits for its supervisor restart to confirm profile flush. If shutdown cannot be confirmed, the operation fails before submitting provider pause. This is distinct from provider TTL expiry.",
        "x-cli": "cloudbrowser pause br_123 --wait",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/resume": {
      "post": {
        "operationId": "resumeBrowser",
        "summary": "Resume a browser",
        "description": "Disconnect transports on pause. Resume checks quota and requires fresh CDP grants or new viewer sessions obtained through existing share URLs. Profile persists; live page memory is not guaranteed. Pause/runtime replacement ends viewer sessions and outstanding tickets and invalidates CDP grants; reusable Live View shares remain configured. After resume, reopen the same share URL for a new runtime-bound session. No implicit resume for viewers.",
        "x-cli": "cloudbrowser resume br_123 --wait",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/fork": {
      "post": {
        "operationId": "forkBrowser",
        "summary": "Fork a browser",
        "description": "Create independent browser ID and profile from a consistent snapshot. No access grants are inherited. Snapshot consistency must be qualified before implementation ships. The source must be paused; a running source returns 409 pause_required. Paused-source inheritance and independent writes passed the live test. Never treat a failed operation as a successful child snapshot.",
        "x-cli": "cloudbrowser fork br_123 --name research-copy --wait",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Replays with the same payload return the same operation. Different payload returns 409."
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForkBrowser"
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/grants": {
      "post": {
        "operationId": "createGrant",
        "summary": "Create connection access",
        "description": "Create CDP access or a Live View link. Live View defaults to a reusable share URL, read-only permission and no expiration. A short-lived one-time ticket is a separate session-entry credential. Share visits may create multiple independent viewer sessions; CLI exits after returning/opening the URL. Browser must be running when creating/using entry tickets. Draft only.",
        "x-cli": "cloudbrowser connect br_123 --audience cdp --ttl 1h --output json",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GrantIssue"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGrant"
              },
              "examples": {
                "cdp": {
                  "value": {
                    "audience": "cdp",
                    "permission": "control",
                    "ttlSeconds": 3600
                  }
                },
                "liveView": {
                  "value": {
                    "audience": "live-view",
                    "mode": "share",
                    "permission": "view",
                    "expiresAt": null,
                    "ticketTtlSeconds": 300,
                    "autoRenew": true
                  }
                }
              }
            }
          }
        },
        "x-implementation": "local"
      },
      "get": {
        "operationId": "listGrants",
        "summary": "List access grants",
        "description": "List metadata only, never bearer URLs, share secrets, refresh cookies or entry tickets. Live View metadata includes permission, expiresAt (null for never), lastOpenedAt, activeSessions and revision. Filter by audience/status; paginate with cursor/limit. Local implementation returns all matching entries with nextCursor=null; pagination is pending.",
        "x-cli": "cloudbrowser tokens list br_123",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "audience",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "cdp",
                "live-view"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "expired",
                "revoked",
                "used"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Grant"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/grants/{grantId}/renew": {
      "post": {
        "operationId": "renewGrant",
        "summary": "Renew a grant lease",
        "description": "CDP ONLY; reject live-view audience with 400 audience_not_supported. Requires workspace authorization, active renewable CDP grant and unchanged runtime. Extend expiresAt to min(now + original TTL, renewUntil), keeping opaque token stable. No hard-stop or privilege expansion. Expired/revoked CDP grants cannot renew. Viewer sessions instead use the viewer refresh endpoint and their own refresh cookie.",
        "x-cli": "cloudbrowser tokens renew br_123 grant_123",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CdpGrant"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/grants/{grantId}": {
      "delete": {
        "operationId": "revokeGrant",
        "summary": "Revoke access",
        "description": "Permanently revoke this grant, close its streams, and invalidate all tickets and refresh families. Reusable URL can no longer create sessions. Idempotent; revoked grants cannot be revived. Does not delete the browser.",
        "x-cli": "cloudbrowser tokens revoke br_123 grant_123 --yes",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Grant"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      },
      "patch": {
        "operationId": "updateLiveViewLink",
        "summary": "Change a Live View share link policy",
        "description": "Share-mode Live View only. At least one property required. Every effective policy change invalidates all old sessions and tickets immediately and increments revision. The same reusable URL can then open a fresh session. Revoked grants cannot be revived. An expired share may be extended by its owner, but previous sessions stay ended. Gateway immediately closes old streams and rejects all old refresh credentials and input frames. Read-only to control changes also require a fresh session, so a refresh cannot silently gain write permission. Check workspace ownership and authorization on every update. No changes to CDP grants through this endpoint.",
        "x-cli": "cloudbrowser links update br_123 grant_123 --permission view --expires-in 24h",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLiveViewGrant"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LiveViewGrant"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/operations/{operationId}": {
      "get": {
        "operationId": "getOperation",
        "summary": "Inspect an operation",
        "description": "",
        "x-cli": "cloudbrowser operations get op_123",
        "parameters": [
          {
            "name": "operationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get current usage",
        "description": "",
        "x-cli": "cloudbrowser usage --output json",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/b/{browserId}/session/exchange": {
      "post": {
        "operationId": "exchangeViewerTicket",
        "summary": "Exchange a Live View link",
        "description": "Exchange a short-lived viewer ticket minted through account/library authorization. Atomically consume once, checking recipient account binding, browser, current grant revision, permission and runtime. The viewer never receives the main account cookie or a workspace API key. Reusable sharing acceptance happens on cloudbrowser.tv, not by anonymous viewer redemption. Atomically consume the ticket once before ticketExpiresAt. Check browser, current grant revision and runtime. Link expiresAt is a separate authorization deadline (null means no absolute deadline). Issue a 5-minute access cookie and, for autoRenew=true, a rotating refresh cookie with a 30-minute inactivity deadline, both capped by expiresAt when finite. For autoRenew=false, no refresh cookie and idle/access expiration is 5 minutes capped by expiresAt. Ticket expiry after exchange does not end the session. A reusable share may create multiple recipient sessions; each ticket is single-use. Cookie secrets never appear in JSON. A paused browser cannot exchange tickets and must not be implicitly resumed. Use isolated viewer origin, no-store/no-referrer, strict CSP, same-origin credentials, exact Origin/CSRF checks and redacted logs. Check current link authorization, permission, revision, runtime and issuer access on every request and stream. Never accept a workspace API key here. Local cookies are independently named per viewer session (cb_access_{sessionId}, cb_refresh_{sessionId}) and omit Secure on localhost HTTP. No anonymous account authorization.",
        "servers": [
          {
            "url": "http://localhost:3001",
            "description": "Isolated local viewer"
          },
          {
            "url": "https://view.cloudbrowser.tv",
            "description": "Planned production viewer"
          }
        ],
        "security": [],
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Credentials are returned only via HttpOnly Set-Cookie; invalid/consumed tickets return 401.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewerSessionReceipt"
                }
              }
            }
          }
        },
        "x-viewer": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExchangeViewerTicket"
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/b/{browserId}/session/refresh": {
      "post": {
        "operationId": "refreshViewerSession",
        "summary": "Refresh the viewer session",
        "description": "Use the rotating refresh cookie even if access has expired. No CLI or workspace credential required. Reject ended/revoked sessions, inactive links, changed revisions/runtimes or expired idle deadline. Refresh is allowed indefinitely for actively used links with expiresAt=null. For finite expiresAt, stop all access when the next authorization check observes the deadline, including existing streams. Rotate atomically with reuse detection; serialize refresh across same-browser tabs. Access expiry=min(now+5 minutes, link expiresAt when finite); idle expiry=min(now+30 minutes, finite link deadline). Successful refresh reauthorizes existing same-session streams without page reload. Do not extend the link deadline or widen permissions. After inactivity, reopen the saved browser through account/library authorization to establish a new session. Use isolated viewer origin, no-store/no-referrer, strict CSP, same-origin credentials, exact Origin/CSRF checks and redacted logs. Check current link authorization, permission, revision, runtime and issuer access on every request and stream. Never accept a workspace API key here. Validate the recipient\u2019s active account/membership as well as the owner\u2019s source grant. Removal from the recipient library invalidates that recipient\u2019s sessions only. Local cookies are independently named per viewer session (cb_access_{sessionId}, cb_refresh_{sessionId}) and omit Secure on localhost HTTP. No anonymous account authorization.",
        "servers": [
          {
            "url": "http://localhost:3001",
            "description": "Isolated local viewer"
          },
          {
            "url": "https://view.cloudbrowser.tv",
            "description": "Planned production viewer"
          }
        ],
        "security": [
          {
            "ViewerRefresh": []
          }
        ],
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Non-secret session ID returned by exchange; matching cookies still required."
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials are returned only via HttpOnly Set-Cookie; invalid/consumed tickets return 401.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewerSessionReceipt"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-viewer": true,
        "x-implementation": "local"
      }
    },
    "/b/{browserId}/session/logout": {
      "post": {
        "operationId": "logoutViewerSession",
        "summary": "End the viewer session",
        "description": "End only the current viewer session, revoke its refresh family and close streams; clear cookies using the exact browser-specific path. Does not revoke the share URL: the accepted account can reopen its saved browser for a new session. Direct one-time tickets remain consumed. Missing/expired cookies are cleared and return 200. Closing a page alone is not reliable logout. Use isolated viewer origin, no-store/no-referrer, strict CSP, same-origin credentials, exact Origin/CSRF checks and redacted logs. Check current link authorization, permission, revision, runtime and issuer access on every request and stream. Never accept a workspace API key here. Validate the recipient\u2019s active account/membership as well as the owner\u2019s source grant. Removal from the recipient library invalidates that recipient\u2019s sessions only. Local cookies are independently named per viewer session (cb_access_{sessionId}, cb_refresh_{sessionId}) and omit Secure on localhost HTTP. No anonymous account authorization.",
        "servers": [
          {
            "url": "http://localhost:3001",
            "description": "Isolated local viewer"
          },
          {
            "url": "https://view.cloudbrowser.tv",
            "description": "Planned production viewer"
          }
        ],
        "security": [
          {
            "ViewerAccess": []
          },
          {
            "ViewerRefresh": []
          }
        ],
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Non-secret session ID returned by exchange; matching cookies still required."
          }
        ],
        "responses": {
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-viewer": true,
        "x-implementation": "local"
      }
    },
    "/browsers/{browserId}/grants/{grantId}/sessions": {
      "get": {
        "operationId": "listLiveViewSessions",
        "summary": "List a link\u2019s authorized viewer sessions",
        "description": "Workspace authorization required. Return metadata only; no cookies, tokens or private page content. Expired/ended sessions are excluded. Local implementation returns all matching entries with nextCursor=null; pagination is pending.",
        "x-cli": "cloudbrowser links sessions br_123 grant_123",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ViewerSession"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "items",
                    "nextCursor"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      },
      "delete": {
        "operationId": "endLiveViewSessions",
        "summary": "End all sessions for a Live View link",
        "description": "Workspace authorization required. Invalidate all current sessions, refresh families and outstanding entry tickets; close streams. The reusable share URL stays valid for future visits. Use revokeGrant to prevent future visits too. Does not operate on CDP.",
        "x-cli": "cloudbrowser links end-sessions br_123 grant_123 --yes",
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "grantId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Grant"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/library/browsers": {
      "get": {
        "operationId": "listBrowserLibrary",
        "summary": "List my owned and shared browsers",
        "description": "Signed-in account only. Owned and shared entries appear in one grid. A shared entry does not transfer ownership, create a browser, consume the recipient\u2019s browser concurrency, or grant lifecycle/reshare/CDP rights. Every preview subscription rechecks current authorization. Unavailable entries may remain visible without streaming private contents. Compute remains on the owning workspace. Local implementation returns all matching entries with nextCursor=null; pagination is pending.",
        "x-cli": "cloudbrowser library list --source all --output json",
        "x-account": true,
        "security": [
          {
            "AccountSession": []
          },
          {
            "AccountAccess": []
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BrowserLibraryEntry"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "items",
                    "nextCursor"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/library/shares/accept": {
      "post": {
        "operationId": "acceptSharedBrowser",
        "summary": "Accept a shared browser into my account",
        "description": "Requires signed-in account and a valid invitation. Reusable share links can be accepted by multiple accounts. Direct one-time invitations atomically bind to the first accepting account; subsequent redemption receives 409, including by the same account; use the saved library entry instead. Validate link expiration, revocation, revision and owner authority before accepting. Save an account-to-browser access reference, not ownership or a raw bearer token. Deduplicate by account/browser and retain a specific authorized source grant; never silently widen rights. Existing owner entries remain owned. Accepted members can later request fresh viewer tickets without replaying the original invite. Entry ticket expiration does not expire accepted membership; parent-link expiration/revocation does. Return the canonical /browsers/{id} URL. A paused browser can be added but must not be resumed automatically.",
        "x-cli": "cloudbrowser library add --url \"$LIVE_VIEW_URL\"",
        "x-account": true,
        "security": [
          {
            "AccountSession": []
          },
          {
            "AccountAccess": []
          }
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptSharedBrowser"
              }
            }
          }
        },
        "responses": {
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "browserId"
                  ],
                  "properties": {
                    "browserId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/library/browsers/{browserId}": {
      "delete": {
        "operationId": "removeSharedBrowser",
        "summary": "Remove a shared browser from my library",
        "description": "Remove only this recipient\u2019s saved access relationship and end that recipient\u2019s viewer sessions. Do not delete/pause the owner\u2019s browser, revoke the public share link or affect other recipients. Reject owned entries; owner deletion uses the lifecycle API. Re-adding requires a valid reusable invite or new invite if one-time access was removed.",
        "x-cli": "cloudbrowser library remove br_shared --yes",
        "x-account": true,
        "security": [
          {
            "AccountSession": []
          },
          {
            "AccountAccess": []
          }
        ],
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "const": true
                    }
                  }
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/library/browsers/{browserId}/viewer-ticket": {
      "post": {
        "operationId": "createLibraryViewerTicket",
        "summary": "Open an owned or saved shared browser",
        "description": "Account authorization required. Check ownership or saved membership AND its current parent grant permission, revision, expiration and issuer access; a saved list entry is not authority by itself. Browser must be running to stream; paused view shows an explicitly labeled authorized last frame without implicitly resuming. Mint a short-lived one-use runtime-bound ticket for the isolated viewer origin. The account shell passes only this ticket through a strict origin-checked handshake to the viewer, never its account cookie or workspace API key. Shared control grants no lifecycle, deletion, fork, reshare or CDP rights. Grid and fullscreen use this same authorization path; switching presentation alone does not duplicate runtime sessions.",
        "x-cli": "cloudbrowser library view br_shared",
        "x-account": true,
        "security": [
          {
            "AccountSession": []
          },
          {
            "AccountAccess": []
          }
        ],
        "parameters": [
          {
            "name": "browserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewerTicket"
                }
              }
            }
          },
          "default": {
            "description": "Structured error; HTTP 400/401/403/404/409/429/503 as applicable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-implementation": "local"
      }
    },
    "/billing/status": {
      "get": {
        "operationId": "getBillingStatus",
        "summary": "Get plan, runtime, estimated overage and billing access",
        "description": "Only the workspace billing owner can modify billing. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "default": {
            "description": "Authentication, ownership, billing state, validation or Stripe configuration error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/checkout": {
      "post": {
        "operationId": "createCheckout",
        "summary": "Start hosted Stripe Checkout for a new subscription",
        "description": "Only the workspace billing owner can modify billing. Additional usage is charged separately at period close. Monthly amount and extra hourly rate appear in Checkout.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Authentication, ownership, billing state, validation or Stripe configuration error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ]
      }
    },
    "/billing/portal": {
      "post": {
        "operationId": "createBillingPortal",
        "summary": "Open payment methods and invoices",
        "description": "Only the workspace billing owner can modify billing. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Authentication, ownership, billing state, validation or Stripe configuration error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/refresh": {
      "post": {
        "operationId": "refreshBilling",
        "summary": "Reconcile Stripe payment and subscription status",
        "description": "Only the workspace billing owner can modify billing. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "default": {
            "description": "Authentication, ownership, billing state, validation or Stripe configuration error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/upgrade-preview": {
      "post": {
        "operationId": "previewUpgrade",
        "summary": "Preview Basic to Pro proration; valid for five minutes",
        "description": "Only the workspace billing owner can modify billing. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "amountDueCents": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "prorationDate": {
                      "type": "integer"
                    },
                    "additionalIncludedSeconds": {
                      "type": "integer"
                    },
                    "periodEndsAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Authentication, ownership, billing state, validation or Stripe configuration error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing/plan": {
      "post": {
        "operationId": "changePlan",
        "summary": "Upgrade after payment or schedule downgrade/cancel for next period",
        "description": "Only the workspace billing owner can modify billing. ",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "updated",
                        "scheduled",
                        "payment_pending"
                      ]
                    },
                    "plan": {
                      "type": "string"
                    },
                    "effectiveAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "paymentUrl": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Authentication, ownership, billing state, validation or Stripe configuration error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanChangeRequest"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Account/workspace API key. Distinct from a per-browser CDP token or viewer ticket."
      },
      "ViewerAccess": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Secure-cb_viewer_access",
        "description": "Viewer-origin HttpOnly cookie; actual name is session-suffixed (cb_access_{sessionId} or cb_refresh_{sessionId}). Exact Origin is required. Local HTTP only omits Secure."
      },
      "ViewerRefresh": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Secure-cb_viewer_refresh",
        "description": "Viewer-origin HttpOnly cookie; actual name is session-suffixed (cb_access_{sessionId} or cb_refresh_{sessionId}). Exact Origin is required. Local HTTP only omits Secure."
      },
      "AccountSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-cb_account",
        "description": "Signed-in account on cloudbrowser.tv; HttpOnly, Secure, host-only, SameSite=Lax; CSRF protection required. Not sent to viewer origin."
      },
      "AccountAccess": {
        "type": "http",
        "scheme": "bearer",
        "description": "User-account access token issued by human CLI login. Not a workspace API key, CDP token or viewer refresh token."
      }
    },
    "schemas": {
      "Browser": {
        "type": "object",
        "required": [
          "id",
          "name",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "br_123"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "starting",
              "running",
              "pausing",
              "paused",
              "resuming",
              "failed",
              "deleting"
            ]
          },
          "parentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "runtimeGeneration": {
            "type": "integer"
          }
        }
      },
      "CreateBrowser": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          }
        }
      },
      "ForkBrowser": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "startPaused": {
            "type": "boolean",
            "default": true,
            "description": "Paused forks do not reserve running capacity. They retain independent profile writes."
          }
        }
      },
      "CreateGrant": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/CreateCdpGrant"
          },
          {
            "$ref": "#/components/schemas/CreateLiveViewGrant"
          }
        ],
        "discriminator": {
          "propertyName": "audience",
          "mapping": {
            "cdp": "#/components/schemas/CreateCdpGrant",
            "live-view": "#/components/schemas/CreateLiveViewGrant"
          }
        }
      },
      "Grant": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/CdpGrant"
          },
          {
            "$ref": "#/components/schemas/LiveViewGrant"
          }
        ]
      },
      "GrantIssue": {
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "grant",
              "cdpUrl"
            ],
            "properties": {
              "grant": {
                "$ref": "#/components/schemas/CdpGrant"
              },
              "cdpUrl": {
                "type": "string",
                "description": "Secret returned only on issuance."
              }
            }
          },
          {
            "type": "object",
            "required": [
              "grant",
              "liveViewUrl"
            ],
            "properties": {
              "grant": {
                "$ref": "#/components/schemas/LiveViewGrant"
              },
              "liveViewUrl": {
                "type": "string",
                "description": "Public account-shell entry URL, returned only at issuance: https://cloudbrowser.tv/view/{grantId}#share=... (reusable) or #ticket=... (one-time invitation). Sign in and accept into your library, then open /browsers/{browserId} fullscreen in the same workspace. Viewer transport remains isolated at view.cloudbrowser.tv. Raw secrets never appear in listings."
              }
            }
          }
        ]
      },
      "Operation": {
        "type": "object",
        "required": [
          "id",
          "browserId",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "browserId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "succeeded",
              "failed"
            ]
          },
          "error": {
            "$ref": "#/components/schemas/Error"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message",
          "requestId"
        ],
        "properties": {
          "code": {
            "type": "string",
            "examples": [
              "CONCURRENCY_LIMIT",
              "ALLOWANCE_EXHAUSTED",
              "BROWSER_NOT_RUNNING",
              "GRANT_EXPIRED",
              "GRANT_REVOKED",
              "RENEWAL_LIMIT",
              "CONFLICT"
            ]
          },
          "message": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          }
        }
      },
      "Usage": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "basic",
              "pro"
            ]
          },
          "includedSeconds": {
            "type": "integer"
          },
          "usedSeconds": {
            "type": "integer"
          },
          "runningBrowsers": {
            "type": "integer",
            "description": "Running or reserved slots, including pending or uncertain operations."
          },
          "concurrencyLimit": {
            "type": "integer"
          },
          "periodStartsAt": {
            "type": "string",
            "format": "date-time"
          },
          "periodEndsAt": {
            "type": "string",
            "format": "date-time"
          },
          "period": {
            "type": "string",
            "description": "UTC calendar month for Free; subscription billing period for paid plans."
          },
          "billingEnabled": {
            "type": "boolean"
          },
          "remainingSeconds": {
            "type": "integer",
            "minimum": 0
          },
          "estimatedOverageCents": {
            "type": "integer",
            "minimum": 0
          },
          "canManageBilling": {
            "type": "boolean"
          },
          "hasBillingAccount": {
            "type": "boolean"
          },
          "cancelAtPeriodEnd": {
            "type": "boolean"
          },
          "billingIssue": {
            "type": "boolean"
          },
          "pendingPlan": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "free",
              "basic",
              "pro",
              null
            ]
          },
          "subscriptionStatus": {
            "type": "string"
          },
          "billingMode": {
            "type": "string"
          }
        }
      },
      "CreateCdpGrant": {
        "type": "object",
        "required": [
          "audience"
        ],
        "additionalProperties": false,
        "properties": {
          "audience": {
            "const": "cdp"
          },
          "permission": {
            "const": "control"
          },
          "ttlSeconds": {
            "type": "integer",
            "minimum": 300,
            "maximum": 86400,
            "default": 3600
          },
          "autoRenew": {
            "type": "boolean",
            "default": false,
            "description": "CDP only. Authenticated CLI/console manager performs renewal; the CDP client cannot refresh itself."
          },
          "renewUntil": {
            "type": "string",
            "format": "date-time",
            "description": "Required for autoRenew; must be at least initial expiry and at most 7 days after issue. Immutable hard stop."
          }
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "autoRenew": {
                  "const": true
                }
              },
              "required": [
                "autoRenew"
              ]
            },
            "then": {
              "required": [
                "renewUntil"
              ]
            }
          }
        ]
      },
      "CreateLiveViewGrant": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "audience": {
            "const": "live-view"
          },
          "mode": {
            "type": "string",
            "enum": [
              "share",
              "one-time"
            ],
            "default": "share",
            "description": "share returns a reusable bearer URL. one-time returns a ticket URL redeemable once."
          },
          "permission": {
            "type": "string",
            "enum": [
              "view",
              "control"
            ],
            "default": "view",
            "description": "view is read-only: stream only, no mouse, keyboard, touch, clipboard writes, uploads, navigation commands or browser control. control allows browser interaction but no workspace, lifecycle or CDP administration. Enforced by the gateway, not just UI."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "default": null,
            "description": "Absolute link authorization deadline, measured from issuance/update; null means no expiration. Applies to new visits AND all existing sessions. Automatic refresh never moves this deadline."
          },
          "ticketTtlSeconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 900,
            "default": 300,
            "description": "One-time ticket lifetime, independent of the link authorization deadline. Shared links mint fresh tickets on each visit. Direct one-time links mint a ticket at issuance."
          },
          "autoRenew": {
            "type": "boolean",
            "default": true,
            "description": "Viewer webpage refreshes short-lived credentials while link authorization remains valid; no fixed maximum when expiresAt is null. If false, access lasts 5 minutes or until expiresAt, whichever comes first, with no refresh cookie."
          }
        },
        "required": [
          "audience"
        ]
      },
      "CdpGrant": {
        "type": "object",
        "required": [
          "id",
          "browserId",
          "audience",
          "permission",
          "expiresAt",
          "renewUntil",
          "autoRenew",
          "revoked"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "browserId": {
            "type": "string"
          },
          "audience": {
            "const": "cdp"
          },
          "permission": {
            "const": "control"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "renewUntil": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "autoRenew": {
            "type": "boolean",
            "description": "Renewal enabled when renewUntil is set; an authenticated manager must request renewals. The local console exposes manual renewal."
          },
          "revoked": {
            "type": "boolean"
          }
        }
      },
      "LiveViewGrant": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "browserId": {
            "type": "string"
          },
          "audience": {
            "const": "live-view"
          },
          "mode": {
            "type": "string",
            "enum": [
              "share",
              "one-time"
            ]
          },
          "permission": {
            "type": "string",
            "enum": [
              "view",
              "control"
            ],
            "default": "view",
            "description": "view is read-only: stream only, no mouse, keyboard, touch, clipboard writes, uploads, navigation commands or browser control. control allows browser interaction but no workspace, lifecycle or CDP administration. Enforced by the gateway, not just UI."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "default": null,
            "description": "Absolute link authorization deadline, measured from issuance/update; null means no expiration. Applies to new visits AND all existing sessions. Automatic refresh never moves this deadline."
          },
          "ticketTtlSeconds": {
            "type": "integer"
          },
          "entryExpiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Direct one-time URL only; null for reusable shares."
          },
          "autoRenew": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastOpenedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "activeSessions": {
            "type": "integer",
            "minimum": 0,
            "description": "Current authorized viewer sessions, not connected socket count."
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "revoked",
              "used"
            ]
          },
          "revoked": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "browserId",
          "audience",
          "mode",
          "permission",
          "expiresAt",
          "ticketTtlSeconds",
          "entryExpiresAt",
          "autoRenew",
          "createdAt",
          "lastOpenedAt",
          "activeSessions",
          "revision",
          "status",
          "revoked"
        ]
      },
      "ViewerSession": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "grantId",
          "browserId",
          "permission",
          "autoRenew",
          "accessExpiresAt",
          "idleExpiresAt",
          "ended",
          "authorizationExpiresAt",
          "accountId"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "grantId": {
            "type": "string"
          },
          "browserId": {
            "type": "string"
          },
          "permission": {
            "type": "string",
            "enum": [
              "view",
              "control"
            ],
            "default": "view",
            "description": "view is read-only: stream only, no mouse, keyboard, touch, clipboard writes, uploads, navigation commands or browser control. control allows browser interaction but no workspace, lifecycle or CDP administration. Enforced by the gateway, not just UI."
          },
          "autoRenew": {
            "type": "boolean"
          },
          "accessExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "idleExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "ended": {
            "type": "boolean"
          },
          "authorizationExpiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "default": null,
            "description": "Absolute link authorization deadline, measured from issuance/update; null means no expiration. Applies to new visits AND all existing sessions. Automatic refresh never moves this deadline."
          },
          "accountId": {
            "type": "string",
            "description": "Recipient account binding; never infer identity from browserId alone."
          }
        }
      },
      "ExchangeViewerTicket": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "ticket"
        ],
        "properties": {
          "ticket": {
            "type": "string",
            "description": "Single-use opaque secret from the URL fragment. Never log it."
          }
        }
      },
      "UpdateLiveViewGrant": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "permission": {
            "type": "string",
            "enum": [
              "view",
              "control"
            ],
            "default": "view",
            "description": "view is read-only: stream only, no mouse, keyboard, touch, clipboard writes, uploads, navigation commands or browser control. control allows browser interaction but no workspace, lifecycle or CDP administration. Enforced by the gateway, not just UI."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "default": null,
            "description": "Absolute link authorization deadline, measured from issuance/update; null means no expiration. Applies to new visits AND all existing sessions. Automatic refresh never moves this deadline."
          }
        },
        "required": [],
        "minProperties": 1,
        "description": "Share-mode Live View only. At least one property required. Every effective policy change invalidates all old sessions and tickets immediately and increments revision. The same reusable URL can then open a fresh session. Revoked grants cannot be revived. An expired share may be extended by its owner, but previous sessions stay ended."
      },
      "ViewerTicket": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ticket": {
            "type": "string",
            "description": "One-time secret minted after account/library authorization, bound to recipient account, browser, grant revision, permission and runtime. Return only on ticket issuance."
          },
          "ticketExpiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "description": "Isolated viewer URL carrying a single-use ticket in its fragment. The viewer clears the fragment before exchange."
          }
        },
        "required": [
          "ticket",
          "ticketExpiresAt",
          "url"
        ]
      },
      "BrowserLibraryEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "br_123"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "starting",
              "running",
              "pausing",
              "paused",
              "resuming",
              "failed",
              "deleting",
              "unavailable"
            ]
          },
          "parentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "runtimeGeneration": {
            "type": "integer"
          },
          "owner": {
            "type": "boolean"
          },
          "permission": {
            "type": "string",
            "enum": [
              "view",
              "control"
            ]
          },
          "ownerName": {
            "type": "string"
          },
          "shared": {
            "type": "boolean",
            "description": "Owned browser has at least one currently active sharing grant."
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "owner",
          "permission",
          "runtimeGeneration"
        ]
      },
      "AcceptSharedBrowser": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "linkId": {
            "type": "string"
          },
          "shareToken": {
            "type": "string"
          },
          "ticket": {
            "type": "string"
          }
        },
        "required": [
          "linkId"
        ],
        "oneOf": [
          {
            "required": [
              "shareToken"
            ],
            "not": {
              "required": [
                "ticket"
              ]
            }
          },
          {
            "required": [
              "ticket"
            ],
            "not": {
              "required": [
                "shareToken"
              ]
            }
          }
        ],
        "description": "Account authorization plus one invitation credential. Token read from URL fragment and removed immediately. Store a server-side pending authorization handle bound to the login transaction when redirecting to sign-in; never put the bearer secret in OAuth state, returnTo, query strings, analytics or localStorage."
      },
      "ViewerSessionReceipt": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "sessionId",
          "autoRenew",
          "permission",
          "expiresAt"
        ],
        "properties": {
          "sessionId": {
            "type": "string"
          },
          "autoRenew": {
            "type": "boolean"
          },
          "permission": {
            "type": "string",
            "enum": [
              "view",
              "control"
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CheckoutRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "plan"
        ],
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "basic",
              "pro"
            ]
          }
        }
      },
      "PlanChangeRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "plan"
        ],
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "basic",
              "pro"
            ]
          },
          "prorationDate": {
            "type": "integer",
            "description": "Required for Basic to Pro; use value from upgrade-preview."
          }
        }
      }
    }
  }
}
