Skip to content

WHMCS

The CorePanel module for WHMCS lets your billing system run hosting accounts on its own: it creates them when an order is paid, suspends them when an invoice is overdue, lifts the suspension when it is settled, moves customers between packages, resets passwords, reports disk and bandwidth usage, and signs customers into their control panel from your client area.

It is a thin client of the panel’s JSON-RPC API — the same API the panel’s own interface speaks — authenticated with an API token. Nothing is decided in the module: what a suspension closes and what a package grants is decided by the panel, so a suspension from WHMCS and a suspension from the panel are the same act.

The module requires CorePanel Business on the server, and WHMCS 8.0 or newer.

How the two systems fit together: WHMCS on the left holds the customer, the invoice and the service username; an API token in the middle is the only credential between them; CorePanel on the right holds the account. Arrows from WHMCS to CorePanel are labelled with each billing action and the API method it calls — create, suspend, unsuspend, change package, change password, usage update, terminate and sign in. One arrow points back from CorePanel to WHMCS, labelled "the account's real username", showing that the panel is the authority on the name.

In the panel, go to Server → API tokens and create one with the provisioning scope, plus terminate if you want WHMCS to delete accounts when a service is cancelled.

Terminal window
corepanel api-token create "WHMCS production" --scope provisioning --scope terminate

The credential is shown once. Copy it.

Download it and unzip it into your WHMCS root, so the module lands in modules/servers/corepanel/. No other file is touched.

Terminal window
cd /path/to/whmcs
curl -fsSLO https://get.corepanel.net/whmcs/whmcs-corepanel-latest.zip
unzip whmcs-corepanel-latest.zip

A specific version is at https://get.corepanel.net/whmcs/whmcs-corepanel-1.0.0.zip; latest always points at the current one.

System Settings → Servers → Add New Server:

FieldValue
HostnameThe panel’s hostname, exactly as its TLS certificate spells it
ModuleCorePanel
Access HashThe token (cpk_…)
Username / PasswordLeave empty — a token authenticates on its own

Then press Test Connection. It reports the panel’s version and refuses if the server’s edition cannot do billing automation, so a licence problem surfaces now rather than on your first overdue invoice.

On the product, Module Settings has two fields:

  • Hosting package — the name of a package in the panel, from Server → Packages. The package decides every limit the account gets. Left empty, accounts are created with the server defaults.
  • PHP version — for example 8.3. Left empty, the highest version installed on the server is used.

Limits live in the panel’s package and nowhere else. A product that repeated them in WHMCS would give you two places for the same number to be wrong.

In WHMCSOn the server
CreateCreates the account with the product’s package and PHP version
SuspendSuspends the account across every plane
UnsuspendLifts it and restores what it changed
TerminateDeletes the account and its data
Change PasswordChanges the account’s panel/FTP login. Its FTP users keep their own passwords
Change PackageApplies the new package’s limits to the existing account
Usage UpdateWrites each account’s disk and bandwidth back into WHMCS, once a day
”Log in to the control panel”Signs the customer in with a single-use link, valid for 60 seconds

WHMCS generates a username when the order is placed; the module sends it to the panel and then writes the panel’s answer back onto the service. So the two sides always agree about who the customer is, even when the panel had to adjust the name.

Names are normalised to what the panel accepts — lowercase letters and digits, not starting with a digit, at most 16 characters — so Shop_EU-2 becomes shopeu2. The restriction is not cosmetic: an underscore would put two accounts into one MySQL namespace, and a name that is all digits could not be told apart from an account id.

Suspending an account that is already suspended, unsuspending one that is not, and terminating an account that no longer exists all report success: the state the request asked for is the state the server is in, and WHMCS retries failed actions. A refused token or an unreachable server never reports success — a cancellation that never happened must not close the service in your billing system.

There is no option to disable certificate checking. This credential can suspend and delete every account on the server; accepting an unverified certificate would hand that to anything on the network able to answer for the hostname.

A server that still serves a self-signed certificate — a freshly installed or freshly transformed box — has to finish issuing a real one before WHMCS can drive it. Registering the server under the hostname its certificate covers is usually all it takes.

The figure reported to WHMCS is what the account’s disk quota is checked against: the files it owns. Its MySQL databases live under the database server’s own user, where no per-account quota can see them, so they are not added in — the number has to mean the same thing as the limit printed next to it.

Nothing about the module is disabled from our side. Each operation is checked by the panel against the server’s own entitlements, so a lapsed licence stops exactly what that edition stops and no more: reading and reporting keep working, and a refused operation comes back with the reason in your WHMCS activity log. See Licensing.

The module above is for a new server entry in WHMCS. That is fine for a new server and wrong for an existing one: adopting it on a box you have been provisioning for years means creating a server entry, re-pointing every product and re-linking hundreds of live services — which nobody does to try a new panel.

So there is a second, entirely separate route. corepanel-whmcompat answers the WHM API on :2087, the way cPanel’s whostmgrd did, using the hostname, the IP and the credential your WHMCS already has. After a transform your billing system keeps talking to the same server in the same way, and there is nothing to reconfigure.

It is a different package from the panel, and you have to ask for it:

Terminal window
dnf install corepanel-whmcompat
corepanel whmcompat enable

It requires CorePanel Business, like the module above: enable is refused on any other edition, and says so. Turning it off and reading its state are never refused — a licence that lapsed must not leave you unable to close a port you opened.

That applies to the WHM API specifically. The same package also fixes your customers’ old :2083 and :2096 bookmarks, and that part is available on every edition — a Personal server that came from cPanel installs corepanel-whmcompat for that alone and never enables the API at all.

enable opens the port in the host firewall too, and disable closes it — in the corepanel zone on a server that adopted the host firewall, in the distribution’s default zone otherwise. The command prints which one it touched:

Firewall: port 2087/tcp opened in the "public" firewall zone

Worth reading that line, because the two halves fail independently. A daemon listening on :2087 behind a firewall that drops the traffic passes corepanel whmcompat selftest — which runs over loopback — and is still unreachable for your WHMCS, so the only symptom is a server WHMCS reports as down. If the line says nothing was changed, that is because firewalld is stopped or absent on this server, and nothing is filtering the port anyway.

If you moved the API to another port in whmcompat.ini, open that one yourself: the switch manages 2087.

Your WHMCS holds either a WHM API token or the old access hash. Import whichever it is, once, and nothing on the WHMCS side changes:

Terminal window
corepanel whmcompat import-credential --user root --kind token < token.txt

Only the digest is stored. The server keeps proof of the credential, never the credential itself — SHA-512, which is exactly what cPanel stores for a WHM API token. A transform can therefore import your existing token straight from the cPanel install without anybody ever learning its value, and the WHMCS that holds the secret keeps authenticating unchanged.

Access hashes still work where they still work: a cPanel with allow_deprecated_accesshash switched on — which any hosting provider still using one necessarily has — authenticates the existing file unchanged, and so does this. Import both if you are not sure which one is in use; they cost nothing side by side.

To see what a server accepts, and to stop accepting one:

Terminal window
corepanel whmcompat credentials # digests, labels, dates
corepanel whmcompat revoke-credential --digest a9e70ee8

The digest is what names a credential — a label cannot, since several can share one. Revoking everything a user holds is --all, spelled out on purpose: an empty store accepts nothing, which is how a server stops answering your billing system altogether.

The compatibility layer is built against a recorded WHMCS session — a full service lifecycle captured from a real WHMCS 8.11 driving a real cPanel, request and response both — rather than against a guess at what WHMCS sends. Every row below has since been run again the other way round: through WHMCS’s own cPanel module, pointed at a CorePanel server.

WHMCS asks forStatus
Test Connection, server version and hostnameAnswered
The server status panelAnswered
The package list on a productAnswered
The account list and its disk and bandwidth figuresAnswered
Create an accountAnswered
Suspend and unsuspendAnswered
Change packageAnswered
Change passwordAnswered
TerminateAnswered
The nightly usage update (disk, bandwidth, overage)Answered
The usage bars in your client areaAnswered
The “Log in to cPanel” buttonAnswered, but off until you turn it on — see below

An action that is not implemented is refused in the shape WHMCS expects, with a reason in your Module Log. It never looks like the server being down.

The “Log in to cPanel” link in your client area works over this route, and it is off until you say otherwise:

Terminal window
corepanel whmcompat sso enable

Off by default, and off on servers that were already serving :2087 before this switch existed — enabling the layer months ago was not consent to this.

The reason it is separate is what the capability looks like from the outside. Everything else the layer does is loud: a password change stops the customer’s own password working, a suspension takes their site down, a termination shows up in three places. A minted session is silent — it is the customer’s own session, which is exactly what makes it the right thing for that button. Most billing setups only provision and suspend and never press it.

While it is off, the button reports a reason in your client area that names the command above, so an operator who turned it off by mistake finds out from their own client area rather than from a ticket. Customers can always sign in at the panel with their account username and password, and nothing else on this route is affected.

corepanel whmcompat status prints the switch in both states, on its own line, always — it is the one thing here that leaves no trace a customer would notice, so it has to be readable rather than inferable:

WHM API compatibility: enabled
Passwordless sign-in: disabled
Legacy bookmarks: enabled

The module at the top of this page is not affected by any of this: it reaches the same feature through an API token you issued, over the port the panel already owns, and its login button works whatever this switch says.

The rest of the old cPanel port set is a separate problem with a separate switch, and it is the one your customers notice first.

On a cPanel server, :2083 is the URL every customer has saved and :2096 is how they reach their webmail. The day you transform the server those become a refused connection — no page, no explanation, no clue where their panel went — for everybody at once.

Terminal window
corepanel whmcompat redirects enable

What answers the old cPanel ports. On the left, three kinds of caller: a customer&#x27;s old panel bookmark on https://sudominio.com:2083, their webmail bookmark on :2096, and anything that is not a browser — a /json-api/ path or a request carrying an Authorization header. In the middle, corepanel-whmcompat listening on 2082, 2083 and 2086 and on 2095 and 2096, asking one question of every request: is this a browser? A browser gets 302 Found; anything else gets an empty 404. On the right, where each lands: the panel at https://panel.suempresa.net/, their own webmail at https://webmail.sudominio.com/ only if this server serves that domain, and nowhere at all for the API client, with no Location header. The redirect target is built from the server&#x27;s own configuration, never from the request.

With that on, this server answers those ports and sends the browser where the thing lives now:

PortTLSA browser is sent to
2082nothe panel
2083yesthe panel
2086nothe panel
2095nowebmail.<domain>, for the domain it asked for
2096yeswebmail.<domain>, for the domain it asked for

This one is available on every edition, unlike the WHM API above. It is not part of “WHMCS integration”: what it serves is a redirect to your own panel, it holds no credential and it reads no parameter. A Personal server that came from a transform has exactly the same broken bookmarks as a Business one, so it gets the same fix.

It is off by default all the same, including on servers already serving :2087 — five ports on an internet-facing box is a decision you make, not something an upgrade does for you. The switch opens and closes them in the host firewall along with everything else.

What these ports will not do, because getting this wrong is worse than not doing it:

  • They never redirect something that is not a browser. A request on a /json-api/ path, or one carrying an Authorization header, is never given a redirect — an automated call that follows a 302 to a login page reports whatever it finds there as success. On :2086, cPanel’s cleartext WHM API port, an API client gets a readable refusal pointing at https://<hostname>:2087 instead; the other four answer nothing at all, as they did on cPanel.
  • They never send a browser to an address from the request. The webmail target is built from the Host header only after the panel confirms this server serves that domain. A request naming somebody else’s domain lands on your panel. Without that check these would be an open redirect on the most-scanned ports on the server.
  • They never send a permanent redirect. A 301 would be cached in the browsers of people who are not your customers, outliving any later change you make.

corepanel whmcompat status names which of the five are actually bound. Fewer than five means something else on the server is holding the rest — worth checking on a freshly transformed box, where a leftover from the old panel is the usual cause.

Five places, and they are all in the direction of not lying to you:

  • Terminating an account that is already gone reports success. cPanel reports an error. WHMCS retries what fails, so copying cPanel would leave a cancelled service stuck in a retry loop over a termination that had already worked. Suspending an already-suspended account also succeeds — that one matches cPanel.
  • keepdns is not honoured. Deleting an account always takes its DNS zones with it. The deletion still goes ahead, because refusing it would leave a customer hosted after they cancelled, and the answer says the zone went too.
  • A password change moves fewer credentials. On cPanel, passwd rotates the system, FTP, mail and MySQL passwords together. Here it changes the account’s panel login and its Linux user — so SSH and SFTP — and deliberately leaves FTP users, mailboxes and database users alone: those are separate credentials from the moment they are created. The answer reports exactly what changed rather than cPanel’s four.
  • A downgrade below current usage is refused, with a reason naming each limit and what the account is actually using. cPanel would apply it. A quota silently set below current usage is how an account ends up unable to write to its own files with nothing to point at.
  • Bandwidth counts web traffic, and only the current month. The figure your usage update collects is the bytes CorePanel’s web server served for that account. Mail, FTP and anything an application opens for itself are not in it yet, and neither is the protocol overhead underneath. Expect your numbers to drop after a migration — they are lower than cPanel’s, not different in units. There is also no history: a request for a past month is refused rather than answered with this month’s figures under last month’s label, so run your usage update on its normal schedule and it will never notice.

Check this side before touching WHMCS:

Terminal window
corepanel whmcompat selftest

That drives a real request over loopback — the port, TLS, the credential, the response envelope and the call into the panel, with nothing stubbed. When it passes, press Test Connection in WHMCS: only WHMCS proves WHMCS.

Full command reference: corepanel whmcompat.

  • Third-party WHMCS addons. cPanel Extended and similar addons drive hundreds of per-user cPanel API calls. Neither the module nor the compatibility layer implements a per-user cPanel API, and there are no plans to.

  • Creating resellers. WHMCS cannot make one: setupreseller, the per-reseller ACL list and setting a reseller’s limits are not implemented on either route. Resellers are created in the panel or from the CLI.

    Provisioning on behalf of an existing reseller does work, on the compatibility layer: createacct honours WHM’s owner=, so a product configured for a reseller creates the account under that reseller, and it counts against that reseller’s own ceilings — a full reseller is refused, with the wall named, exactly as the panel’s own button is refused. An owner naming nobody on this server is refused rather than quietly reassigned to you, and the refusal lists the resellers that do exist.

  • The admin’s “Log in to WHM” button. That is a different path from the customer’s: it asks for a session as root, which is not an account on a CorePanel server, and it is answered as the missing user it is. The customer-facing login button does work — on the module always, and on the compatibility layer once you turn it on.

Every call the module makes is recorded in Utilities → Logs → Module Log in WHMCS, with the request, the answer and the reason for any refusal. The panel’s own wording is passed through unchanged, so an error there says what the panel would have said.

That log is off by default — the page has an Enable Debug Logging button. Turn it on before reproducing a problem, and off again afterwards: it records every module on the system, not only this one.

What you seeWhat it means
CorePanel rejected the API tokenThe Access Hash is wrong, or the token was revoked in the panel
requires the "provisioning" scopeThe token was created without the scope this action needs. Scopes are fixed for a token’s life — create a new one
The panel's TLS certificate could not be verifiedThe server is registered under a name the certificate does not cover, or the panel has not issued one yet
No CorePanel account matches username "…"The account was renamed or removed in the panel; the service in WHMCS points at a name the server does not have
No hosting package named "…" existsThe product’s Hosting package does not match a package in the panel. The message lists the ones that do exist
licence does not cover the operationThe server’s edition does not include this action. See Licensing