Panellicense

Create and scope WHM API tokens for safe automation

WHM API tokens replace root passwords for billing scripts, monitoring agents, and Blesta or WHMCS integrations — here's how to scope them tightly and rotate them safely.

8 min readUpdated 2026-05-16cpanel · whm · api · automation
schema: HowToschema: FAQPageschema: BreadcrumbList

Every WHM server that talks to a billing platform, a monitoring agent, or a CI job is authenticating something on every call. If that something is the root password — or a session cookie scraped from a logged-in browser — you have a credential that can do anything WHM can do, sitting in a config file, an environment variable, or a CI secret store. WHM API tokens are the fix. They are scoped, revocable, attributable, and they cost nothing to issue.

This guide covers how to create a token, scope it down to the minimum ACLs it needs, test it against whmapi1, and rotate it without breaking the systems that depend on it.

Why tokens, not passwords or session cookies

Three concrete reasons to stop authenticating WHM automation with the root password:

  • Rotation breaks everything at once. Change the root password and every script that embedded it stops working until you've found and updated each one. A token rotation is one-at-a-time and lets you cut over without downtime.
  • Tokens have ACLs; the root password doesn't. A billing module that only needs to create accounts and suspend them should not be able to read /etc/shadow over WHM's fetchsslinfo calls or run arbitrary shell via cpanel_exec.
  • Audit trail. Every API call signed with a token is logged with the token name in /usr/local/cpanel/logs/api_tokens_log. Root-password auth shows up as "root", and you cannot tell which automation it came from.

Session cookies are worse still — they expire, can't be CIDR-restricted, and grant the full ACL set of the user they belong to.

Step 1 — Create a token in the UI

In WHM → Development → Manage API Tokens → Generate Token:

  1. Name — descriptive, no spaces. blesta-prod, prometheus-exporter, nightly-backup-rotation. The token name shows up in audit logs; future you will thank present you for naming things specifically.
  2. Expiration — set one. WHM defaults to "never expires," which is the wrong default for anything other than infrastructure tokens you actively monitor. Six or twelve months is reasonable. The UI will not warn you when a token is about to expire, so put the date in your calendar when you create it.
  3. ACL — start with None, then grant only what the caller actually needs. The default suggestions ("All Features") are the equivalent of granting root.

Click Save. WHM displays the token once — copy it now. If you close the dialog without saving the token somewhere durable, you have to delete it and generate a new one.

The token format is a 32-character string. WHM stores only a hash; nothing on the server can reveal the plaintext after the dialog closes.

Step 2 — Generate from the CLI when you're scripting onboarding

For provisioning automation, the UI is the wrong tool. Use whmapi1 directly:

whmapi1 api_token_create token_name=blesta-prod \
  acl-1=create-acct acl-2=kill-acct acl-3=suspend-acct \
  acl-4=list-accts acl-5=show-bandwidth \
  expires_at=1797811200

expires_at is a Unix timestamp. Generate one with date -d "+1 year" +%s on Linux or date -v+1y +%s on macOS. The full list of ACL names is in /var/cpanel/acllists/ — each file is a feature group, and the filename is the ACL string you pass to api_token_create.

The response includes the token value at data.token. Capture it from the JSON, write it to your secret store, and discard the shell history line that contained it:

TOKEN=$(whmapi1 --output=jsonpretty api_token_create token_name=blesta-prod \
  acl-1=create-acct acl-2=suspend-acct | jq -r '.data.token')

# write to your secret store, e.g.
op item create --category=password --title="WHM blesta-prod" password="$TOKEN"

Step 3 — Scope ACLs to the actual workload

The five most common automation profiles, and the minimum ACLs each needs:

WorkloadRequired ACLs
Billing module creating/suspending accounts (Blesta, WHMCS)create-acct, kill-acct, suspend-acct, upgrade-account, list-accts, edit-account
Monitoring (uptime, load, disk)basic-system-info, show-bandwidth, list-accts
DNS automationadd-dns, edit-dns, kill-dns, park-dns
Backup orchestrationlist-accts, restore-base-understanding, backup
SSL automationmanage-ssl, ssl, ssl-gencrt

Do not grant all unless the caller is genuinely a root replacement (and if it is, rotate it monthly and CIDR-restrict it). Avoid cpanel_api — it lets the token act as any cPanel user on the server, which is essentially root via a different door.

If you discover a token needs an additional ACL later, update it in place rather than re-issuing:

whmapi1 api_token_update token_name=blesta-prod new-acl-1=park-dns

The token value does not change; only the ACL set does.

Step 4 — Test the token before handing it to automation

Authenticate with the WHM USERNAME:TOKEN header format. Use root as the username for tokens issued under the root account, or the reseller's username for reseller-issued tokens:

curl -sk \
  -H "Authorization: whm root:THE_TOKEN_VALUE_HERE" \
  "https://server1.example.com:2087/json-api/listaccts?api.version=1" | jq '.metadata'

A successful call returns metadata.result: 1. A 403 means the ACL is missing; a 401 means the token name or value is wrong; a 503 means cpsrvd is restarting (give it 30 seconds).

For deeper testing, hit a write endpoint in a controlled way. The cleanest "this token actually works" check is applist, which returns the list of API functions the token can call:

curl -sk -H "Authorization: whm root:$TOKEN" \
  "https://server1.example.com:2087/json-api/applist?api.version=1" | jq '.app | length'

If the number is shorter than you expect, you missed an ACL.

Step 5 — Lock down where the token can be used

WHM does not natively bind tokens to source IPs — that's a gap. The two ways to close it:

  • Firewall the WHM port (2087) at the host. Limit inbound tcp/2087 to the CIDRs that legitimately call WHM (your office, your billing host, your monitoring system). Pair with a working cPHulk configuration so brute-force noise doesn't drown the log.
  • Run automation through a bastion that holds the token and exposes a narrower internal API. This is overkill for a single server, but if you have a fleet of 50 cPanel hosts, one signing service with per-host tokens beats 50 secrets in every CI job.

Step 6 — List, revoke, and rotate

List active tokens with their ACL sets and last-used timestamp:

whmapi1 api_token_list_tokens

Revoke a token by name. The change is immediate; in-flight requests using that token will get a 401 on the next call:

whmapi1 api_token_revoke token_name=blesta-prod

Rotation is "create new, switch callers, revoke old." Never edit the token value (you can't), and never reuse a token name within seconds of revoking it — the audit log will conflate the two.

A rotation script for a single caller looks like:

NEW=$(whmapi1 --output=jsonpretty api_token_create token_name=blesta-prod-2026q3 \
  acl-1=create-acct acl-2=suspend-acct | jq -r '.data.token')

# Update the caller (Blesta module config, env var, secret manager) with $NEW
# Verify the next scheduled billing run succeeds
# Then revoke the old one
whmapi1 api_token_revoke token_name=blesta-prod

Build this into the same playbook you use for WHM transfer operations — credential rotation belongs next to credential creation, not in a separate runbook that drifts.

Next steps

Can I use a WHM API token from a reseller account?+
Yes. Resellers generate tokens from their own WHM session, and the token inherits the reseller's ACL set — it cannot exceed what the reseller is allowed to do. Authenticate with the reseller username, not root.
Do WHM API tokens work with the cPanel UAPI for user-level operations?+
No. WHM tokens authenticate to the WHM API (port 2087, /json-api/ endpoints). Per-user cPanel UAPI calls (port 2083, /execute/) need cPanel API tokens, which each cPanel user generates from their own interface under Manage API Tokens.
How do I see which token was used for a specific API call?+
Check /usr/local/cpanel/logs/api_tokens_log. Each line includes the token name, the calling IP, the endpoint, and the timestamp. This is the file to grep when you need to attribute a configuration change to a specific automation.
What happens to running scripts when I revoke a token?+
Requests already on the wire complete; the next request returns 401. There is no grace period. For zero-downtime rotation, issue the new token, deploy it to the caller, verify a real call succeeds, then revoke the old one.
Can I restrict a WHM API token to a single endpoint?+
Not directly. ACLs are coarse — they map to feature groups, not individual API functions. The closest you can get is granting a single narrow ACL (e.g. show-bandwidth alone) and accepting that the token can call every function inside that group.
Why does my Blesta or WHMCS module still ask for a root password?+
Older module versions predate token support. Most maintained modules now accept a token in the password field; check the module's release notes. If it really only accepts a password, the safer option is to run the module against a dedicated reseller account with a strong unique password rather than root.
Switch in an afternoon

Switch from your current reseller — free.

We migrate active cPanel, Plesk, LiteSpeed and CloudLinux licenses from any reseller. We prorate the first month so you never pay twice, and your customers see zero downtime during the swap.