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
- In ZoPanel, open My account → API tokens and click Create token.
- Set Permissions to Provisioning only (WHMCS, CMS).
- In Allowed from IPs, enter the addresses of the servers that call the API (up to 20 IPs or ranges).
- 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 returns403. - 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,passwordandpackage(name or ID) are required.email,domainandphpare optional. The response holdsaccountand eithersiteorsite_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,pathandexpires_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_mbpsanddb_connections, 0 means automatic and -1 means unlimited.php_memory_mbdefaults to 256 andphp_childrento 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) and429answers 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 |