Instances
Instances are running Chrome processes managed by PinchTab. Each managed instance has:
- an instance ID
- a profile
- a port
- a mode (
headlessorheaded) - an execution status
One profile can have at most one active managed instance at a time.
List Instances
curl http://localhost:9867/instances# Response: JSON array (see below)
# 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.
pinchtab instance start --profile prof_278be873 --mode headed --port 9999 --allow-domain wikipedia.org --allow-domain wikimedia.orgcurl -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 namemode: optional; useheadedfor a visible browser, anything else is treated as headlessport: optionalsecurityPolicy.allowedDomains: optional additive instance-scoped IDPI/domain allowlist entries
Notes:
- if
profileIdis omitted, PinchTab creates an auto-generated temporary profile - if
portis omitted, PinchTab allocates one from the configured instance port range - the CLI flag is
--profile, even though the API field isprofileId securityPolicy.allowedDomainsis merged with the server-levelsecurity.allowedDomainsbaseline 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.extensionPathson the server instead. By default, PinchTab uses the localextensions/directory under its state/config folder.
POST /instances/launch
/instances/launch is a compatibility alias for /instances/start.
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 namemode: optional;headedor headless by defaultport: optionalsecurityPolicy.allowedDomains: optional additive instance-scoped IDPI/domain allowlist entries
Important:
/instances/launchdoes not read aheadlessfield. Usemode:"headed"when you want a headed browser.nameis no longer supported on/instances/launch. Create the profile first viaPOST /profiles, then use the returnedidasprofileId.- request-supplied extension paths are rejected; configure
browser.extensionPathson the server instead. By default, PinchTab uses the localextensions/directory under its state/config folder.
Get One Instance
curl http://localhost:9867/instances/inst_ea2e747fcurl http://localhost:9867/instances/inst_ea2e747f Common status values:
startingrunningstoppingstoppederror
Instance responses include:
mode:"headless"or"headed"headless: boolean kept for compatibility
Get Instance Logs
pinchtab instance logs inst_ea2e747fcurl 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
pinchtab instance stop inst_ea2e747fcurl -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:
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
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:
pinchtab instance navigate inst_ea2e747f https://pinchtab.compinchtab 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
curl http://localhost:9867/instances/inst_ea2e747f/tabscurl http://localhost:9867/instances/inst_ea2e747f/tabs List All Tabs Across Running Instances
curl http://localhost:9867/instances/tabscurl 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
curl http://localhost:9867/instances/metricscurl http://localhost:9867/instances/metrics Attach An Existing Browser (CDP)
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 viachromedp.NewRemoteAllocator - registers the instance with
attached=true,attachType="cdp-bridge", and the routableurlset to the HTTP bridge URL (not the rawws://CDP URL) - preserves the original
cdpUrlas 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/versionURL
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.allowHostsmust allow thecdpUrlhost- if you pass an HTTP DevTools origin,
security.attach.allowSchemesmust includehttporhttps - 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
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:
baseUrlmust be a bare bridge origin; do not include credentials, query strings, fragments, or a pathbrowseris optional and accepts a provider or target name; withbrowser.targetsconfigured, an omitted value attaches to the default target- the orchestrator performs a health check before registering it
security.attach.allowHostsmust allow the bridge hostallowHosts: ["*"]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.