Billing integrations: WHMCS, Blesta and WiseCP
Last updated
Jabali Panel has provisioning modules for three billing systems: WHMCS, Blesta and WiseCP. They are in the jabali-billing-modules repository on GitHub, under the same AGPL-3.0 licence as the panel. When a customer orders, upgrades, stops paying or cancels, the billing system calls the panel and the hosting account changes to match.
Supported billing systems
| Billing system | Version | Install folder | Setup guide |
|---|---|---|---|
| WHMCS | 8.x | modules/servers/jabali/ | WHMCS module |
| Blesta | 5.x | components/modules/jabali/ | Blesta module |
| WiseCP | 3.x | coremio/modules/Servers/Jabali/ | WiseCP module |
All three need PHP 7.4 or newer with the curl and json extensions on the billing server.
How the modules talk to the panel
The modules call the panel’s Automation API under /api/v1/automation/. Every request is signed with HMAC-SHA256 using an automation token. The billing system never stores a panel admin password, and there is no login session to expire. All three modules ship the same PHP API client, so signing, retries and error messages are identical.
- Port. The API answers over HTTPS on
:8443. Some billing hosts block outbound 8443 (CSF’s defaultTCP_OUTdoes). For them, turn on Also serve the API on port 443 on the panel’s Server API Access page and use port443in the billing system. Port 443 serves only the automation routes. - Certificate. The panel must present a browser-trusted certificate on that port. The modules have no option to skip certificate checks.
- Clock. The request timestamp must be within 5 minutes of the panel’s clock, and no more than 30 seconds ahead of it. Keep the billing server and the panel on NTP.
- Replay protection. The panel accepts each signature once. When a module retries a request, it signs it again with a new timestamp.
1. Create an automation token
Open Server API Access (/jabali-admin/automation) in the admin panel and click Create token. Name the token after the billing system, pick the scopes below and click Generate. The secret is shown once, as a 64-character hex string. Paste it into the billing system straight away: the panel keeps it encrypted and never shows it again. Make sure Writes enabled is on for the token.
From the command line:
jabali automation-token mint whmcs \
--scope read:status --scope read:users --scope read:packages \
--scope write:users --scope write:domains --scope delete:users
| Scope | What the modules use it for |
|---|---|
read:status | Test Connection |
read:users | Finding accounts; disk and bandwidth usage |
read:packages | The package dropdown in product settings |
write:users | Create, suspend, unsuspend, change password, change package, one-click login |
write:domains | Creating the order’s domain together with the account |
delete:users | Terminate or cancel. Leave it out if accounts should only ever be suspended. |
assert:domain_ownership | Optional. The new account’s domain is verified at once instead of waiting for DNS proof. |
write:domainsis required for orders with a domain. All three modules send the order’s domain when they create the account. If the token lackswrite:domains, the panel rejects the create with403 scope_denied. Test Connection does not catch this: it confirms the token works and lists the panel’s actions, but does not check that the token holds every scope the modules need.
assert:domain_ownership lets the billing system vouch that its customer owns the domain. Without it, the domain waits for DNS proof of ownership, like a domain a customer adds in the panel. write:* does not include this scope; grant it only to a billing system you trust.
Use one token per billing system, so you can revoke one without touching the others. A token with write scopes can change any non-admin account on the server, so store the secret like a root password. Revoke a token on the same page or with jabali automation-token revoke <name>. It stops working on its next request.
2. Install the module
Each module installs by copying one folder into the billing system. The steps for each system are in its guide: WHMCS, Blesta, WiseCP. Copy the whole folder: each module loads its API client from its own lib/ subfolder.
3. Add the panel as a server
Every billing system asks for the same four values:
| Billing field | Value |
|---|---|
| Hostname | The panel hostname, e.g. panel.example.com |
| Username / Token ID | The automation token’s ID |
| Password / Token secret | The automation token’s secret |
| Port | 8443, or 443 if you turned on the port 443 option |
Then run the connection test. It reports the actions the panel offers. “No write capabilities advertised” means the panel is older than the billing endpoints. Upgrade it.
4. Map products to packages
Each billing product points at a Jabali hosting package. The package sets the account’s limits; see Hosting Packages. With read:packages, the product settings show a dropdown of the panel’s packages. Without it, paste the package ID (a 26-character ULID) into the manual override field. The package is applied when the account is created and again on upgrade or downgrade.
What each billing action does
| Billing action | Effect on the panel | Endpoint | Scope |
|---|---|---|---|
| Test connection | Checks the token and lists the panel’s actions | GET /automation/status, GET /automation/capabilities | read:status |
| Create | Creates the user on the product’s package and creates the order’s domain | POST /automation/users | write:users, write:domains |
| Suspend | Runs the same suspend as the admin panel: login blocked, sessions ended, domains disabled, Docker apps stopped, SSH and SFTP locked | POST /automation/users/:id/disable | write:users |
| Unsuspend | Reverses the suspend | POST /automation/users/:id/enable | write:users |
| Change password | Sets the customer’s panel password | POST /automation/users/:id/password | write:users |
| Change package | Moves the account to the new package | PUT /automation/users/:id/package | write:users |
| One-click login | Returns a single-use login link that expires after 5 minutes | POST /automation/users/:id/login-token | write:users |
| Usage | Reads disk and bandwidth used and quota | GET /automation/users/:id/usage | read:users |
| Terminate / cancel | Deletes the account and its domains | DELETE /automation/users/:id | delete:users |
Some things hold for every module:
- Admin accounts are out of reach. The API never creates an admin. It refuses to suspend, delete, change the password of, or create a login link for one.
- Creating an account is safe to retry. If the email and username already belong to the same account, the panel answers with that account’s ID instead of creating a second one.
- Writes are limited to 30 a minute per token, with bursts of up to 10. Spread out bulk jobs, or give each billing server its own token.
- Every write is audited under the token’s ID in the Audit Log. Creating, suspending or deleting an account also sends a panel notification.
How accounts are matched
The panel identifies an account by its username. Email is not unique: one billing client can own several hosting accounts under the same email address. Creating an account with a new username and an existing email makes a second account. Only a username that is already taken returns 409 conflict.
When an account is created, the module saves the panel’s user ID (a 26-character ULID) and uses it for every later action. If the ID is missing, the module falls back to the username, and accepts an email match only when exactly one account has that email.
| Billing system | Where the panel user ID is kept |
|---|---|
| WHMCS | The product custom field jabali_user_id |
| Blesta | The service fields jabali_user_id, jabali_username and jabali_domain |
| WiseCP | The order’s creation info, editable as Jabali User ID in the admin service view |
When the panel lacks an action
When a module connects, it calls GET /automation/capabilities to see which actions the panel offers. If an action is missing, usually because the panel is older than that endpoint, the module returns a clear message instead of failing halfway through provisioning. Suspend, unsuspend and the connection test work on any current Jabali Panel.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
Create fails with 403 scope_denied: “creating a domain requires the write:domains scope” | The token lacks write:domains. Create a token with the full scope list above and update the server’s credentials. |
scope_denied on another action | The token lacks that action’s scope. Check the scope table above. |
401 with “check NTP” | The clocks on the billing server and the panel differ by more than 5 minutes. Sync both with NTP. |
401 “replay detected” | The same signed request reached the panel twice. The module signs every retry again, so a repeat usually means two cron runs overlapping. |
429 rate_limited | More than 30 writes a minute on one token. Spread out the job or use a token per billing server. |
| Connection times out | The billing host’s outbound firewall blocks 8443. Turn on the port 443 option and set the server port to 443. |
| TLS or certificate error | The panel is not presenting a trusted certificate on that port. See Panel Certificate. |
| Package dropdown is empty | The token lacks read:packages. Add it, or paste the package ID into the override field. |
| ”panel does not support X yet” | The panel is older than that endpoint. Upgrade the panel. |
| Login link opens the sign-in page | The panel predates one-click login. Upgrade the panel. |