<!-- Documentation revision 2.3.1: Documents CPU-aware browser launch, reserved thread capacity, and structured recovery without replay. -->
# Promptbox Browser API

## What does the API do?

Open pages, read content, fill forms, click links, manage tabs, take screenshots, and reuse saved login profiles through REST.

Endpoint: `https://promptbox.cn/api/browser.php`  
OpenAPI: [browser.openapi.json](https://promptbox.cn/api/browser.openapi.json)

## How to use it

1. Request an API key and send it as a bearer token on every JSON POST request.
2. Set `mode` to `agent` for the guided control-ID workflow, or omit it for low-level locators and coordinates.
3. Read the fresh page state after every action and use only its current IDs for the next action.
4. Give each state-changing action a unique `request_id`, inspect uncertain results before retrying, and close the session when finished.

Each operation has a 45-second deadline. An expired operation closes the affected session; do not reuse their IDs. Concurrent actions return HTTP 409, while session listing remains available. Inspect uncertain clicks or submissions before retrying.

Navigation follows up to ten redirects. Private-network and credential-bearing destinations are blocked. Human-verification challenges return `verification_required` and require human help. Shared capacity errors may close sessions; inspect saved observations before continuing.

## Authentication

Request an API key from [admin@promptbox.cn](mailto:admin@promptbox.cn). Send it on every request:

```http
Authorization: Bearer YOUR_BROWSER_API_KEY
Content-Type: application/json
```

## Agent interface

Set `mode` to `agent` to receive short-lived references for visible fields, buttons, links, and select options. Each action returns a fresh page observation.

### Workflow

1. Open a page:

```json
{"mode":"agent","action":"open","url":"https://example.com","request_id":"open-example-001"}
```

2. Read `page.page_id`, `page.session_id`, `page.tab_id`, `page.text`, and `page.controls`.

3. Fill fields using IDs from the latest `page.controls`:

```json
{
  "mode":"agent",
  "action":"fill_form",
  "page_id":"LATEST_PAGE_ID",
  "fields":{
    "field-1":"10 Market Street",
    "field-2":"San Francisco",
    "field-3":"option-2",
    "field-4":true
  },
  "request_id":"fill-address-001"
}
```

For a select, send an `option-N` ID returned with that control. For a checkbox or radio control, send `true` or `false`. `fill_form` processes up to 20 fields sequentially and does not submit. Read `applied` and the returned page before continuing.

4. Click a control from the new observation:

```json
{"mode":"agent","action":"click","page_id":"NEW_PAGE_ID","target_id":"button-5","request_id":"submit-form-001"}
```

5. Read the returned page, then close the session:

```json
{"mode":"agent","action":"close","session_id":"SESSION_ID","request_id":"close-session-001"}
```

### Agent actions

| Action | Parameters | Behavior |
|---|---|---|
| `open` | `url` | Opens the URL in the caller's most recently used session, or creates one when none exists. |
| `navigate` | `page_id` or `session_id`, `url` | Navigates the selected session. |
| `snapshot` | `page_id` or `session_id`; optional `focus`, `max_chars` | Returns fresh text and control references. |
| `fill_form` | `page_id`, `fields` | Fills up to 20 fields sequentially without submitting. |
| `fill` | `page_id`, `field_id`, `value` or `checked` | Fills text/select controls or sets a checkbox/radio. |
| `click` | `page_id`, `target_id` | Clicks an observed button or link. |
| `follow` | `page_id`, `link_id` | Clicks an observed link. |
| `press` | `page_id`, `field_id` or `target_id`, `key` | Sends a keyboard key to a control. |
| `scroll` | `page_id`; optional `x`, `y` | Scrolls and returns a fresh observation. |
| `tabs` | `page_id` or `session_id` | Lists tabs in the session. |
| `switch_tab` | `page_id` or `session_id`, `tab_id` | Activates a tab and returns its page. |
| `close_tab` | `page_id` or `session_id`, `tab_id` | Closes a tab. |
| `sessions` | none | Lists the caller's active sessions. |
| `close` | `page_id` or `session_id` | Closes the session and all tabs. |

All agent actions except `snapshot`, `tabs`, and `sessions` require a caller-generated `request_id` containing 8–160 letters, digits, hyphens, or underscores. Use a new ID for each intended action. Reusing an ID within 24 hours returns `duplicate:true` without dispatching it again.

Each observation replaces the preceding control references for that tab. A stale reference is rejected and normally returns a fresh page. When `truncated` is true, use `snapshot` with `focus` to filter controls by a label or text fragment.

### Agent response fields

| Field | Meaning |
|---|---|
| `ok` | The API processed the request. |
| `action_ok` | The requested browser action completed. |
| `action` | Requested action name. |
| `applied` | Field IDs filled before completion or failure. |
| `page` | Fresh page observation, when available. |
| `page.status` | `ready`, `loading`, `login_required`, or `verification_required`. `loading` indicates incomplete rendered content; inspect once more before drawing conclusions. |
| `page.controls` | Up to 40 visible controls and their current IDs. |
| `page.total_controls` | Visible control count before filtering/capping. |
| `page.truncated` | Whether controls were omitted. |
| `page.tabs` | Tabs with ID, URL, and active state. |
| `uncertain` | Whether an action may have taken effect despite an incomplete response. |
| `rejected` | Whether the action was rejected before dispatch. |
| `code` | Machine-readable condition such as `capacity_full`. |

`page.status:"ready"` means the page is readable. Determine completion from page text, controls, or an external receipt. After an uncertain click, key press, or submission, inspect current state before sending another mutation.

### Complete example: fill and submit a form

Open the form page:

```json
{
  "mode":"agent",
  "action":"open",
  "url":"https://merchant.example/order",
  "request_id":"order-page-001"
}
```

Assume the response contains these controls:

```json
{
  "ok":true,
  "action_ok":true,
  "page":{
    "page_id":"PAGE_A",
    "session_id":"SESSION_A",
    "status":"ready",
    "controls":[
      {"id":"field-1","tag":"input","label":"Street address","type":"text"},
      {"id":"field-2","tag":"select","label":"Drink","options":[
        {"id":"option-1","label":"Black milk tea"},
        {"id":"option-2","label":"Jasmine milk tea"}
      ]},
      {"id":"field-3","tag":"input","label":"Delivery","type":"checkbox","checked":false},
      {"id":"button-4","tag":"button","label":"Continue"}
    ]
  }
}
```

Fill the observed fields:

```json
{
  "mode":"agent",
  "action":"fill_form",
  "page_id":"PAGE_A",
  "fields":{
    "field-1":"10 Market Street",
    "field-2":"option-2",
    "field-3":true
  },
  "request_id":"order-fields-001"
}
```

The fill response returns another observation, such as `page_id:"PAGE_B"`. Use its current button ID to continue:

```json
{
  "mode":"agent",
  "action":"click",
  "page_id":"PAGE_B",
  "target_id":"button-4",
  "request_id":"order-continue-001"
}
```

Read the returned `page.text`, `page.controls`, and URL. If another page must be opened in the same session, use `navigate`:

```json
{
  "mode":"agent",
  "action":"navigate",
  "session_id":"SESSION_A",
  "url":"https://merchant.example/order/status",
  "request_id":"order-status-001"
}
```

Close the session when finished:

```json
{
  "mode":"agent",
  "action":"close",
  "session_id":"SESSION_A",
  "request_id":"order-close-001"
}
```

## Low-level interface

Omit `mode` to address Playwright locators, coordinates, tabs, and profiles directly.

### Create and inspect a session

```sh
curl https://promptbox.cn/api/browser.php \
  -H "Authorization: Bearer $PROMPTBOX_BROWSER_KEY" \
  -H "Content-Type: application/json" \
  --data '{"action":"create","url":"https://example.com"}'
```

The response contains `session_id`, `tab_id`, `url`, `title`, `text`, `elements`, `accessibility`, `frames`, `last_dialog`, and `expires_in_seconds`.

```json
{"action":"snapshot","session_id":"SESSION_ID"}
```

### Low-level actions

| Action | Parameters | Behavior |
|---|---|---|
| `sessions` | none | Lists active session IDs, tab counts, busy state, and idle seconds. |
| `create` | optional `url`, `profile`, `width`, `height` | Creates an isolated context and initial tab. |
| `navigate` | `session_id`, `url`; optional `tab_id` | Navigates a tab. |
| `snapshot` | `session_id`; optional `tab_id`, `frame` | Returns visible text, accessibility data, controls, frames, and the last dialog. |
| `click` | locator fields, or `x` and `y` | Clicks a control or viewport position. |
| `fill` | locator fields, `value` | Replaces text in an input, textarea, or editable control. |
| `press` | locator fields, `key` | Sends a keyboard key. |
| `select` | locator fields, `value` | Selects one or more native options. |
| `check` | locator fields, `checked` | Sets a checkbox or radio control. |
| `hover` | locator fields | Moves the pointer over a control. |
| `scroll` | optional `x`, `y` | Scrolls with the mouse wheel. |
| `drag` | `from_x`, `from_y`, `to_x`, `to_y` | Drags between viewport coordinates. |
| `wait` | locator fields; optional `state`, `timeout_ms` | Waits for a locator state. |
| `screenshot` | `session_id`; optional `tab_id`, `timeout_ms` | Returns a viewport PNG as base64. |
| `tabs` | `session_id` | Lists tabs, titles, URLs, and active state. |
| `new_tab` | `session_id`; optional `url` | Creates and activates a tab. |
| `switch_tab` | `session_id`, `tab_id` | Activates a tab. |
| `close_tab` | `session_id`; optional `tab_id` | Closes a tab. |
| `back`, `forward`, `reload` | `session_id`; optional `tab_id` | Uses tab history or reloads. |
| `evaluate` | `session_id`, `expression`; optional `tab_id` | Evaluates JavaScript in the page and returns JSON data. |
| `dialog_policy` | `session_id`, `mode`; optional `prompt_text` | Accepts or dismisses the next JavaScript dialog. |
| `save_profile` | `session_id`, `profile` | Saves cookies, local storage, and IndexedDB. |
| `delete_profile` | `profile` | Deletes a saved profile. |
| `close` | `session_id` | Closes the browser context and all tabs. |

### Locators

Supply one locator form:

```json
{"selector":"#searchboxinput"}
```

```json
{"role":"button","name":"Search","exact":true}
```

```json
{"label":"Street address","exact":true}
```

```json
{"text":"Continue","exact":false}
```

Add `frame` with an iframe CSS selector to resolve a locator inside a frame. If a locator matches multiple elements, refine it before acting.

Example sequence:

```json
{"action":"fill","session_id":"SESSION_ID","label":"Search","value":"milk tea"}
```

```json
{"action":"press","session_id":"SESSION_ID","label":"Search","key":"Enter"}
```

```json
{"action":"wait","session_id":"SESSION_ID","selector":"[role=feed]","state":"visible","timeout_ms":25000}
```

```json
{"action":"snapshot","session_id":"SESSION_ID"}
```

```json
{"action":"close","session_id":"SESSION_ID"}
```

## Profiles

Save a profile:

```json
{"action":"save_profile","session_id":"SESSION_ID","profile":"shop-login"}
```

Open a new session with it:

```json
{"action":"create","profile":"shop-login","url":"https://example.com/account"}
```

Delete it:

```json
{"action":"delete_profile","profile":"shop-login"}
```

Profiles contain cookies, local storage, and IndexedDB. They do not contain open tabs or session storage.

## Limits and errors

1. Destinations must use HTTP or HTTPS on port 80 or 443.
2. Maximum 2 simultaneous sessions and 4 tabs per session.
3. Agent observations return up to 40 controls; low-level snapshots return up to 150 controls.
4. Idle sessions expire after 3 minutes of inactivity (cleanup runs every minute, so actual closure occurs after approximately 3–4 idle minutes); maximum lifetime is 2 hours.
5. Interaction `timeout_ms` range: 1–25,000 ms; default 12,000 ms.
6. Request limit: 100 KB. Response limit: 8 MB.
7. Screenshots capture the current viewport.
8. Downloads, uploads, permission grants, media calling, extensions, and interactive takeover are unavailable.

```json
{"ok":false,"error":"Error message","retry_note":"Recovery guidance"}
```

| HTTP status | Meaning |
|---|---|
| `400` | Invalid input or browser action error. |
| `401` | Missing or invalid API key. |
| `404` | Unknown or expired session, tab, or profile. |
| `408` | Browser action timed out. |
| `409` | Session is busy. |
| `410` | A known session expired or was reclaimed; open a new session. |
| `413` | Request or result is too large. |
| `429` | Rate or browser capacity limit reached. |
| `502` | Browser worker connection was lost. |
| `503` | Browser worker is unavailable. |
| `504` | Operation deadline exceeded. |

An HTTP `200` or `ok:true` can accompany a website challenge or error page. Read the returned URL, status, and page content.

## Health

```sh
curl "https://promptbox.cn/api/browser.php?health=1" \
  -H "Authorization: Bearer $PROMPTBOX_BROWSER_KEY"
```

The public response reports readiness and the API version. Detailed process and resource information remains private.

## Navigation and recovery

URL navigation may retry one transient connection failure within a 30-second budget. Clicks, key presses, form submissions, and POST requests are not automatically retried.

A `loading` observation is incomplete. Request one read-only snapshot before drawing conclusions; do not repeat the preceding action. Use `focus` to find controls omitted from the default snapshot. Responses still return at most 40 controls.

`navigate`, `snapshot`, and `tabs` may omit `session_id` only when exactly one session belongs to the caller. Supply it explicitly when more than one session is active.


## Graceful failure and recovery

1. Inspect both HTTP status and `ok` / `action_ok`. Failures may include `code`, `session_closed`, `uncertain`, `rejected`, `recovery`, `request_id`, and `retry_after`.
2. `capacity_full` with `rejected:true` and `uncertain:false` means no action started. Wait at least `retry_after` seconds before an intentional new attempt; do not poll or automatically repeat submissions.
3. `resource_limit`, `capacity_reclaimed`, `browser_disconnected`, `session_expired`, and `session_unavailable` identify a closed session. Its old page and control IDs are no longer usable. Open a new session when capacity is available.
4. `operation_timeout` ends the affected session after 45 seconds. A resource shutdown interrupts pending requests promptly. `service_timeout` and `service_unavailable` describe transport or startup failures.
5. If `uncertain:true`, a click or submission may have taken effect. Check the destination or saved observations before attempting it again. Opening a fresh session does not make repeating the action safe. Actions are never automatically replayed by recovery.
6. Version 2.3.1 runs on two allowed CPUs with one raster thread to limit background thread pools. The helper thread ceiling is 110 against the verified hosting account allowance of 120, preserving ten threads for other account work; its browser memory ceiling remains 1.5 GiB. New sessions and tabs reserve 75 and 20 threads respectively before admission (a warm browser reserves 20 for a new session). These are admission estimates, not guarantees for arbitrary pages. New sessions and tabs are rejected before the hard resource ceiling. Under pressure, sessions idle for over one minute may be reclaimed early; active agent actions and observations are protected from idle cleanup. The hard resource ceiling can still close all sessions.
7. Automatic startup uses a restricted local launcher so browser processes do not inherit the web worker's virtual-address-space ceiling; the account thread and browser physical-memory safeguards still apply. Following a resource shutdown, a short cooldown prevents repeated browser restarts. A hung Chromium shutdown restarts the private worker; a later request can start it again through bounded startup checks. Recent closure details are owner-scoped and bounded in memory; after a worker restart, an old ID returns `session_unavailable`.

```json
{"ok":false,"code":"resource_limit","error":"Resource budget exceeded. Browser sessions released.","session_closed":true,"uncertain":true,"rejected":false,"recovery":"open_new_session","request_id":"your-request-id"}
```
