Authentication

The ProxyHat API authenticates every request with a Bearer token. Obtain a token by registering or logging in through the API, or by creating an API key in your dashboard. Send that token in the Authorization header on every authenticated request.

Bearer Tokens

Authenticated endpoints expect the Authorization header with your token:

Authorization: Bearer __API_KEY__

The base URL for all endpoints is https://api.proxyhat.com/v1. Tokens are Laravel Sanctum personal access tokens — they do not expire on their own and stay valid until you revoke or regenerate them.

Which token do I use? The accessToken returned by /v1/auth/register and /v1/auth/login, the plain_text_token returned by /v1/profile/api-keys, and a key created in the dashboard are all interchangeable Bearer tokens. Use whichever is most convenient for your integration.

Getting a Token

There are three ways to obtain a Bearer token:

  1. Dashboard — open the API page and create a key. It is shown once; copy and store it securely.
  2. Register or log in via the API — the endpoints below return an accessToken you can use immediately.
  3. Create an API key programmatically — call POST /v1/profile/api-keys with an existing token.

Register

POST /v1/auth/register

Register

Register

Create a new ProxyHat account and receive a Bearer access token in one call. No authentication is required. The password must be at least 8 characters and contain upper- and lower-case letters and at least one number.

Example request:
Request Body
Name Type Required Description
email string Required Account email address. Must be a valid, unique email (max 255 characters).
password string Required Minimum 8 characters, must include mixed case and at least one number (e.g. "SecurePass123").
password_confirmation string Required Must match the password field.
name string Optional Display name. If omitted, a name is derived from the email local part.
referral_code string Optional Optional referral code to credit a referrer (max 64 characters).
curl -X POST https://api.proxyhat.com/v1/auth/register \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "you@example.com",
    "password": "SecurePass123",
    "password_confirmation": "SecurePass123"
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/auth/register",
    headers={"Accept": "application/json"},
    json={
        "email": "you@example.com",
        "password": "SecurePass123",
        "password_confirmation": "SecurePass123",
    },
)

data = response.json()
access_token = data["accessToken"]
print(access_token)
const response = await fetch("https://api.proxyhat.com/v1/auth/register", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    email: "you@example.com",
    password: "SecurePass123",
    password_confirmation: "SecurePass123",
  }),
});

const data = await response.json();
console.log(data.accessToken);
payload := map[string]string{
    "email":                 "you@example.com",
    "password":              "SecurePass123",
    "password_confirmation": "SecurePass123",
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/auth/register", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var data struct {
    AccessToken string `json:"accessToken"`
    TokenType   string `json:"tokenType"`
}
json.NewDecoder(resp.Body).Decode(&data)
fmt.Println(data.AccessToken)
If validation fails (duplicate email, weak password, mismatched confirmation) the API returns 422 Unprocessable Content with a Laravel validation body: { "message": "...", "errors": { "email": ["..."], "password": ["..."] } }.

Log In

POST /v1/auth/login

Log In

Log In

Exchange email and password for a Bearer access token. If two-factor authentication is enabled on the account, include the current TOTP (or a recovery) code in twofa_code.

Example request:
Request Body
Name Type Required Description
email string Required Account email address.
password string Required Account password.
twofa_code string Optional Six-digit TOTP code or a one-time recovery code. Required only when 2FA is enabled (max 16 characters).
curl -X POST https://api.proxyhat.com/v1/auth/login \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "you@example.com",
    "password": "SecurePass123"
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/auth/login",
    headers={"Accept": "application/json"},
    json={
        "email": "you@example.com",
        "password": "SecurePass123",
        # "twofa_code": "123456",  # only if 2FA is enabled
    },
)

data = response.json()
access_token = data["accessToken"]
print(access_token)
const response = await fetch("https://api.proxyhat.com/v1/auth/login", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    email: "you@example.com",
    password: "SecurePass123",
    // twofa_code: "123456", // only if 2FA is enabled
  }),
});

const data = await response.json();
console.log(data.accessToken);
payload := map[string]string{
    "email":    "you@example.com",
    "password": "SecurePass123",
    // "twofa_code": "123456", // only if 2FA is enabled
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/auth/login", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var data struct {
    AccessToken string `json:"accessToken"`
    TokenType   string `json:"tokenType"`
}
json.NewDecoder(resp.Body).Decode(&data)
fmt.Println(data.AccessToken)

Login Error Responses

Invalid email or password returns 401 Unauthorized with a flat body:

{
  "message": "Unauthorized"
}

When 2FA is enabled but no twofa_code was supplied, the API returns 403 Forbidden using the standard response envelope. Note there is no message key — the human-readable text is in description, and requires_2fa signals that you should prompt for a code and retry:

{
  "success": false,
  "payload": null,
  "errors": [],
  "description": "2FA verification required",
  "requires_2fa": true
}

If the supplied twofa_code is wrong, the API returns 422 Unprocessable Content with the invalid code reported under errors.twofa_code:

{
  "success": false,
  "payload": null,
  "errors": {
    "twofa_code": ["Invalid 2FA code"]
  },
  "description": "Invalid 2FA code"
}

Create an API Key

POST /v1/profile/api-keys Requires Auth

Create API Key

Create API Key

Issue a new long-lived API key for the authenticated account. The plaintext token is returned only in this response and cannot be retrieved again — store it immediately.

Example request:
Request Body
Name Type Required Description
name string Optional Label for the key. Defaults to "API key <timestamp>" if omitted.
abilities array Optional Token abilities/scopes. Defaults to ["*"] (full access).
curl -X POST https://api.proxyhat.com/v1/profile/api-keys \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "name": "Production server" }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/profile/api-keys",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={"name": "Production server"},
)

data = response.json()
print(data["plain_text_token"])  # shown only once
const response = await fetch("https://api.proxyhat.com/v1/profile/api-keys", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({ name: "Production server" }),
});

const data = await response.json();
console.log(data.plain_text_token); // shown only once
body, _ := json.Marshal(map[string]string{"name": "Production server"})

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/profile/api-keys", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var data struct {
    ID            int    `json:"id"`
    Name          string `json:"name"`
    PlainTextToken string `json:"plain_text_token"`
}
json.NewDecoder(resp.Body).Decode(&data)
fmt.Println(data.PlainTextToken) // shown only once
Stored once, no id| prefix. Unlike the raw Sanctum token format, plain_text_token is returned with the numeric id and pipe stripped off — use the value exactly as returned. It is shown only in this response (and when you regenerate a key); ProxyHat cannot show it to you again. If you lose it, delete the key and create a new one.

Using the Token

Once you have a token, send it as a Bearer credential. A quick way to confirm it works is the /v1/auth/user "whoami" endpoint, which returns your account and traffic balance:

curl https://api.proxyhat.com/v1/auth/user \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

headers = {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
}

response = requests.get("https://api.proxyhat.com/v1/auth/user", headers=headers)
print(response.json())
const response = await fetch("https://api.proxyhat.com/v1/auth/user", {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const data = await response.json();
console.log(data);
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/auth/user", nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

Authentication Errors

If the token is missing, malformed, or has been revoked, authenticated endpoints return 401 Unauthorized with a flat body:

{
  "message": "Unauthenticated."
}

Token Security

Important: Treat your token like a password. Never expose it in client-side code, public repositories, or shared logs. If a token is compromised, delete or regenerate it immediately from the dashboard or via the API.