CustomDomain · Schema

BrowserSessionView

One cloud browser session as the popup may see it. `live_view_url` and `live_view_origin` are present only in `awaiting_sign_in` and `manual_assist` (`interactive` true) and, read only, in `checking`, `plan_ready`, `applying` and `verifying`; never in a terminal state. The address is a bearer: anyone holding it can drive the browser. While the customer signs in it is the live view of the newest tab of the remote browser on the provider's hosts or a social sign in host of the provider (a sign in popup), and the first tab's again once that one closes. On Browserbase it is always that tab's own view, so the debugging socket its `wss` parameter names is that tab's too. On Kernel (`vendor` is `Kernel`) the live view is on a host of the session's own browser (`https://.kernel.sh:8443/browser/live/...`, which `live_view_origin` names) and shows the browser's active tab; the key relay's socket is then `relay_socket`.

CompanyCustom DomainsDNSSSLEdge ProxySaaS InfrastructureWhite LabelMCP

Properties

Name Type Description
id string
state string
provider string
provider_name string
vendor string The browser hosting company.
domain string
site string The provider's registrable domain, for example namecheap.com. The popup shows it beside a lock while the customer signs in.
interactive boolean True when the customer may type in the live view.
live_view_url string
live_view_origin string The origin of `live_view_url`: the vendor's one pinned origin on Browserbase, the session browser's own host on Kernel.
relay_socket string Only while the person is in control (`awaiting_sign_in`, a hand back, `manual_assist`) and only when the session's vendor lets the person's own browser type into the page through a socket of its own (
relay_target string With `relay_socket`: the vendor's id of the tab the live view shows, for the key relay to attach to. Absent when it is not known yet; the popup then picks the first tab on the provider's site.
deadline string The next timer that matters: the sign in window in `awaiting_sign_in`, the approval window in `plan_ready`, the apply window in `applying`, the hard cap otherwise. A code prompt keeps the sign in wind
plan object
manual object Present in `manual_assist`: the records the customer adds by hand.
notice string `not_signed_in`: present after a `:signed-in` that landed on the sign in page, until the customer presses the button again. `switching_account`: after `:switch-account`, while the cloud browser signs
wrongAccount object In `awaiting_sign_in`: the account signed in does not hold the domain (the provider's DNS page said so twice, and its account switcher, where it has one, opened none that does). The customer chooses:
left_key object An API key the cloud browser made at the provider and CustomDomain did not keep (the provider's API refused it, it could not be stored, or the first write with it failed), so it is still live in the c
kept_key object The lasting API key the cloud browser made at the provider and CustomDomain kept for the connection, once it is kept (the session has recorded `key_connected`). Present from then on, in every state, s
api_activation object Present while the customer signs in (created, awaiting_sign_in) when the session will also turn the provider's API on for the account if it is off. `order`: it opens the provider's free order for it (
error object Present in `manual_assist` and `failed`, and in `checking` after the records changed under a plan. `browser_wrong_account` ends a session whose accounts did not hold the domain.
sign_in_viewport object
work_viewport object
sign_in_frame integer In `awaiting_sign_in` only: the bottom edge of the provider's sign in card on the remote screen, in CSS pixels from the top and under `sign_in_viewport`'s height, measured when the sign in page opened
signed_in_detected boolean In `awaiting_sign_in` only: the provider's sign in page gave way to its DNS panel page and the panel held across two polls, so the customer looks signed in. A hint: the popup then calls `:signed-in`,
check boolean In `awaiting_sign_in` only: the vendor reports the sign in tab with the title of a known human check page (Cloudflare's "Just a moment..."), so the popup asks the customer to tick the box. Only this b
signals object
handback object
form object
password_again object
no_live_page boolean The popup never shows the provider's page (every session since 2026-11-02, Kernel's since 2026-10-30): the sign in, a code, a push, a link wait and the backup are the popup's own screens. The only rea
live_check boolean With `no_live_page`: a human check (a CAPTCHA, a bot wall) is shown on the provider's real page, zoomed to it, because the person answers it there. Absent on Kernel, where the popup's own check screen
idp object In `awaiting_sign_in` on a `no_live_page` session: the identity provider step (`POST :idp`). While its `state` is `starting` or `form` the customer is in the vendor's form, not in the remote browser:
View JSON Schema on GitHub

JSON Schema

customdomain-ai-browser-session-view-schema.json Raw ↑
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/customdomain-ai/json-schema/customdomain-ai-browser-session-view-schema.json",
  "title": "BrowserSessionView",
  "description": "One cloud browser session as the popup may see it. `live_view_url` and `live_view_origin` are present only in `awaiting_sign_in` and `manual_assist` (`interactive` true) and, read only, in `checking`, `plan_ready`, `applying` and `verifying`; never in a terminal state. The address is a bearer: anyone holding it can drive the browser. While the customer signs in it is the live view of the newest tab of the remote browser on the provider's hosts or a social sign in host of the provider (a sign in popup), and the first tab's again once that one closes. On Browserbase it is always that tab's own view, so the debugging socket its `wss` parameter names is that tab's too. On Kernel (`vendor` is `Kernel`) the live view is on a host of the session's own browser (`https://<host>.kernel.sh:8443/browser/live/...`, which `live_view_origin` names) and shows the browser's active tab; the key relay's socket is then `relay_socket`.",
  "x-generated": "2026-11-04",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/customdomain-ai-openapi.yml#/components/schemas/BrowserSessionView",
  "type": "object",
  "required": [
    "id",
    "state",
    "provider",
    "domain"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "state": {
      "type": "string",
      "enum": [
        "created",
        "awaiting_sign_in",
        "checking",
        "plan_ready",
        "applying",
        "verifying",
        "manual_assist",
        "key_ready",
        "done",
        "done_manual",
        "cancelled",
        "expired",
        "abandoned",
        "failed"
      ]
    },
    "provider": {
      "type": "string"
    },
    "provider_name": {
      "type": "string"
    },
    "vendor": {
      "type": "string",
      "description": "The browser hosting company."
    },
    "domain": {
      "type": "string"
    },
    "site": {
      "type": "string",
      "description": "The provider's registrable domain, for example namecheap.com. The popup shows it beside a lock while the customer signs in."
    },
    "interactive": {
      "type": "boolean",
      "description": "True when the customer may type in the live view."
    },
    "live_view_url": {
      "type": "string",
      "format": "uri"
    },
    "live_view_origin": {
      "type": "string",
      "description": "The origin of `live_view_url`: the vendor's one pinned origin on Browserbase, the session browser's own host on Kernel."
    },
    "relay_socket": {
      "type": "string",
      "description": "Only while the person is in control (`awaiting_sign_in`, a hand back, `manual_assist`) and only when the session's vendor lets the person's own browser type into the page through a socket of its own (`Kernel`): a browser level debugging socket, `wss://<host>.kernel.sh:8443/browser/cdp?jwt=...`. A bearer like the live view; never in a terminal state. Absent on Browserbase, whose live view URL carries its own socket."
    },
    "relay_target": {
      "type": "string",
      "description": "With `relay_socket`: the vendor's id of the tab the live view shows, for the key relay to attach to. Absent when it is not known yet; the popup then picks the first tab on the provider's site."
    },
    "deadline": {
      "type": "string",
      "format": "date-time",
      "description": "The next timer that matters: the sign in window in `awaiting_sign_in`, the approval window in `plan_ready`, the apply window in `applying`, the hard cap otherwise. A code prompt keeps the sign in window open for ten minutes from its first sight and raises the hard cap by five minutes, to twenty at most."
    },
    "plan": {
      "$ref": "#/$defs/BrowserPlan"
    },
    "manual": {
      "type": "object",
      "description": "Present in `manual_assist`: the records the customer adds by hand.",
      "properties": {
        "records": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "value": {
                "type": "string"
              },
              "ttl": {
                "type": "integer"
              },
              "present": {
                "type": "boolean",
                "description": "The live tick from public DNS, refreshed about every 8 seconds."
              }
            }
          }
        },
        "guide_url": {
          "type": "string"
        }
      }
    },
    "notice": {
      "type": "string",
      "enum": [
        "not_signed_in",
        "switching_account"
      ],
      "description": "`not_signed_in`: present after a `:signed-in` that landed on the sign in page, until the customer presses the button again. `switching_account`: after `:switch-account`, while the cloud browser signs out of the provider; `interactive` is false and there is no `form` or `signals` until the sign in is back."
    },
    "wrongAccount": {
      "type": "object",
      "required": [
        "tried",
        "max"
      ],
      "description": "In `awaiting_sign_in`: the account signed in does not hold the domain (the provider's DNS page said so twice, and its account switcher, where it has one, opened none that does). The customer chooses: `:switch-account` to sign in with another account, or `:cancel`. The live view stays (they may switch accounts by hand in it and press \"I'm signed in\" again, which counts no new try); there is no `form`. On a session that ended because the third account did not hold the domain either, the last count without `account`. Never the domain on other nameservers: that ends with `browser_not_provider_dns`.",
      "properties": {
        "account": {
          "type": "string",
          "maxLength": 64,
          "description": "The name the provider shows for the account signed in (a user name, a customer number or an address, read from its header or account menu), shown to this customer only and never stored or logged; an address is masked to its first character and its domain (`j***@gmail.com`). Absent when the page shows none."
        },
        "tried": {
          "type": "integer",
          "minimum": 1,
          "maximum": 3,
          "description": "How many accounts were found not to hold the domain."
        },
        "max": {
          "type": "integer",
          "description": "How many may be tried: 3."
        }
      }
    },
    "left_key": {
      "type": "object",
      "required": [
        "name"
      ],
      "description": "An API key the cloud browser made at the provider and CustomDomain did not keep (the provider's API refused it, it could not be stored, or the first write with it failed), so it is still live in the customer's account and most providers offer no API to delete it. Present from then on, in every state, so the popup can name it and link the provider's page to delete it. Never the key itself.",
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 80,
          "description": "The name the key was made with, \"CustomDomain Acme 2026-10-21\"."
        },
        "page": {
          "type": "string",
          "format": "uri",
          "description": "The provider's page that lists and deletes its keys, when known."
        }
      }
    },
    "kept_key": {
      "type": "object",
      "required": [
        "rights"
      ],
      "description": "The lasting API key the cloud browser made at the provider and CustomDomain kept for the connection, once it is kept (the session has recorded `key_connected`). Present from then on, in every state, so the popup's last screen can name it: what to look for on the provider's key page and where to delete it later. Never the key itself. A key that was made and not kept is `left_key`, never this.",
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 80,
          "description": "The name the key was made with, \"CustomDomain Acme 2026-10-21 1528 3f9a\". Absent for a provider whose key is the account's own and has no name of its own (Namecheap)."
        },
        "page": {
          "type": "string",
          "format": "uri",
          "description": "The provider's page that lists and deletes its keys, when known."
        },
        "rights": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "transfer",
              "billing",
              "account"
            ]
          },
          "description": "What the key can do beyond the domain's DNS records, as `rails.cloud_browser.key.rights`: empty for a key that reaches DNS only."
        },
        "expires_at": {
          "type": "string",
          "format": "date-time",
          "description": "When the key ends. Absent for a key with no end date."
        },
        "reused": {
          "type": "boolean",
          "description": "`true` when no key was made in this session: the provider's key page still listed the row of the key an earlier sign in made for this same provider account (its name and account, as CustomDomain recorded them), and the key CustomDomain stored for it, checked again and still working, was used instead. No key was read off the page. `name` and `expires_at` are then that key's own. Absent for a key made in this session."
        }
      }
    },
    "api_activation": {
      "type": "object",
      "required": [
        "kind"
      ],
      "description": "Present while the customer signs in (created, awaiting_sign_in) when the session will also turn the provider's API on for the account if it is off. `order`: it opens the provider's free order for it (IONOS's free API access, in its shop, never above zero), whose terms and order the customer accepts and places themselves in the live view (`handback.reason` `order`). `access`: it turns the account's API access on and allows CustomDomain's address (Namecheap), the terms and the password it asks for being the customer's. The popup says so under Sign in, with the provider's terms when known.",
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "order",
            "access"
          ]
        },
        "terms": {
          "type": "string",
          "format": "uri",
          "description": "The provider's terms for its API product, when known."
        }
      }
    },
    "error": {
      "type": "object",
      "description": "Present in `manual_assist` and `failed`, and in `checking` after the records changed under a plan. `browser_wrong_account` ends a session whose accounts did not hold the domain.",
      "properties": {
        "code": {
          "type": "string"
        },
        "message": {
          "type": "string"
        }
      }
    },
    "sign_in_viewport": {
      "$ref": "#/$defs/BrowserViewport"
    },
    "work_viewport": {
      "$ref": "#/$defs/BrowserViewport"
    },
    "sign_in_frame": {
      "type": "integer",
      "minimum": 1,
      "description": "In `awaiting_sign_in` only: the bottom edge of the provider's sign in card on the remote screen, in CSS pixels from the top and under `sign_in_viewport`'s height, measured when the sign in page opened. The popup ends its frame just under it, so the links below the card stay out of sight. Sent only while the sign in tab still shows the page it was measured on and no human check or second window is on screen; once that changes it is gone for the rest of the session. Absent means show the whole screen."
    },
    "signed_in_detected": {
      "type": "boolean",
      "description": "In `awaiting_sign_in` only: the provider's sign in page gave way to its DNS panel page and the panel held across two polls, so the customer looks signed in. A hint: the popup then calls `:signed-in`, which checks the page itself. Absent means not detected."
    },
    "check": {
      "type": "boolean",
      "description": "In `awaiting_sign_in` only: the vendor reports the sign in tab with the title of a known human check page (Cloudflare's \"Just a moment...\"), so the popup asks the customer to tick the box. Only this boolean is sent, never the title. A page can fake its title, so it is a hint for the popup's words and nothing else: it never counts toward `signed_in_detected`, and nothing clicks or types because of it. Absent means no check page."
    },
    "signals": {
      "$ref": "#/$defs/BrowserSignals"
    },
    "handback": {
      "$ref": "#/$defs/BrowserHandback"
    },
    "form": {
      "$ref": "#/$defs/BrowserSignInForm"
    },
    "password_again": {
      "$ref": "#/$defs/BrowserPasswordAgain"
    },
    "no_live_page": {
      "type": "boolean",
      "description": "The popup never shows the provider's page (every session since 2026-11-02, Kernel's since 2026-10-30): the sign in, a code, a push, a link wait and the backup are the popup's own screens. The only real pages are a human check (`live_check`), a social provider's own sign in and the provider's terms, order or account details handed back (`handback` `agreement`, `order` or `account`). The relay still types for our form. Absent (false) from an older control plane, which keeps the old behaviour."
    },
    "live_check": {
      "type": "boolean",
      "description": "With `no_live_page`: a human check (a CAPTCHA, a bot wall) is shown on the provider's real page, zoomed to it, because the person answers it there. Absent on Kernel, where the popup's own check screen comes first."
    },
    "idp": {
      "type": "object",
      "required": [
        "provider",
        "domain",
        "state"
      ],
      "description": "In `awaiting_sign_in` on a `no_live_page` session: the identity provider step (`POST :idp`). While its `state` is `starting` or `form` the customer is in the vendor's form, not in the remote browser: `form`, `signals`, `signed_in_detected`, `check`, `interactive` and the relay are absent. `finishing`: the remote browser was made again on the signed in profile and is back on the provider's sign in page with a fresh `relay_socket` and `form`; the popup forwards the customer's press of the provider's own button. `failed`: `error_code` says why (the vendor's own codes such as `credentials_invalid`, `account_locked`, `unsupported_auth_method`, `captcha_blocked`, or the step's `vendor_error`, `expired`, `social_timeout`, `cancelled`); the customer tries again (`:idp`) or uses the password (`:idp-cancel`).",
      "properties": {
        "provider": {
          "type": "string",
          "enum": [
            "google",
            "apple"
          ]
        },
        "domain": {
          "type": "string",
          "description": "The identity provider's site, for example google.com."
        },
        "connection_id": {
          "type": "string",
          "description": "The vendor's connection, opaque."
        },
        "handoff_code": {
          "type": "string",
          "description": "Only while `state` is `form`: the vendor's single use bearer, which the popup exchanges in the customer's browser for a token of its own. Never logged or stored."
        },
        "state": {
          "type": "string",
          "enum": [
            "starting",
            "form",
            "finishing",
            "failed"
          ]
        },
        "error_code": {
          "type": "string",
          "description": "With `failed`: a short machine word."
        }
      }
    }
  },
  "$defs": {
    "BrowserHandback": {
      "type": "object",
      "required": [
        "reason"
      ],
      "description": "Present while the agent has paused in the middle of its work and waits for the customer: the provider asks for a code (a re-authentication or a step up), shows a check, (`agreement`) shows the terms its API page asks the account holder to accept before a key is made, or (`order`) is at the customer's part of the free order that turns the provider's API on (IONOS's checkout: its terms box and Buy now, which can place a temporary hold on their card), or (`account`) the provider's account lacks details only the customer may give before its API page makes a key (Gandi: the organization's postal address and phone), which they fill in on the provider's page and save. The agent never accepts terms, places an order or types account details for them. `interactive` is true and `live_view_url` is the page's own view: the customer answers in the live view, and the keys go from their browser to the vendor, never through the control plane. Once the prompt is gone from the page the agent goes on. A password the provider asks for again keeps `hand_back`.",
      "properties": {
        "reason": {
          "type": "string",
          "enum": [
            "code",
            "check",
            "agreement",
            "order",
            "account"
          ]
        },
        "relay": {
          "type": "boolean",
          "description": "With `reason: code` only: the provider asks for the code in a frame of its own (Squarespace's \"Verify to continue\") and the code is asked on the popup's own screen, with no live view taking input. The customer types it there and `POST :code` sends it; the agent types it into the provider's frame and presses its verify button. Absent (false) for a code the customer types in the live view."
        },
        "rejected": {
          "type": "boolean",
          "description": "With `relay`: the provider refused the last code sent, so the screen says so and asks again."
        }
      }
    },
    "BrowserPasswordAgain": {
      "type": "object",
      "required": [
        "field"
      ],
      "description": "Present after the sign in while the agent waits on the account password the provider asks for again (Infomaniak, before it shows a new API key), for a provider whose prompt was reviewed. The popup asks for the password on its own screen and types it into `field` from the customer's browser over `relay_socket`, as it does for the sign in form, then presses `submit` (Enter when it is absent). No keystroke goes through CustomDomain™ and the agent never sees the password. Absent otherwise: the customer answers on the provider's page.",
      "properties": {
        "field": {
          "type": "string",
          "description": "A CSS selector of the prompt's password field."
        },
        "submit": {
          "type": "string",
          "description": "A CSS selector of the control that sends it; absent when Enter sends it."
        }
      }
    },
    "BrowserPlan": {
      "type": "object",
      "required": [
        "hash",
        "base_hash",
        "changes",
        "unchanged",
        "approve_by"
      ],
      "properties": {
        "hash": {
          "type": "string",
          "description": "The sha256 of the plan, 64 hex characters. `:approve` sends all of it."
        },
        "base_hash": {
          "type": "string",
          "description": "The hash of the page the plan was made from."
        },
        "changes": {
          "type": "array",
          "description": "Removes, then changes, then adds. Values are shown in full.",
          "items": {
            "type": "object",
            "required": [
              "op",
              "type",
              "name"
            ],
            "properties": {
              "op": {
                "type": "string",
                "enum": [
                  "add",
                  "replace",
                  "remove"
                ]
              },
              "type": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "description": "The name as the provider's page shows it, `@` for the root."
              },
              "old": {
                "type": "string"
              },
              "new": {
                "type": "string"
              },
              "ttl": {
                "type": "integer",
                "description": "The rounded value that will be written."
              },
              "priority": {
                "type": "integer"
              },
              "reason": {
                "type": "string",
                "description": "Why a record that is not ours goes."
              }
            }
          }
        },
        "unchanged": {
          "type": "integer",
          "description": "How many records on the page stay as they are."
        },
        "approve_by": {
          "type": "string",
          "format": "date-time"
        }
      }
    },
    "BrowserRect": {
      "type": "object",
      "required": [
        "x",
        "y",
        "w",
        "h"
      ],
      "description": "The region of the page that matters now, in CSS pixels from the top left of the page's viewport and clipped to it, for the popup to zoom to: the form around the focused field, else the main form, else the challenge's box on a check page, else the main card on a Google or Apple page.",
      "properties": {
        "x": {
          "type": "integer"
        },
        "y": {
          "type": "integer"
        },
        "w": {
          "type": "integer"
        },
        "h": {
          "type": "integer"
        }
      }
    },
    "BrowserSignInForm": {
      "type": "object",
      "required": [
        "identifier",
        "steps"
      ],
      "description": "Present while the customer signs in, for a provider whose sign in form is known: the provider's form, so the popup's own form can drive it from the customer's browser over the vendor's debugging socket (the live view's `wss` parameter). No keystroke goes through CustomDomain™. Selectors are reviewed constants, not secrets; one that matches nothing on the day leaves the customer on the live page.",
      "properties": {
        "identifier": {
          "type": "string",
          "enum": [
            "email",
            "username",
            "email_or_username",
            "username_or_customer"
          ],
          "description": "What the first field takes."
        },
        "steps": {
          "type": "array",
          "description": "The form's pages in order (two for a provider that asks for the identifier first).",
          "items": {
            "type": "object",
            "required": [
              "fields",
              "submit"
            ],
            "properties": {
              "fields": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "kind",
                    "selector"
                  ],
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "username",
                        "password"
                      ]
                    },
                    "selector": {
                      "type": "string",
                      "description": "A CSS selector for document.querySelector."
                    }
                  }
                }
              },
              "submit": {
                "type": "string",
                "description": "A CSS selector of the control that sends the step."
              }
            }
          }
        },
        "social": {
          "type": "object",
          "description": "The provider's own social sign in buttons the session allows, by kind.",
          "additionalProperties": {
            "type": "string"
          },
          "properties": {
            "google": {
              "type": "string"
            },
            "apple": {
              "type": "string"
            },
            "facebook": {
              "type": "string"
            },
            "amazon": {
              "type": "string"
            },
            "microsoft": {
              "type": "string"
            },
            "github": {
              "type": "string"
            }
          }
        },
        "passkeyAlt": {
          "type": "string",
          "description": "The selector of the provider's \"use another way\" control on a passkey prompt."
        },
        "emailLink": {
          "type": "boolean",
          "description": "The provider may email a sign in link instead of a code."
        },
        "keyTyped": {
          "type": "boolean",
          "description": "The provider's page takes a value only from real key events (its submit stays disabled after a text insert), so the popup types each character as a key."
        },
        "continue": {
          "type": "string",
          "description": "The selector of the page's own \"Continue\", \"I've confirmed\" or \"Done\" control on the page that waits for a code or a link, for the popup to click from the customer's browser when they come back from their email. Absent when it was not seen (today, every provider)."
        },
        "resend": {
          "type": "string",
          "description": "The selector of the page's own \"Resend the code\" or \"Resend the link\" control. Absent when it was not seen (today, every provider)."
        },
        "forgotUrl": {
          "type": "string",
          "format": "uri",
          "description": "The provider's own \"forgot password\" page, https on the provider's own hosts, for the popup to open in a new tab of the customer's own browser (never in the cloud browser), so the reset email's link opens there too. Absent when it was not seen."
        },
        "consent": {
          "type": "string",
          "description": "The accept button of the provider's cookie banner, for the popup to press from the customer's browser before a social button the banner can cover (the `idp` step's `finishing`); the agent presses the same one later. Absent for a provider with no banner on record."
        }
      }
    },
    "BrowserSignals": {
      "type": "object",
      "required": [
        "host",
        "hostVerified"
      ],
      "description": "Present only while the customer is in control (in `awaiting_sign_in`, or while the agent has handed the page back), and only while fresh: what the page the customer sees asks of them, so the popup can say it in its own words. The control plane reads it about every 700 ms from the page's DOM with a fixed, read only probe in an isolated world of the page, never by the agent or a model, and it never holds a field's value. A page can show anything, so it is a hint for the popup's words and nothing else.",
      "properties": {
        "host": {
          "type": "string",
          "description": "The live page's host name; empty when its address is not https (a blank window)."
        },
        "hostVerified": {
          "type": "boolean",
          "description": "The host is one of the provider's hosts or one of its social sign in hosts, on an https address."
        },
        "field": {
          "type": "object",
          "required": [
            "kind"
          ],
          "description": "The focused editable field, if any.",
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "username",
                "password",
                "code",
                "text"
              ]
            },
            "label": {
              "type": "string",
              "maxLength": 60,
              "description": "What the page calls the field: its label, aria label or placeholder."
            },
            "maxLength": {
              "type": "integer",
              "minimum": 1,
              "maximum": 256
            }
          }
        },
        "codePrompt": {
          "type": "object",
          "required": [
            "kind"
          ],
          "description": "The page asks for a code.",
          "properties": {
            "kind": {
              "type": "string",
              "enum": [
                "sms",
                "app",
                "email",
                "unknown"
              ]
            },
            "digits": {
              "type": "integer",
              "minimum": 4,
              "maximum": 10
            }
          }
        },
        "push": {
          "type": "boolean",
          "description": "The page waits for an approval on another device. Never set with `linkPrompt`."
        },
        "linkPrompt": {
          "type": "boolean",
          "description": "The page says a sign in or approval link was sent (\"we sent you a link\", \"check your email to confirm\", \"approve this sign in\"), in English and in the providers' German, Spanish, French and Italian. The customer opens it in their mail and comes back; the popup may then click the form's `continue` control. Like a code prompt, it keeps the sign in open for 10 minutes from its first sight."
        },
        "social": {
          "type": "array",
          "description": "The social sign in buttons the page shows.",
          "items": {
            "type": "string",
            "enum": [
              "google",
              "apple",
              "facebook",
              "amazon",
              "microsoft",
              "github"
            ]
          }
        },
        "passkey": {
          "type": "boolean",
          "description": "The page asks for a passkey or a security key."
        },
        "check": {
          "type": "boolean",
          "description": "A CAPTCHA or a bot wall is showing."
        },
        "socialRefused": {
          "type": "string",
          "enum": [
            "google"
          ],
          "description": "The identity provider whose own page says it will not sign this browser in (Google's \"Couldn't sign you in\"). The popup shows its own screen instead, offering the provider's password."
        },
        "formError": {
          "type": "string",
          "maxLength": 120,
          "description": "The error the provider's own page shows near its form (\"That password is incorrect\"): the text of an alert, a live region, the description of an invalid field or a common error element, from an element that holds no field. Every word with an at sign and every run of four or more digits is replaced with an ellipsis. Never on a social sign in host's page."
        },
        "focus": {
          "$ref": "#/$defs/BrowserRect"
        },
        "viewport": {
          "type": "object",
          "required": [
            "w",
            "h"
          ],
          "description": "The page's own viewport (the remote browser's), in CSS pixels.",
          "properties": {
            "w": {
              "type": "integer"
            },
            "h": {
              "type": "integer"
            }
          }
        }
      }
    },
    "BrowserViewport": {
      "type": "object",
      "required": [
        "width",
        "height"
      ],
      "description": "The remote screen's size in CSS pixels, sent with the live view: `sign_in_viewport` while the customer signs in (the size the popup last asked for with `:viewport`, when it did), `work_viewport` after.",
      "properties": {
        "width": {
          "type": "integer"
        },
        "height": {
          "type": "integer"
        }
      }
    }
  }
}

Work with this as data

Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for schemas

4 MCP tools reach this
  • find_json_schemasBrowse and filter every JSON Schema in the catalog.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/customdomain-ai-browser-session-view"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.