Developer Documentation

PBX Local API v1

A loopback HTTP/JSON API for creating, editing, validating, launching, stopping, and automating PBX virtual browser profiles from local software.

Default endpointhttp://127.0.0.1:8787
The configured port can be changed. The Python SDK can discover the installed PBX configuration automatically.

Download the Python SDK

PBX Local API Python SDK

Dependency-free synchronous client. Includes installed-PBX discovery, structured API errors, lifecycle polling, profile CRUD, proxy, fingerprint, diagnostics, and batch helpers.

SHA256: 0F8353094464D16DB67F59B565D5F1EFE10D98DD8FE3143A54DF693FF5C6F08C

Quick SDK usage

from pbx_local_api import PBXLocalAPI, PBXAPIError

# Best option when your script runs on the same Windows installation:
pbx = PBXLocalAPI.from_installed_pbx()

print(pbx.health())
print(pbx.capabilities())
print(pbx.list_environments())

draft = pbx.generate_draft({"os": "win64"})
print(draft)

# API start is automation-only:
pbx.start_selenium("env_001")
pbx.wait_until_running("env_001")

print(pbx.status("env_001"))
print(pbx.automation("env_001"))

pbx.stop("env_001")
pbx.wait_until_stopped("env_001")

Important API rules

Authentication

Protected routes accept either authentication header:

Authorization: Bearer <PBX_API_KEY>

or:

X-PBX-API-Key: <PBX_API_KEY>

Installed-PBX discovery for Python

PBXLocalAPI.from_installed_pbx() reads the Local API configuration from the current Windows PBX installation. The SDK uses the installed API key and the configured Local API port, including the PBX_LOCAL_API_PORT override when present.

Use the API from different languages

Every PBX route is normal HTTP + JSON, so any language with an HTTP client can use it. These reusable helpers let you call all methods in the endpoint table below.

from pbx_local_api import PBXLocalAPI, PBXAPIError

# Best option when your script runs on the same Windows installation:
pbx = PBXLocalAPI.from_installed_pbx()

print(pbx.health())
print(pbx.capabilities())
print(pbx.list_environments())

draft = pbx.generate_draft({"os": "win64"})
print(draft)

# API start is automation-only:
pbx.start_selenium("env_001")
pbx.wait_until_running("env_001")

print(pbx.status("env_001"))
print(pbx.automation("env_001"))

pbx.stop("env_001")
pbx.wait_until_stopped("env_001")
$Base = "http://127.0.0.1:8787"
$Key  = "YOUR_PBX_API_KEY"
$Headers = @{ Authorization = "Bearer $Key" }

function Invoke-PBX {
    param(
        [string]$Method,
        [string]$Path,
        $Body = $null,
        [switch]$Public
    )

    $args = @{
        Method = $Method
        Uri    = "$Base$Path"
    }

    if (-not $Public) { $args.Headers = $Headers }

    if ($null -ne $Body) {
        $args.ContentType = "application/json"
        $args.Body = ($Body | ConvertTo-Json -Depth 20)
    }

    Invoke-RestMethod @args
}

Invoke-PBX GET "/v1/health" -Public
Invoke-PBX GET "/v1/environments"

$draft = Invoke-PBX POST "/v1/environments/draft" @{ os = "win64" }

Invoke-PBX POST "/v1/environments/env_001/start" @{
    automation = "selenium"
}

Invoke-PBX GET "/v1/environments/env_001/status"
Invoke-PBX POST "/v1/environments/env_001/stop" @{}
# Public health
curl http://127.0.0.1:8787/v1/health

# Authenticated request
curl -H "Authorization: Bearer YOUR_PBX_API_KEY" \
  http://127.0.0.1:8787/v1/environments

# Draft
curl -X POST \
  -H "Authorization: Bearer YOUR_PBX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"os":"win64"}' \
  http://127.0.0.1:8787/v1/environments/draft

# Start with Selenium
curl -X POST \
  -H "Authorization: Bearer YOUR_PBX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"automation":"selenium"}' \
  http://127.0.0.1:8787/v1/environments/env_001/start
const BASE = "http://127.0.0.1:8787";
const API_KEY = "YOUR_PBX_API_KEY";

async function pbx(method, path, body = undefined, auth = true) {
  const headers = { "Accept": "application/json" };
  if (auth) headers["Authorization"] = `Bearer ${API_KEY}`;
  if (body !== undefined) headers["Content-Type"] = "application/json";

  const res = await fetch(BASE + path, {
    method,
    headers,
    body: body === undefined ? undefined : JSON.stringify(body)
  });

  const data = await res.json();
  if (!res.ok) throw new Error(JSON.stringify(data));
  return data;
}

console.log(await pbx("GET", "/v1/health", undefined, false));
console.log(await pbx("GET", "/v1/environments"));

const draft = await pbx("POST", "/v1/environments/draft", { os: "win64" });

await pbx("POST", "/v1/environments/env_001/start", {
  automation: "cdp"
});

console.log(await pbx("GET", "/v1/environments/env_001/status"));
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var baseUrl = "http://127.0.0.1:8787";
var apiKey = "YOUR_PBX_API_KEY";
using var http = new HttpClient();

async Task PBX(
    HttpMethod method,
    string path,
    object? body = null,
    bool auth = true)
{
    using var req = new HttpRequestMessage(method, baseUrl + path);

    if (auth)
        req.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", apiKey);

    if (body is not null)
        req.Content = new StringContent(
            JsonSerializer.Serialize(body),
            Encoding.UTF8,
            "application/json");

    using var res = await http.SendAsync(req);
    var text = await res.Content.ReadAsStringAsync();
    res.EnsureSuccessStatusCode();
    return text;
}

Console.WriteLine(await PBX(HttpMethod.Get, "/v1/health", auth:false));
Console.WriteLine(await PBX(HttpMethod.Get, "/v1/environments"));

Console.WriteLine(await PBX(
    HttpMethod.Post,
    "/v1/environments/draft",
    new { os = "win64" }));

await PBX(
    HttpMethod.Post,
    "/v1/environments/env_001/start",
    new { automation = "selenium" });
= 300)
        throw new Exception("PBX API HTTP " . $status . ": " . $result);

    return json_decode($result, true);
}

print_r(pbx("GET", "/v1/health", null, false));
print_r(pbx("GET", "/v1/environments"));
print_r(pbx("POST", "/v1/environments/draft", ["os" => "win64"]));

pbx("POST", "/v1/environments/env_001/start", [
    "automation" => "cdp"
]);

Complete endpoint reference

The API retains the route name /v1/environments for compatibility. In the product documentation these objects are called PBX virtual profiles.

MethodRouteAuthPurpose
GET/v1/healthPublicHealth check
GET/v1/capabilitiesAuthSupported capabilities
GET/v1/diagnosticsAuthCurrent diagnostics
POST/v1/diagnostics/exportAuthExport diagnostics
GET/v1/environmentsAuthList saved virtual profiles
GET/v1/environments/{id}AuthGet one virtual profile
POST/v1/environments/draftAuthGenerate unsaved draft
POST/v1/environmentsAuthCreate/save a profile
POST/v1/environments/batchAuthCreate multiple profiles (1–100)
PATCH/v1/environments/{id}AuthPartially update a profile
DELETE/v1/environments/{id}AuthDelete a profile
POST/v1/environments/{id}/cloneAuthClone a profile
POST/v1/environments/{id}/validateAuthValidate a profile
GET/v1/environments/{id}/statusAuthRead lifecycle/automation status
POST/v1/environments/{id}/startAuthStart profile using CDP or Selenium
POST/v1/environments/{id}/stopAuthStop profile
POST/v1/proxy/checkAuthCheck a proxy configuration
PATCH/v1/environments/{id}/proxyAuthUpdate profile proxy
POST/v1/environments/{id}/proxy/checkAuthCheck assigned profile proxy
PATCH/v1/environments/{id}/fingerprintAuthUpdate supported fingerprint settings
POST/v1/environments/{id}/fingerprint/randomizeAuthRandomize supported fingerprint fields
PATCH/v1/environments/batchAuthBatch update profiles
POST/v1/environments/batch/deleteAuthBatch delete profiles
POST/v1/environments/batch/fingerprint/randomizeAuthBatch fingerprint randomization
POST/v1/environments/batch/startAuthBatch start with CDP/Selenium automation
POST/v1/environments/batch/stopAuthBatch stop

Complete Python SDK method reference

The downloadable module exposes the following public helpers:

GroupPython methodUnderlying operation
Service / diagnosticshealth()GET /v1/health
Service / diagnosticscapabilities()GET /v1/capabilities
Service / diagnosticsdiagnostics()GET /v1/diagnostics
Service / diagnosticsexport_diagnostics()POST /v1/diagnostics/export
Profileslist_environments()GET /v1/environments
Profilesget_environment(id)GET /v1/environments/{id}
Profilesgenerate_draft(options)POST /v1/environments/draft
Profilescreate_environment(options)POST /v1/environments
Profilescreate_batch(count, template, name_prefix=None)POST /v1/environments/batch
Profilesupdate_environment(id, patch)PATCH /v1/environments/{id}
Profilesdelete_environment(id)DELETE /v1/environments/{id}
Profilesclone_environment(id, overrides=None)POST /v1/environments/{id}/clone
Profilesvalidate_environment(id)POST /v1/environments/{id}/validate
Lifecyclestatus(id)GET /v1/environments/{id}/status
Lifecyclestart(id, automation='cdp'|'selenium')POST /v1/environments/{id}/start
Lifecycle helperstart_cdp(id)Client helper
Lifecycle helperstart_selenium(id)Client helper
Lifecycle helperwait_for_status(id, desired, ...)Polling helper
Lifecycle helperwait_until_running(id, ...)Polling helper
Lifecycle helperwait_until_stopped(id, ...)Polling helper
Lifecycle helperautomation(id)Reads status and returns automation object
Lifecyclestop(id)POST /v1/environments/{id}/stop
Proxycheck_proxy(proxy)POST /v1/proxy/check
Proxyupdate_proxy(id, proxy)PATCH /v1/environments/{id}/proxy
Proxyset_direct_network(id)PATCH /v1/environments/{id}/proxy
Proxycheck_environment_proxy(id)POST /v1/environments/{id}/proxy/check
Fingerprintupdate_fingerprint(id, patch)PATCH /v1/environments/{id}/fingerprint
Fingerprintrandomize_fingerprint(id, fields=None)POST /v1/environments/{id}/fingerprint/randomize
Batchbatch_update(ids, patch)PATCH /v1/environments/batch
Batchbatch_delete(ids)POST /v1/environments/batch/delete
Batchbatch_randomize_fingerprint(ids, fields=None)POST /v1/environments/batch/fingerprint/randomize
Batchbatch_start(ids, automation=...)POST /v1/environments/batch/start
Batchbatch_stop(ids)POST /v1/environments/batch/stop

Common request bodies

Generate a draft

POST /v1/environments/draft
{ "os": "win64" }

Create multiple profiles

POST /v1/environments/batch
{
  "count": 5,
  "template": { "os": "win64" },
  "name_prefix": "QA"
}

The Python SDK validates count between 1 and 100.

Start a profile

POST /v1/environments/env_001/start
{
  "automation": "selenium"
}

Use "cdp" or "selenium". API starts are automation launches; there is no Local API manual-start mode.

Update proxy

PATCH /v1/environments/env_001/proxy
{
  "proxy": {
    "enabled": true,
    "host": "proxy.example",
    "port": 1080
  }
}

Set direct network

PATCH /v1/environments/env_001/proxy
{
  "proxy": {
    "enabled": false
  }
}

Randomize selected supported fingerprint fields

POST /v1/environments/env_001/fingerprint/randomize
{
  "fields": ["language", "timezone"]
}

Batch update

PATCH /v1/environments/batch
{
  "environment_ids": ["env_001", "env_002"],
  "patch": {
    "name": "Updated"
  }
}

Batch start

POST /v1/environments/batch/start
{
  "environment_ids": ["env_001", "env_002"],
  "automation": "cdp"
}
Schema noteProfile/create/update/fingerprint/proxy objects are versioned PBX data structures. Use GET /v1/capabilities, generated drafts, and existing profile documents as the authoritative shape for the PBX version installed on the machine.

Errors and lifecycle handling

The Python SDK raises PBXAPIError with status, code, message, and the parsed API response when available. Connection failures and timeouts use status 0.

from pbx_local_api import PBXLocalAPI, PBXAPIError

pbx = PBXLocalAPI.from_installed_pbx()

try:
    pbx.start_selenium("env_001")
    running = pbx.wait_until_running("env_001", timeout=60)
    print(running)
except PBXAPIError as exc:
    print("status :", exc.status)
    print("code   :", exc.code)
    print("message:", exc.message)

Async start

Do not assume the browser is already ready when the start request returns. Poll /status or use the Python SDK's wait_until_running(). For Selenium, read the published automation information from profile status instead of attempting to use PBX's internal Chromium attachment endpoint.

Security


Download the PBX Local API Python SDK →