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
- Sign in to the dashboard.
- Run the agent for that same account (browsers will not open without it).
- Open the sidebar API tab → + Create key.
- 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
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.
| Status | Meaning |
|---|---|
400 | Bad input, agent offline, already open, unknown club |
401 | Missing or invalid key |
402 | Out of PAYG credits |
403 | Role / tenant / profile or plan cap |
404 | Unknown id |
409 | Profile has no proxy / fingerprint (identity export) |
503 | Proxy 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/keys | Own keys. Admin: ?all=1. ?includeRevoked=1 |
| POST | /api/keys | { "name": "ci" } → key once |
| DELETE | /api/keys/:id | Revoke |
Profiles
Ids start at 0. browser is chrome or camoufox.
| Path | ||
|---|---|---|
| GET | /api/profiles | List. Admin ?tenantId=. Includes proxyFailed / proxyFailReason |
| GET | /api/profiles/:id | One profile + live (state, ip, Block Bypass) + proxyFailed / proxyFailReason |
| GET | /api/profiles/:id/identity | Fingerprint + 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/:id | name, note, browser, shared, blockBypassClub |
| DELETE | /api/profiles/:id | Closes browser, purges agent dir |
| POST | /api/profiles/:id/rotate-proxy | Next unused (not probed) |
| POST | /api/profiles/:id/test-proxy | Probe current. Failure unlocks reveal-proxy |
| POST | /api/profiles/:id/reveal-proxy | Credentials after a failed test/check. Logged (password not stored) |
| POST | /api/profiles/:id/replace-proxy | Verified-live unused proxy |
| POST | /api/profiles/:id/diary | { text } |
| DELETE | /api/profiles/:id/diary/:entryId | |
| GET | /api/profiles/:id/logs | IP, 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-proxies | id/host/port/assigned/profileName, no credentials |
| GET | /api/tenants/:id/proxies | tenant-admin, includes credentials |
| POST | /api/tenants/:id/proxies/upload | append host:port:user:pass lines |
| POST | /api/tenants/:id/proxies/replace | replace file |
| DELETE | /api/tenants/:tid/proxies/:id | shifts later ids down |
| POST | /api/tenants/:tid/proxies/:id/test | Any tenant member |
| POST | /api/tenants/:tid/proxies/:id/reveal | Credentials after a failed test/check. Logged (password not stored) |
| GET | /api/tenants/:id/proxies/check | Last pool-wellness report + progress. Any member of that tenant |
| POST | /api/tenants/:id/proxies/check | Start background pool probe. Any tenant member. 202; 409 if already running |
| POST | /api/tenants/:id/proxies/replace-unhealthy | Move 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).