# Read a cloud browser session

> Read a cloud browser session: CustomDomain™ API reference in the cloud browser group. Covers request parameters, the request body, responses and error codes.

Source: https://docs.customdomain.ai/docs/api-reference/cloud-browser/getBrowserSession

## `GET https://api.customdomain.ai/v1/browser-sessions/{sid}`

The session view. Every call is the popup's heartbeat: a session that is not polled for 90 seconds before approval is ended and its browser released. The response is `Cache-Control: no-store`, because the view can carry the live view address, which anyone holding can drive.

### Authentication

`browserSession`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `sid` | path | string | yes | The cloud browser session id, `bs_` and 32 hex characters. |

### Responses

| Status | Description |
| --- | --- |
| `200` | The session as the popup may see it. |
| `401` | `browser_expired`: the token is wrong, expired, or for another session. |
| `404` | Error envelope: {code, title, details}. |
| `503` | Error envelope: {code, title, details}. |

### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `state` | `created`, `awaiting_sign_in`, `checking`, `plan_ready`, `applying`, `verifying`, `manual_assist`, `done`, `done_manual`, `cancelled`, `expired`, `abandoned`, `failed` | yes |  |
| `provider` | string | yes |  |
| `provider_name` | string | no |  |
| `vendor` | string | no | The browser hosting company. |
| `domain` | string | yes |  |
| `site` | string | no | The provider's registrable domain, for example namecheap.com. The popup shows it beside a lock while the customer signs in. |
| `interactive` | boolean | no | True when the customer may type in the live view. |
| `live_view_url` | string | no |  |
| `live_view_origin` | string | no |  |
| `deadline` | string | no | 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` | object | no |  |
| `manual` | object | no | Present in `manual_assist`: the records the customer adds by hand. |
| `notice` | `not_signed_in`, `switching_account` | no | `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` | object | no | 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`. |
| `error` | object | no | 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 | no | 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. |
| `work_viewport` | object | no | 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. |
| `sign_in_frame` | integer | no | 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` | boolean | no | 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` | boolean | no | 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` | object | no | 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. |
| `handback` | object | no | 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, or (`agreement`) shows the terms its API page asks the account holder to accept before a key is made, which the agent never accepts 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`. |
| `form` | object | no | 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. |

The full specification is served at `GET https://api.customdomain.ai/v1/openapi.json`.
