HTTP API

Every dashboard action is available over HTTPS. Create a key, then call /api/... or the alias /v1/.... Browsers still launch on the machine whose agent is connected for the account that owns the key. Full writeup with request/response examples: docs/API.md in the repo.

Get a key

  1. Sign in to the dashboard.
  2. Run the agent for that same account (browsers will not open without it).
  3. Open the sidebar API tab → + Create key.
  4. Copy the secret (pbk_live_…). It is shown once. Max 20 active keys per account.

The key has the same role and tenant as your login. Revoke it on that tab; deleting the account revokes it too.

Auth

Send the key on every request:

Authorization: Bearer pbk_live_…

Same thing: header X-API-Key. The secret is shown once at creation and stored as a hash. A key inherits that account’s role and tenant. Revoking it is immediate. Max 20 active keys per account.

Quick start

export API=https://proxy.sporttickets.uk
export KEY=pbk_live_YOUR_KEY

curl -s -H "Authorization: Bearer $KEY" $API/api/me

curl -s -H "Authorization: Bearer $KEY" $API/api/profiles

curl -s -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"club":"arsenal"}' $API/api/profiles/378/open

curl -s -H "Authorization: Bearer $KEY" $API/api/profiles/378

curl -s -X POST -H "Authorization: Bearer $KEY" $API/api/profiles/378/close
The account’s agent must be online (GET /api/agents). Open always runs on your agent, not the profile owner’s machine. Poll GET /api/profiles/:id for live.state / exit IP — scripts do not need the dashboard WebSocket. An empty Block Bypass pool still starts the browser; then blockBypass.activated is false.

Python / JavaScript

import os, time, requests
API = os.environ["API"].rstrip("/")
H = {"Authorization": f"Bearer {os.environ['KEY']}"}

requests.get(f"{API}/api/me", headers=H).json()
requests.post(f"{API}/api/profiles/378/open", headers=H, json={"club": "arsenal"}).json()
for _ in range(15):
    live = requests.get(f"{API}/api/profiles/378", headers=H).json().get("live") or {}
    if live.get("state") == "running":
        break
    time.sleep(1.5)
requests.post(f"{API}/api/profiles/378/close", headers=H)
const API = process.env.API;
const H = { Authorization: `Bearer ${process.env.KEY}`, 'Content-Type': 'application/json' };
await fetch(`${API}/api/profiles/378/open`, { method: 'POST', headers: H, body: JSON.stringify({ club: 'arsenal' }) });
const live = await fetch(`${API}/api/profiles/378`, { headers: H }).then(r => r.json());
await fetch(`${API}/api/profiles/378/close`, { method: 'POST', headers: H });

Errors & roles

Failures are { "error": "…" }. Plan limits also send limit, current, max, plan.

StatusMeaning
400Bad input, agent offline, already open, unknown club
401Missing or invalid key
402Out of PAYG credits
403Role / tenant / profile or plan cap
404Unknown id
409Profile has no proxy / fingerprint (identity export)
503Proxy replace found nothing live

A tenant-user key sees own + shared profiles. Tenant-admin sees the tenant. Admin sees everything. Opening a browser requires that user’s agent.

API keys

Path
GET/api/keysOwn keys. Admin: ?all=1. ?includeRevoked=1
POST/api/keys{ "name": "ci" } → key once
DELETE/api/keys/:idRevoke

Profiles

Ids start at 0. browser is chrome or camoufox.

Path
GET/api/profilesList. Admin ?tenantId=. Includes proxyFailed / proxyFailReason
GET/api/profiles/:idOne profile + live (state, ip, Block Bypass) + proxyFailed / proxyFailReason
GET/api/profiles/:id/identityFingerprint + full proxy credentials for job replay. Does not open a browser. 409 if no proxy
POST/api/profiles{ name, browser, proxyId?, blockBypassClub? } — response includes identity
PATCH/api/profiles/:idname, note, browser, shared, blockBypassClub
DELETE/api/profiles/:idCloses browser, purges agent dir
POST/api/profiles/:id/rotate-proxyNext unused (not probed)
POST/api/profiles/:id/test-proxyProbe current. Failure unlocks reveal-proxy
POST/api/profiles/:id/reveal-proxyCredentials after a failed test/check. Logged (password not stored)
POST/api/profiles/:id/replace-proxyVerified-live unused proxy
POST/api/profiles/:id/diary{ text }
DELETE/api/profiles/:id/diary/:entryId
GET/api/profiles/:id/logsIP, bandwidth; URLs for admins

Browsers

POST /api/profiles/:id/open
{ "club": "arsenal" }   // or omit / null for Block Bypass off

POST /api/profiles/:id/block-bypass
{ "club": "arsenal", "reload": true }   // running browser; no Chrome restart

POST /api/profiles/:id/close
POST /api/quick-open
{ "browser": "camoufox", "club": "chelsea" }

?screen=0 on open skips the pre-launch proxy screen. Open returns blockBypass.activated so you can see whether the club cookie was applied, and blockBypass.queueToken when a fresh Queue-it URL was used for the first navigation. An empty pool or a down upstream still starts the browser.

Block Bypass

GET /api/block-bypass/clubs → arsenal, chelsea, tottenhamhotspur, cp. Pass that id as club on open, quick-open, or as blockBypassClub on create/patch.

Sessions

Path
GET/api/sessions?limit= ?blockedOnly=1 admin ?tenantId=
GET/api/sessions/:id
POST/api/sessions/:id/report{ reason, note } — blocked / captcha / slow / other

Agents

GET /api/agents — connected agents and their open browsers (state, ip, bandwidth, block, optional phase preparing/starting while launching). Tenant-users only see their own agent.

Proxies

Path
GET/api/my-proxiesid/host/port/assigned/profileName, no credentials
GET/api/tenants/:id/proxiestenant-admin, includes credentials
POST/api/tenants/:id/proxies/uploadappend host:port:user:pass lines
POST/api/tenants/:id/proxies/replacereplace file
DELETE/api/tenants/:tid/proxies/:idshifts later ids down
POST/api/tenants/:tid/proxies/:id/testAny tenant member
POST/api/tenants/:tid/proxies/:id/revealCredentials after a failed test/check. Logged (password not stored)
GET/api/tenants/:id/proxies/checkLast pool-wellness report + progress. Any member of that tenant
POST/api/tenants/:id/proxies/checkStart background pool probe. Any tenant member. 202; 409 if already running
POST/api/tenants/:id/proxies/replace-unhealthyMove expired/burned assignments onto live spares. { reasons, proxyIds, profileIds }

Tenants, accounts, billing

Tenant-admin or admin, as in the dashboard.

Path
GET/api/tenants · /api/tenants/:id
POST/api/tenants admin
PATCH/api/tenants/:id admin — name, plan, limits
DELETE/api/tenants/:id admin
GET/api/tenants/:id/credits
POST/api/tenants/:id/credits admin { amount, note }
GET/api/accounts · /api/accounts/:id
POST/api/accounts
PATCH/api/accounts/:id
POST/api/accounts/:id/regenerate-token agent token, not API key
DELETE/api/accounts/:id
GET/api/settings/billing · /api/billing/sessions · /api/reports · /api/proxy-reputation admin
GET/api/activity · /api/access-log · /api/metrics · /api/urls tenant-admin

Meta

No auth: GET /api/health, GET /api/agent-version, GET /api/openapi.json.

GET /api/me · GET /api/time. CORS is open on /api and /v1.

Agent binary download stays GET /download/agent/:platform?token=AGENT_TOKEN (darwin-arm64, darwin-amd64, windows-amd64).