Instances

Instances are running Chrome processes managed by PinchTab. Each managed instance has:

  • an instance ID
  • a profile
  • a port
  • a mode (headless or headed)
  • an execution status

One profile can have at most one active managed instance at a time.

List Instances

terminal
curl http://localhost:9867/instances# Response: JSON array (see below)
Response
# CLI Alternative (human-readable by default)
pinchtab instances
# Output: inst_0a89  9999  headed  running

pinchtab instances --json              # Full JSON response

pinchtab instances is the simplest way to inspect the current fleet from the CLI.

Response shape:

[
  {
    "id": "inst_0a89a5bb",
    "profileId": "prof_278be873",
    "profileName": "instance-1741410000000",
    "port": "9999",
    "mode": "headed",
    "headless": false,
    "status": "running",
    "securityPolicy": {
      "allowedDomains": ["127.0.0.1", "localhost", "::1", "wikipedia.org"]
    }
  }
]

GET /instances returns a bare JSON array, not an envelope like {"instances":[...]}. Each instance response includes both mode ("headless" or "headed") and the legacy-compatible headless boolean.

Start An Instance

POST /instances/start

Use /instances/start when you want to start by profile ID or profile name, or let PinchTab create a temporary profile.

terminal
pinchtab instance start --profile prof_278be873 --mode headed --port 9999 --allow-domain wikipedia.org --allow-domain wikimedia.org
curl -X POST http://localhost:9867/instances/start \  -H "Content-Type: application/json" \  -d '{"profileId":"prof_278be873","mode":"headed","port":"9999","securityPolicy":{"allowedDomains":["wikipedia.org","wikimedia.org"]}}'

Request body:

  • profileId: optional; accepts a profile ID or an existing profile name
  • mode: optional; use headed for a visible browser, anything else is treated as headless
  • port: optional
  • securityPolicy.allowedDomains: optional additive instance-scoped IDPI/domain allowlist entries

Notes:

  • if profileId is omitted, PinchTab creates an auto-generated temporary profile
  • if port is omitted, PinchTab allocates one from the configured instance port range
  • the CLI flag is --profile, even though the API field is profileId
  • securityPolicy.allowedDomains is merged with the server-level security.allowedDomains baseline for that instance only
  • you can widen a single instance without changing the server default. For example, {"securityPolicy":{"allowedDomains":["*"]}} makes that instance unrestricted while other instances still use the server baseline
  • request-supplied extension paths are rejected; configure browser.extensionPaths on the server instead. By default, PinchTab uses the local extensions/ directory under its state/config folder.

POST /instances/launch

/instances/launch is a compatibility alias for /instances/start.

terminal
curl -X POST http://localhost:9867/instances/launch \  -H "Content-Type: application/json" \  -d '{"profileId":"prof_278be873","mode":"headed","securityPolicy":{"allowedDomains":["wikipedia.org"]}}'
curl -X POST http://localhost:9867/instances/launch \  -H "Content-Type: application/json" \  -d '{"profileId":"prof_278be873","mode":"headed","securityPolicy":{"allowedDomains":["wikipedia.org"]}}'

Request body:

  • profileId: optional existing profile ID or existing profile name
  • mode: optional; headed or headless by default
  • port: optional
  • securityPolicy.allowedDomains: optional additive instance-scoped IDPI/domain allowlist entries

Important:

  • /instances/launch does not read a headless field. Use mode:"headed" when you want a headed browser.
  • name is no longer supported on /instances/launch. Create the profile first via POST /profiles, then use the returned id as profileId.
  • request-supplied extension paths are rejected; configure browser.extensionPaths on the server instead. By default, PinchTab uses the local extensions/ directory under its state/config folder.

Get One Instance

terminal
curl http://localhost:9867/instances/inst_ea2e747f
curl http://localhost:9867/instances/inst_ea2e747f

Common status values:

  • starting
  • running
  • stopping
  • stopped
  • error

Instance responses include:

  • mode: "headless" or "headed"
  • headless: boolean kept for compatibility

Get Instance Logs

terminal
pinchtab instance logs inst_ea2e747f
curl http://localhost:9867/instances/inst_ea2e747f/logs

Response is plain text. There is also an SSE stream at GET /instances/{id}/logs/stream.

Stop An Instance

terminal
pinchtab instance stop inst_ea2e747f
curl -X POST http://localhost:9867/instances/inst_ea2e747f/stop

Stopping an instance preserves the profile unless it was a temporary auto-generated profile.

Start By Profile

You can also start an instance from a profile-oriented route:

terminal
curl -X POST http://localhost:9867/profiles/prof_278be873/start \  -H "Content-Type: application/json" \  -d '{"headless":false,"port":"9999","securityPolicy":{"allowedDomains":["wikipedia.org"]}}'
curl -X POST http://localhost:9867/profiles/prof_278be873/start \  -H "Content-Type: application/json" \  -d '{"headless":false,"port":"9999","securityPolicy":{"allowedDomains":["wikipedia.org"]}}'

This route accepts a profile ID or profile name in the path. Unlike /instances/start and /instances/launch, its request body uses headless instead of mode.

Open A Tab In An Instance

terminal
curl -X POST http://localhost:9867/instances/inst_ea2e747f/tabs/open \  -H "Content-Type: application/json" \  -d '{"url":"https://pinchtab.com"}'
curl -X POST http://localhost:9867/instances/inst_ea2e747f/tabs/open \  -H "Content-Type: application/json" \  -d '{"url":"https://pinchtab.com"}'

There is no dedicated instance-scoped tab open CLI command. The CLI shortcut is:

terminal
pinchtab instance navigate inst_ea2e747f https://pinchtab.com
pinchtab instance navigate inst_ea2e747f https://pinchtab.com

That command opens a blank tab for the instance and then navigates it.

List Tabs For One Instance

terminal
curl http://localhost:9867/instances/inst_ea2e747f/tabs
curl http://localhost:9867/instances/inst_ea2e747f/tabs

List All Tabs Across Running Instances

terminal
curl http://localhost:9867/instances/tabs
curl http://localhost:9867/instances/tabs

This is the fleet-wide tab listing endpoint. It is different from GET /tabs, which is shorthand or bridge scoped.

List Metrics Across Instances

terminal
curl http://localhost:9867/instances/metrics
curl http://localhost:9867/instances/metrics

Attach An Existing Browser (CDP)

terminal
curl -X POST http://localhost:9867/instances/attach \  -H "Content-Type: application/json" \  -d '{    "name":"shared-chrome",    "cdpUrl":"ws://127.0.0.1:9222/devtools/browser/...",    "provider":"chrome",    "browser":"chrome-local"  }'
curl -X POST http://localhost:9867/instances/attach \  -H "Content-Type: application/json" \  -d '{    "name":"shared-chrome",    "cdpUrl":"ws://127.0.0.1:9222/devtools/browser/...",    "provider":"chrome",    "browser":"chrome-local"  }'

What this does:

  • spawns a child pinchtab bridge --cdp-attach ... process that wraps the external CDP browser via chromedp.NewRemoteAllocator
  • registers the instance with attached=true, attachType="cdp-bridge", and the routable url set to the HTTP bridge URL (not the raw ws:// CDP URL)
  • preserves the original cdpUrl as metadata in the API response
  • routes /tabs, /snapshot, /action, /screenshot, etc. through the child bridge

Accepted cdpUrl shapes:

  • browser-level WebSocket URL: ws://host:port/devtools/browser/<id>
  • HTTP DevTools origin: http://host:port (resolved through /json/version)
  • HTTP /json/version URL

Page-level URLs (/devtools/page/...) are rejected.

browser is optional and accepts a provider name (chrome, cloak) or a configured target name from browser.targets. When browser.targets is configured, an omitted value attaches to the configured default target and the target’s browser is used. If provider is also present, it must agree with the browser value. Without browser.targets, provider is chrome (default) or cloak; the cloak browser disables PinchTab JS overlays on the assumption that the external browser owns native fingerprint behavior.

Notes:

  • there is no CLI attach command
  • attach is allowed only when enabled in config under security.attach
  • security.attach.allowHosts must allow the cdpUrl host
  • if you pass an HTTP DevTools origin, security.attach.allowSchemes must include http or https
  • stopping the attached instance shuts down the child PinchTab bridge but leaves the external browser process running
  • allowHosts: ["*"] is a documented, non-default, security-reducing override

Attach An Existing Bridge

terminal
curl -X POST http://localhost:9867/instances/attach-bridge \  -H "Content-Type: application/json" \  -d '{    "name":"shared-bridge",    "baseUrl":"http://10.0.12.24:9868",    "token":"bridge-secret-token",    "browser":"chrome-local"  }'
curl -X POST http://localhost:9867/instances/attach-bridge \  -H "Content-Type: application/json" \  -d '{    "name":"shared-bridge",    "baseUrl":"http://10.0.12.24:9868",    "token":"bridge-secret-token",    "browser":"chrome-local"  }'

Notes:

  • baseUrl must be a bare bridge origin; do not include credentials, query strings, fragments, or a path
  • browser is optional and accepts a provider or target name; with browser.targets configured, an omitted value attaches to the default target
  • the orchestrator performs a health check before registering it
  • security.attach.allowHosts must allow the bridge host
  • allowHosts: ["*"] is a documented, non-default, security-reducing override. It disables host allowlisting entirely and allows any reachable bridge host with an allowed scheme. Use it only on isolated, operator-controlled networks.