Skip to main content

Authentication

Every API request authenticates with a Personal Access Token (PAT) sent as an HTTP bearer token:

curl https://api.vps-server.host/v1/account \
-H "Authorization: Bearer pat_live_aBc12defGHIjklmnopqrstuvwxyz0123456789ABCD"

Creating a token​

PATs are minted from your VPS-Server.Host client area:

  1. Log in at https://vps-server.host/clientarea.php
  2. In the left sidebar under Developers, open API Tokens (or jump straight to /clientarea_api_tokens.php)
  3. In the Create a new token form:
    • Token name — a label so you remember what this token is for (ci-deploy-runner, prod-monitor, etc.)
    • Scopes — tick at least one. Pick the smallest set that lets the token do its job (scope reference below).
    • IP allow-list (optional but strongly recommended) — one or more CIDRs. Requests from anywhere else return 403 permission_denied.
    • Expires at (optional) — set a date if the token is short-lived (rotation, contractor access, etc.).
    • Confirm your password — your client-area password. This is our step-up check — without it the token won't mint.
  4. Click Create token.
  5. The freshly minted secret appears in a yellow banner at the top of the page.

You see the secret exactly once

Copy the pat_live_… value immediately into your secrets manager. We store only sha256(pepper || token) — there is no way for us, or for you, to recover the value later. If you lose it, revoke the token and mint a new one.

The secret format is pat_live_ followed by a high-entropy base62 string. GitHub-style secret scanning watches for the pat_live_ prefix and we auto-revoke any token that turns up in a public repository.

Managing existing tokens​

The same page lists all tokens on your account. Each row shows:

  • Name, prefix (the first 14 characters of the secret — safe to display anywhere), scopes, status (Active / Revoked / Expired)
  • Created, last used, last used IP

The Manage ▾ dropdown on the right of each row exposes:

ActionWhat it doesStep-up required
RotateKeeps the same token id but issues a new secret. The old secret stops working immediately.yes
RevokeSoft-disables the token. Requests return 401 authentication_failed. The DB row stays for audit.yes
DeleteHard-removes the row entirely. Use only after a revoke + grace period.yes

All three actions require re-entering your client-area password (step-up).

Rotation is cheap — do it often

A 60-second grace window means the old secret keeps working for a minute after rotation, so a deploy pipeline can pick up the new value without downtime. Rotate any PAT older than 90 days.

Sandbox tokens​

Sandbox tokens are prefixed pat_test_ and only authenticate against https://sandbox.api.vps-server.host. Same minting flow, separate environment — see Sandbox & test mode.

Sub-account (contact) tokens​

If you're logged in as a contact (a sub-user that the primary account-holder added under Contacts → Sub-accounts in their client area), you can mint your own PATs — but only with scopes that match the permissions the primary granted you.

Mapping from a contact's portal permissions to PAT scope groups:

Contact permissionScope groups granted
productsservers:*, snapshots:*, services:*
manageproductssame as above (server side-effects)
domainsdomains:*, dns:*
managedomainssame as above (writes)
invoicesbilling:*
paymentmethodspayment_methods:*
ticketstickets:*
affiliatesaffiliates:read
contactscontacts:*

The form on the token page only shows scopes you're actually allowed to mint — if a scope is missing, ask the primary account-holder to widen your permissions.

Scopes​

A PAT carries a scope set that controls what it can do. Scopes are hierarchical — servers:* matches every servers: action; *:read matches every read; admin:all is the implicit superuser.

Common scopes:

GroupReadWrite
Serversservers:readservers:power, servers:rebuild, servers:console, servers:credentials
Snapshotssnapshots:readsnapshots:create, snapshots:restore
DNSdns:readdns:write
Accountaccount:readaccount:write
Payment methodspayment_methods:readpayment_methods:write
Billingbilling:readbilling:pay_invoice, billing:add_funds, billing:accept_quote
Servicesservices:readservices:write, services:cancel
Domainsdomains:readdomains:write, domains:transfer
Ticketstickets:readtickets:write

The full enumerated list lives in the Scope schema.

Least privilege

Default any new token to read-only (*:read). Add write scopes only as needed. A token used by your CI to reboot servers doesn't need to see invoices.

IP allow-list​

Every PAT carries an ip_allowlist (CIDR list). Empty means any IP. When set, requests from outside the list return 403 permission_denied.

Configure it at token-creation time. Strongly recommended for any token used outside your laptop:

  • CI runners — your CI provider's egress IP block
  • Production app servers — their own public IPs
  • Office automation — the office WAN IP

Step-up authentication​

Destructive or billed actions (:cancel, :pay, :accept, :rebuild, :restore, :transfer, password changes, PAT rotation/revocation) require a short-lived X-Confirm-Token on top of your PAT. Get one via:

curl https://api.vps-server.host/v1/auth/step-up \
-H "Authorization: Bearer $PAT" \
-d '{"method":"password","password":"…"}'

The token is valid for 5 minutes. See Step-up authentication for the full flow.

Anything to enable in the client area?​

No. The PAT page works for every logged-in client out of the box — no admin toggle, no plan upgrade, no API access flag. The first time you visit the page, the storage table is created automatically.

Internal admin API credentials (used by our staff for administrative tasks) are separate from the public API documented here — you don't need them.

Quick verification​

After minting a token with account:read, paste this into your terminal:

PAT='pat_live_…' # the value you just copied
curl -sS https://api.vps-server.host/v1/account \
-H "Authorization: Bearer $PAT" | jq

You should see your account profile:

{
"id": "cli_42",
"email": "[email protected]",
"first_name": "…",
"last_name": "…",
"currency": "USD",
"credit_balance": { "amount": 1250, "currency": "USD" },
"two_factor_enabled": true,
"created_at": "2024-02-10T15:30:00Z"
}

If you get 401 authentication_failed, the token didn't make it through — re-check the Authorization header. If you get 403 permission_denied, the token lacks the account:read scope; revoke it and mint a new one with the right scope.