Authentication

Updated Oct 3, 2026 Markdown

Every request must carry HMAC-SHA256 authentication headers. The signed payload is canonicalised so a captured signature cannot be replayed against a different endpoint, body or window.

X-WHost-KeyAPI key id (configurable in /admin/api-keys).
X-WHost-TimestampCurrent Unix timestamp (seconds).
X-WHost-Nonce16–256 characters, unique per request (32 hex chars recommended); a replayed nonce is refused.
X-WHost-SignatureHMAC-SHA256(api_secret, "METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY") hex digest.

The agent rejects requests:

  • with a timestamp older than 300 seconds,
  • whose nonce was already seen inside the replay window (twice the 300 s timestamp tolerance plus 60 s),
  • whose signature, recomputed server-side, doesn't match,
  • whose path drifts from the signed one after posixpath.normpath and matrix-param stripping (so trailing ;foo=bar cannot bypass the path binding).

Account-bound (reseller) keys

A key minted from the client panel by a reseller account (Settings → API Access) signs requests exactly like a server key, but is served as that reseller: it reaches only /api/v1/client/* and acts there with the reseller's ownership checks and ACL plan. What such a key gets back:

Request Answer
Any path outside /api/v1/client/ 403 SCOPE_DENIED
/api/v1/client/api-keys*, /api/v1/client/profile* (credential and profile management) 403 HMAC_FORBIDDEN_FOR_CREDENTIAL_MUTATION — browser session only
A sub-account the reseller does not own 404 ACCOUNT_NOT_FOUND
Reseller's ACL plan without api_access, or the account no longer a reseller 403 PERMISSION_DENIED
Owner account suspended, terminated or deleted; key revoked 401 AUTH_FAILED

The key's scope is fixed (client) and cannot be widened; a reseller holds at most 10 keys. Management endpoints for the reseller's own keys: GET/POST /api/v1/client/api-keys, PUT /api/v1/client/api-keys/{key_id}, POST /api/v1/client/api-keys/{key_id}/revoke, DELETE /api/v1/client/api-keys/{key_id}, GET /api/v1/client/api-keys/logs — session cookie only. The administrator sees every key under GET /api/v1/system/api-keys; the owner field names the reseller (null for server-wide keys).

Canonical signing payload (v2)

text
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY

PATH is the agent-visible path (the part after the host, including the /api/v1/... prefix). BODY is the raw HTTP body bytes decoded as UTF-8 with surrogateescape (binary uploads are signable without re-encoding).

Reference Python signer

Python
import hashlib, hmac, json, secrets, time
from urllib.request import Request, urlopen
import ssl

API_KEY    = "your-api-key"
API_SECRET = "your-api-secret"
BASE_URL   = "https://panel.example.com"

def sign(method: str, path: str, body: bytes = b"") -> dict[str, str]:
    ts    = str(int(time.time()))
    nonce = secrets.token_hex(16)
    body_str = body.decode("utf-8", errors="surrogateescape")
    payload  = f"{method}\n{path}\n{ts}\n{nonce}\n{body_str}"
    sig = hmac.new(
        API_SECRET.encode("utf-8"),
        payload.encode("utf-8", errors="surrogateescape"),
        hashlib.sha256,
    ).hexdigest()
    return {
        "X-WHost-Key":       API_KEY,
        "X-WHost-Timestamp": ts,
        "X-WHost-Nonce":     nonce,
        "X-WHost-Signature": sig,
        "Content-Type":      "application/json",
    }

body = json.dumps({"username": "alice"}).encode("utf-8")
headers = sign("POST", "/api/v1/accounts", body)
req = Request(f"{BASE_URL}/api/v1/accounts", data=body,
              headers=headers, method="POST")
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE  # remove in production
print(urlopen(req, context=ctx).read())

Signers in other languages live next to this file:

The /admin/* panel and the /client/* panel mint an httpOnly session cookie via POST /api/v1/auth/login (then /api/v1/auth/login/2fa when 2FA is enabled). SDKs and partner integrations should always use HMAC instead — sessions are scoped to a browser context and rotate on every privilege change.

Still Need Help?

Our support team is here around the clock for anything you can't find above.