API reference

The public JSON API powers self-serve signup, account management, and payments. Your whisker key in the path is the management credential. No separate API key.

Base URL: your site's public origin (e.g. https://socks.cat).


Signup challenge

GET /signup/challenge

Response 200 { "nonce": "..." } — a one-time nonce to echo back to POST /signup (abuse gate).

Create an account

POST /signup

Body { "nonce": "<from /signup/challenge>" }

Response 200

{
  "whisker": "whisker-DLDI-ZF6C-...",
  "username": "cat-XS73LMPQOFK762Q4",
  "password": "a14c2f9f5068d0ddd2f50c39bad4bfdd",
  "life": 1
}

whisker is the management secret (shown once). username is the collar tag (proxy username base). password is the life-1 full pawprint. Never send the whisker to the proxy — only collar + pawprint.

Look up an account

GET /account/{whisker}

Response 200

{
  "id": 42,
  "username": "cat-XS73LMPQOFK762Q4",
  "password": null,
  "life": 1,
  "bytes_in": 52428800,
  "bytes_out": 104857600,
  "traffic_remaining_gib": 12.4,
  "traffic_purchased_gib": 50.0,
  "traffic_used_gib": 37.6,
  "default_selection": "country-us-isp-comcast",
  "allowed_ips": ["10.0.0.0/24", "203.0.113.7/32"]
}

password is always null — derive pawprints in settings or via the derive endpoint.

404 if no account matches the whisker.

Derive a pawprint

GET /account/{whisker}/derive
GET /account/{whisker}/derive?selection=country-us-state-ut-type-residential
GET /account/{whisker}/derive?type=residential&isp=comcast

Optional query parameters narrow a scoped pawprint:

ParamMeaning
selectionkeyed builder route (preferred), e.g. country-us-isp-comcast
typeresidential, mobile, or datacenter
ispISP catalog code (lowercase)
territoryinternal geo code — prefer selection or the web UI

Omit all params for a full pawprint (any route).

Response 200

{
  "username": "cat-XS73LMPQOFK762Q4",
  "life": 1,
  "scope": "<internal>",
  "pawprint": "0a6b52bfad0cab7a8fa5b56e213ff032"
}

scope is internal — you do not put it in proxy clients. Requires a whisker credential (400 if you pass a collar tag).

Rotate pawprints

POST /account/{whisker}/rotate

Bumps life; old pawprints stop working after the grace window. Returns a new full pawprint for the new life.

Response 200 { "username": "...", "life": 2, "password": "..." }

(password here is the new full pawprint.)

Reissue whisker

POST /account/{whisker}/reissue

Mints a new whisker key and invalidates the old one immediately.

Response 200

{
  "whisker": "whisker-NEW-...",
  "username": "cat-XS73LMPQOFK762Q4",
  "password": "...",
  "life": 1
}

Set the default route

POST /account/{whisker}/default

Body { "selection": "country-us-isp-comcast" }routing tag string; empty clears it. Response 200 { "default_selection": "..." }, 404 if not found.

Add an allowed IP

POST /account/{whisker}/ips

Body { "cidr": "10.0.0.0/24" } — a bare IP (stored as a host route) or CIDR. Response 200 { "added": "..." }, 400 on an unparseable address, 404 if not found.

Remove an allowed IP

POST /account/{whisker}/ips/remove

Body { "cidr": "10.0.0.0/24" }. Response 200 { "removed": "..." }.

Request a payment quote

POST /quote

Body { "token": "<whisker>", "coin": "monero", "gib": 5.0 }

Response 200

{
  "coin": "monero",
  "address": "8B...",
  "amount": 0.004,
  "amount_atomic": 4000000000,
  "expires_at": 1712345678
}

Quote status

GET /quote-status?address=...&coin=monero

Response 200

{
  "credited": false,
  "confirmations": 3,
  "required": 10,
  "seen": true
}

Locations & pricing

GET /locations        → array of exits with country/city/isp/type tags
GET /catalog/locations → structured catalog for the web builder
GET /pricing          → { "price_usd_per_gb": 1.5, "min_topup_usd": 5.0, "coins": [...] }

Status codes

CodeMeaning
200OK
201Created
204No content (delete)
400Validation error
404Not found
409Conflict
429Rate limited (signup or credential operations)
500Server error