RackHelm creates a Plesk customer for each portal user and a subscription — a webspace — for each order item they buy, then suspends, unsuspends and terminates those subscriptions as the billing requires. This article is for the operator setting that up: how to get an API key without ever storing a password, every field on the RackHelm form, how a product is bound to a Plesk service plan, and the Plesk-specific traps that will otherwise cost you an afternoon.
Before you start #
- The
edit_integrationspermission. See Roles and Permissions. - A Plesk server reachable from the RackHelm server. Plesk’s admin interface and REST API normally listen on port 8443.
- The login and password of the Plesk administrator, or of the reseller whose service plans your products will sell. You do not need a key in advance — RackHelm mints one for you and never stores the password.
- At least one hosting plan under Plesk » Service Plans » Hosting Plans, owned by that user.
Note: The hint under Base URL says it plainly: “Plesk listens on port 8443. A private or LAN address (a Plesk that cannot be made public) is refused unless it is listed in
SAFEURL_PRIVATE_ALLOWLISTin the portal’s.envfile.” That allow-list is new in 1.0.1.1 and takes addresses or CIDR ranges, for exampleSAFEURL_PRIVATE_ALLOWLIST="192.168.1.50,10.10.0.0/16". Everything else private stays blocked. See Adding a Control Panel Connection.
Note: RackHelm stores one Plesk connection, not a list. The form saves into a single row keyed on the provider name, so one RackHelm install drives one Plesk server. The Panel dropdown on a product will only ever offer that one entry.
Add the connection #
- Open Admin → Integrations and press Configure on the Plesk card (it reads Manage once a connection exists).
- Set Base URL to the Plesk origin including the port, for example
https://plesk.example.com:8443. - If Plesk’s certificate is self-signed, set Verify SSL to No (pin certificate).
- Under Generate an API key, enter the Plesk login and Password and press Generate API key. The key appears in the API Key field above.
- Press Save. That single action stores the key and, if you pinned one, the certificate.

X-API-Key header.Fields #
| Field | Rule | Notes |
|---|---|---|
| Name | Optional, max 120 characters | Defaults to “Plesk”. |
| Active | Checkbox | Required for Plesk to appear as an Integration Type on products. |
| Base URL | Required, http/https, publicly resolvable or allow-listed | Placeholder https://plesk.example.com:8443. |
| API Key | Optional on edit, max 190 characters | Blank keeps the stored key. Stored encrypted. The hint tells you which Plesk user the saved key belongs to, or says “No key is saved yet.” |
| HTTP Timeout (sec) | Integer, minimum 1 (form minimum 5) | Default 30. Creating and deleting a subscription override it to 180 seconds. |
| Verify SSL | Yes or No (pin certificate) | Read by the driver, the connection test and the product-form lookup. With No, Plesk’s public key is pinned and any other key is refused. |
| cPGuard API Key | Optional | Relocated into the encrypted credential block on save, never left in the plain settings blob. |
| Verify cPGuard SSL Certificate | Checkbox, on by default | Untick for a self-signed certificate on port 9098. |
Note: There is deliberately no password field on this form. The Plesk administrator password is used for exactly one request — the key request — and is not part of what gets saved. If an older build of RackHelm stored a password here, it is discarded the next time you press Save.
The pinned certificate #
With Verify SSL set to No (pin certificate), a line appears below reading “No certificate is pinned yet. Click Test API (or Generate API key) to read the panel’s certificate, then Save to pin it. Until then the portal refuses to connect with verification off.” After a successful test it shows the certificate in the shape CN=plesk.example.com, self-signed, expires 2027-03-14, SHA-256 A1:B2:… with a Forget pinned certificate link beside it.
Warning: Testing does not pin; Save pins. If you test, see “certificate pinned, click Save to keep it” and navigate away, nothing is stored. Later, when Plesk’s certificate is replaced, calls fail with “The panel’s certificate key no longer matches the pinned one. Open the panel settings, click Test API to see the new certificate, then Save to trust it.” That refusal happens before the API key is sent, which is the point of it.
Generate an API key #
Plesk issues API keys to a logged-in user. The block at the bottom of the form does that for you: RackHelm posts /api/v2/auth/keys with the login and password you type, checks the new key works, and drops it into the API Key field for you to Save. The password is used for that one request and is never stored, never logged, and never posted to Save.

On success the status line reads something like “Plesk 18.0.80 · API key generated for admin · click Save to store it. The password was not kept.”, and the hint under API Key changes to “New key generated for Plesk user admin. It is not saved yet: click Save.”
Warning: Plesk binds an API key to the IP address it saw the key request arrive from. A key you created on your laptop, or with
plesk bin secret_keyon the Plesk server itself, will be refused here with 401 however carefully you paste it. That is why the button exists: the key must be minted from the RackHelm server. The portal’s own refusal says so — “Plesk rejected the API key. Keys only work from the IP address that created them, so generate a new one from this screen (Generate API key) rather than pasting in one made elsewhere.”
Two login cases are worth knowing:
root— Plesk lets the system root account authenticate as the administrator, but root is not a Plesk user, so it cannot own a key. RackHelm leaves the owner out in that case and Plesk assigns the key to the administrator; the form then shows the login asadmin.- Any other non-Plesk login — the password is accepted and the key request still fails. You get “Plesk accepted the password but cannot issue a key to ‘name‘ because that is not a Plesk user account. Log in as ‘admin’ (the Plesk administrator) or as a reseller. Keys always belong to a Plesk user.”
The key is described in Plesk as your portal’s name, host and today’s date, so you can tell it apart in plesk bin secret_key --list.
Warning: If minting succeeds but the verification step is refused, the message reads “Plesk created a key but then refused it from this server… The key ending “…abc123” is unused; remove it in Plesk with
plesk bin secret_key --delete -key <key>.” This means the two requests reached Plesk from different addresses — a NAT, a VPN or a proxy in the path. Fix the path first; each failed attempt leaves an orphaned key behind.
When the problem is not your password #
Plesk’s Tools & Settings » Security » Restrict Administrative Access policy answers with HTTP 403 before it looks at any credential, which is indistinguishable from a wrong password by status alone. RackHelm recognises that specific refusal and tells you what to allow, naming the address Plesk saw:
Plesk's administrative access policy is blocking this server, which Plesk sees as 10.20.151.17.
In Plesk open Tools & Settings » Security » Restrict Administrative Access and allow 10.20.151.17
(or set the policy to allow everything except the listed networks), then try again.
The login and password were not the problem.
Testing the connection #
Test API runs GET /api/v2/server through the same client provisioning uses, with the TLS settings you have saved, and reports OK · Plesk 18.0.80 on success. A pass means hostname, port, certificate, firewall and key are all correct.
Leaving the key box blank re-tests with the stored key. Two deliberate refusals:
- “Re-enter the API token when changing the Base URL.” — you changed the base URL and left the key blank. The stored key is never rendered back to you, so resolving it onto a freshly typed host would be a way to have it delivered elsewhere.
- “No saved API token; please enter it.” — nothing is stored yet. Use Generate API key first; that button does not need a saved key.
Any failure other than a refused credential or a recognised access policy reads “The panel rejected the connection test. See the application log for the upstream response.” The wording is constant on purpose — varying it by Plesk’s reply would turn the button into a probe for whatever host was typed into Base URL.
Bind a product to Plesk #
Edit the product under Catalogue → Catalogue & Groups, open the Provisioning tab and, in the Server Integration card, set Integration Type to Plesk. Choose the Panel and the Service Plan dropdown fills itself from the server.

| Field | What it is for |
|---|---|
| Panel | Which saved Plesk connection to use. Credentials come from this row and nowhere else — a product cannot supply its own base URL or key. |
| Service Plan | Required. “Plesk » Service Plans » Hosting Plans. Each subscription this product creates is put on this plan; only plans owned by the API key’s user are listed.” |
| Suspend after (days past due) | Stored, default 1. Not read by anything — see the warning below. |
| Terminate after (days after suspend) | Stored, default 7. Same. |
| Provisioning Notes (internal) | Staff-only free text. Never shown to customers. |
Press Reload plans from panel after adding a plan in Plesk; the list is cached for 60 seconds otherwise. A saved plan the server no longer lists stays selected and is relabelled, for example Unlimited (saved value, not found on panel), rather than being silently dropped.
Note: Plesk’s REST API has no service-plan endpoint, so the list comes from one XML API call to
/enterprise/control/agent.php, authenticated with the same key. If that surface is disabled or the key’s user is not allowed to use it, the dropdown falls back to a plain text box with “Could not load the service plans from the panel: … You can type the plan name instead.” and the placeholder “Service plan name exactly as shown in Plesk”. A server with no plans at all reports “Plesk returned no service plans for this API key’s user. Create one under Service Plans » Hosting Plans.”
Note: Unlike in 1.0.0.0, Service Plan is now validated as required. Saving a Plesk product without one fails with “The integration.plesk.package id field is required.”
Warning: Suspend after (days past due) and Terminate after (days after suspend) are saved on the product and then ignored. The timing lives on the schedules Suspend Overdue Services and Terminate Overdue Services under Schedules, each with its own
daysoption, and both ship disabled. Until you enable them nothing is suspended or terminated automatically, whatever those product fields say. See Suspending, Unsuspending and Terminating a Service.
Warning: The Enable Provisioning switch at the top of the Server Integration card is not written when a product is saved. Switching it off does not stop provisioning; the integration is rebuilt as active every time. To sell a product without automatic provisioning, set Integration Type to None / Manual Processing.
The domain each subscription is created for #
A Plesk webspace is named after a domain, and RackHelm takes it from the order item — the DomainName the customer typed at checkout, or a domain already stored on the item. Attach the seeded Domain Name custom field (key DomainName) to the product under Options & Add-ons → Configurable Options, or give the product a fixed saved value. See Custom Fields.
Two failures come from this step:
Missing DomainName (OrderItem.variable_values or product/group saved variable).
'shop' is not a valid domain name for a Plesk subscription.
The second means the value is not a hostname — no dot, an illegal character, or shorter than four characters. Plesk is stricter here than cPanel, so a bare label that would have produced a cPanel account is refused outright.
What provisioning does #
Nothing provisions itself: the Provision Orders schedule finds paid items awaiting provisioning and queues each one, and like most schedules it ships disabled — enable it under Schedules. See How Automatic Provisioning Works. Once an item is queued the driver:
- Checks the product’s service plan actually exists on the server. If it does not: “Plesk has no service plan named ‘Silver’ for the API key’s user — check the product’s Service Plan setting (available: Unlimited, Starter).” If the list cannot be fetched for a reason other than a refused key, the check is skipped and the subscription call answers authoritatively instead.
- Refuses to touch a domain that already exists on the server: “The domain ‘example.com’ already exists on the Plesk server under another subscription.” A same-named webspace RackHelm did not create is somebody else’s, and adopting it would hand a stranger’s site to a new customer.
- Finds or creates the Plesk customer for this portal user. The login is derived from the local part of their email address — letters and digits only, at most 16 characters, starting with a letter — and falls back to that plus their user id, then plus a random suffix, if the login is taken. Plesk’s
external_idcarries the portal user id, which is how a re-link after a lost local record can identify our own customer rather than adopting a same-login stranger. - Invents an FTP login from the domain (at most 14 characters, never starting
testoradmin) and a 16-character password. - Creates the subscription with
POST /domains,hosting_type: virtual, the customer as owner and the plan by name. Neitherbase_domainnorparent_domainis sent, which is what makes Plesk create a new webspace rather than an add-on domain. - Stores the numeric Plesk domain id as the item’s external id, plus
panel_id,client_id,domain,domain_id,guid,plan,ftp_loginand the encrypted FTP password under the item’spleskmetadata key. The link between portal user and Plesk customer is kept separately, per panel.
Every step is safe to re-run. A webspace this driver already created is adopted rather than duplicated, and an existing Plesk customer carrying our user id is reused.
Warning: The generated Plesk customer password and FTP password are stored only as ciphertext and nothing reads them back. They are not emailed — the “Your Service is Ready!” notification carries a dashboard link and no credentials — and no screen reveals them. Customers work through the portal’s Sites area, which mints its own one-time Plesk sessions. If someone needs the password itself, reset it in Plesk.
Warning: Provisioning is attempted once. A failed item is marked failed, an admin notification “Provisioning failed for order item #…” is raised, and nothing retries it — the background scan only picks up items that are pending or unstarted. Clear it by hand from Pending Provisioning with Retry Provisioning or Mark Provisioned. See The Pending Provisioning Queue.
Renewal, suspension and termination #
| Action | What RackHelm does |
|---|---|
| Renew | Nothing on the server. The driver answers “Hosting renewal is billing-only. No remote action needed.” |
| Suspend | PUT /domains/{id}/status with suspended. |
| Unsuspend | The same call with active. |
| Terminate | DELETE /domains/{id}, tolerating a webspace that has already gone, then the customer clean-up below. |
All three read the numeric domain id from the order item. Without it they fail with “Plesk domain id not found on OrderItem.”
Warning: Terminating the customer’s last subscription also deletes their Plesk customer account and the portal’s link to it. Plesk removes a customer’s subscriptions along with the customer, so this only runs when Plesk itself confirms the customer has no webspaces left. If that listing is unavailable — older Plesk builds do not serve it — the clean-up is skipped and the result says so: “Plesk subscription terminated (Plesk client cleanup skipped: …)”. Those orphaned Plesk customers accumulate quietly; tidy them up by hand.
What the customer sees #
A provisioned Plesk item appears under Sites in the client portal, and since 1.0.1.1 that area is real on Plesk, not just Enhance. The portal translates its own screens onto three Plesk surfaces — REST for subscriptions, customers and databases; the XML API for the DNS zone, mail, FTP users, webspace properties and certificates; and the CLI gateway for everything neither exposes. The customer gets FTP, Domains and DNS records, Email, SQL databases and users (with phpMyAdmin reached through a one-time Plesk session minted for the subscription’s own owner), the PHP version, and WordPress installs through WP Toolkit.
Note: Backups are not offered on Plesk. Plesk’s backup list is per storage, not per subscription, so a customer cannot be shown only their own. The Backups card is hidden rather than shown broken, and anyone reaching it by URL is told “Creating a backup is handled in Plesk under Backup Manager on this platform, where you can also download a copy. Open your control panel to run one, or contact support and we will take care of it.” Restores are deliberately staff-only: “Restoring a Plesk backup is done by our team so nothing is overwritten by accident. Please open a ticket with the date you would like restored.” cPanel is the only other panel that does offer backups in the portal.
Signing straight into WordPress is the other gap: “Signing straight into WordPress is not available from here on Plesk hosting. Open your control panel, go to WP Toolkit and use the Log In button on the installation, which signs you in without a password.” Installing, removing and updating WordPress all work from the portal.
See My Sites: the site dashboard for the customer’s view.
Troubleshooting #
| Symptom | Cause and fix |
|---|---|
| “Plesk panel has Verify SSL switched off but no pinned certificate” | The upgrade case. Open the connection, Test API, then Save. |
| “The panel’s certificate key no longer matches the pinned one” | Plesk’s certificate was replaced. Test API to see the new one, then Save to trust it. |
| “The panel presented a certificate the portal cannot verify” | Verification is on and Plesk’s certificate is self-signed. Set Verify SSL to No, test, then save to pin it. |
| Test API says the key was rejected, and the key was pasted in | Plesk binds keys to the IP that created them. Use Generate API key on this screen instead. |
| 403 that looks like a wrong password | Plesk’s administrative access policy. The message names the address to allow under Tools & Settings » Security » Restrict Administrative Access. |
| “Plesk accepted the password but cannot issue a key to ‘…'” | That login is not a Plesk user. Use admin, or a reseller login. |
| “Plesk created a key but then refused it from this server” | The mint and verify requests reached Plesk from different addresses. Remove the orphaned key with plesk bin secret_key --delete -key <key> and fix the NAT or VPN in the path. |
| “Plesk needs an API key. Open Admin » Integrations » Plesk and generate one with the Plesk admin login and password.” | No key is stored. Generate one and save. |
| Service Plan dropdown is empty or has become a text box | The XML API could not be reached, or the key’s user owns no plans. The message on the form says which. |
| Product will not save | Service Plan is empty: “The integration.plesk.package id field is required.” |
| Order fails with “Missing DomainName …” | The product has no Domain Name configurable option and no saved value. |
| “‘…’ is not a valid domain name for a Plesk subscription.” | The customer typed a bare label, not a hostname. Correct the item’s domain and retry. |
| “The domain ‘…’ already exists on the Plesk server under another subscription.” | Deliberate. Remove or rename the existing webspace in Plesk, or provision this item by hand. |
| “Plesk has no service plan named ‘…’ for the API key’s user” | The plan was renamed or belongs to a different Plesk user. Reload the list on the product and pick again. |
| Nothing is ever suspended for non-payment | The Suspend Overdue Services schedule is disabled by default. The product’s Suspend after value is not used. |
| Plesk missing from the Integration Type dropdown | The connection is saved but not Active. |
| Base URL rejected on save | The host resolves to a private, reserved or unresolvable address. Use a public hostname, or list the address in SAFEURL_PRIVATE_ALLOWLIST. |
| Item stuck showing “We’re setting up your service…” | The job was killed mid-run, leaving the item in processing. Resolve it from Pending Provisioning. Subscription creation is allowed 180 seconds and the job 270, so the worker timeout must be at least that; see Queue Workers, Horizon and the Scheduler. |