DocsProvisioning API

Provisioning API

Create, suspend and bill hosting accounts from your own system with the /api/v1 provisioning API: tokens, endpoints, idempotency, rate limits and examples.

The provisioning API at /api/v1 lets a billing system or your own CMS manage hosting accounts on a ZoPanel server. The WHMCS, Blesta, HostBill and Paymenter modules all use it. Accounts are addressed by username and packages by name, so your system does not need to store any ZoPanel IDs. A machine-readable description is available as an OpenAPI 3 file (docs/provisioning-openapi.yaml in the source).

Authentication

  1. In ZoPanel, open My account → API tokens and click Create token.
  2. Set Permissions to Provisioning only (WHMCS, CMS).
  3. In Allowed from IPs, enter the addresses of the servers that call the API (up to 20 IPs or ranges).
  4. Copy the zpat_… token. It is shown only once.

Send the token in every request:

Authorization: Bearer zpat_xxxxxxxxxxxx

API tokens require a Pro license. Keep in mind:

  • A Provisioning only token can only call /api/v1. Anything else returns 403.
  • A call from an address that is not in the token's list returns 403.
  • If you restrict panel access under Settings → General → Restrict panel access, the calling server must be in that list too.
  • If the panel requires two-factor authentication for the token's owner, the owner must have 2FA set up, or the token is refused.
  • Changing the owner's password revokes all of the owner's tokens.

Who owns the token

Token owner What the token can do
Administrator Sells the administrator's packages and sees every hosting account
Reseller Sells only the reseller's own packages and sees only the reseller's customers, within the reseller's plan

Endpoints

Base URL: https://panel.example.com:8888/api/v1

Method and path Purpose
GET /api/v1 API version, token owner and panel URL (connection test)
GET /api/v1/packages Packages you can sell
PUT /api/v1/packages/{name} Create or update a package by name
POST /api/v1/accounts Create an account, optionally with its first website
GET /api/v1/accounts/{username} Status, package and usage
POST /api/v1/accounts/{username}/suspend Suspend (repeating it is harmless)
POST /api/v1/accounts/{username}/unsuspend Unsuspend (repeating it is harmless)
PUT /api/v1/accounts/{username}/package Change the package
PUT /api/v1/accounts/{username}/password Set the panel and SFTP password
POST /api/v1/accounts/{username}/login One-time sign-in link, valid once for 60 seconds
DELETE /api/v1/accounts/{username} Terminate the account
GET /api/v1/usage Usage of every account you manage

Usernames are 3–16 characters, lowercase letters and digits, starting with a letter. Names starting with zp are reserved. Passwords are 8–128 characters.

Notes on specific calls

  • Create: username, password and package (name or ID) are required. email, domain and php are optional. The response holds account and either site or site_error. If the account was created but the website was not (for example, the domain is already hosted elsewhere), the order still succeeds and the customer can add the website later.
  • Login: returns url, path and expires_in. It only works for active customer accounts. Resellers sign in with their own password and second factor.
  • Terminate: the account's websites, databases, mail and files are removed. The data is kept on the server for the grace period set in Settings → General → Keep deleted accounts (days).
  • Packages: fields you do not send are set to 0. For most limits, 0 means unlimited. Exceptions: for io_mbps and db_connections, 0 means automatic and -1 means unlimited. php_memory_mb defaults to 256 and php_children to 10.

Account and usage fields

GET /accounts/{username} and each item of GET /usage return:

Field Meaning
username, email, package Account details
status active or suspended
created_at Unix time
usage.disk_mb, usage.disk_limit_mb Disk used and the package limit (0 = unlimited), refreshed hourly
usage.bandwidth_mb Traffic served in the current calendar month, from the access logs, refreshed hourly
usage.sites, usage.sites_limit Websites
usage.databases, usage.databases_limit Databases

Errors

Errors return a JSON body {"error": "…"} with a matching status code:

Status Meaning
400 Invalid input, for example an unknown package
401 Missing, invalid or expired token
402 License limit reached, or no Pro license
403 The token is not allowed (scope, IP address or 2FA policy)
404 Unknown account, or one you do not manage
409 A request with the same idempotency key is still running
429 Too many requests

Idempotency keys

Send a unique Idempotency-Key header with POST, PUT and DELETE requests, for example billing-create-<order id>. Keys can be up to 200 characters.

  • If the same token repeats the same method and URL with the same key within 24 hours, it gets the first answer back, with the header Idempotent-Replayed: true. The action is not done again, so a retry after a timeout never creates an account twice.
  • While the first request is still running, a repeat gets 409.
  • Server errors (5xx) and 429 answers are not saved, so those requests can be retried with the same key.

Rate limits

Each token may make 1200 requests per minute. Above that, the API returns 429 with Retry-After: 60. Repeated failed authentication from one address is also throttled.

Examples

T="Authorization: Bearer zpat_xxx"
J="Content-Type: application/json"
P=https://panel.example.com:8888/api/v1

# Connection test
curl -H "$T" $P

# Push a plan
curl -X PUT -H "$T" -H "$J" \
  -d '{"max_sites":3,"disk_mb":10240,"memory_mb":1024,"cpu_percent":100,"sftp":true}' \
  $P/packages/Starter

# Sell it (the website gets SSL once the domain points to the server)
curl -X POST -H "$T" -H "$J" -H "Idempotency-Key: order-1001" \
  -d '{"username":"shop1","password":"S3cure-Pass-1","email":"owner@shop1.vn","package":"Starter","domain":"shop1.vn","php":"8.3"}' \
  $P/accounts

# Invoice overdue, then paid
curl -X POST -H "$T" $P/accounts/shop1/suspend
curl -X POST -H "$T" $P/accounts/shop1/unsuspend

# Upgrade
curl -X PUT -H "$T" -H "$J" -d '{"package":"Business"}' $P/accounts/shop1/package

# "Login to control panel" button: redirect the customer to .url
curl -X POST -H "$T" $P/accounts/shop1/login

# Usage for billing
curl -H "$T" $P/usage

Package fields

Field Meaning
max_sites, max_databases, max_cron, max_ftp, max_mailboxes Counts (0 = unlimited)
disk_mb, memory_mb Disk space and memory (0 = unlimited)
cpu_percent CPU, where 100 = one core (0 = unlimited)
processes Processes (0 = unlimited)
io_mbps Disk throughput (0 = automatic, -1 = unlimited)
db_connections Database connections per user (0 = automatic, -1 = unlimited)
php_memory_mb, php_children PHP memory limit and workers
sftp, terminal Access, true or false

← WHMCS and billing modules Fleet and multiple servers →