PHP SDK

Updated Sep 27, 2026 Markdown

Package: wisecp/whost-php-sdk 0.1.1 — a download from this site Download: whost-php-sdk.zip · SHA-256: whost-php-sdk.zip.sha256 Requires: PHP 7.4 or newer with the curl and json extensions

The SDK is a PHP client for the WHost admin API. It is not tied to one billing system: WISECP, WHMCS, Blesta and your own code use the same package. It signs every request, offers an idempotency key on every write call, retries on request, maps error answers to typed exceptions and verifies incoming webhooks.

The package is not published on Packagist or GitHub; the archive on this site is the way to get it.


Download and install

shell
curl -O https://whost.wisecp.com/developer/downloads/whost-php-sdk.zip
curl -O https://whost.wisecp.com/developer/downloads/whost-php-sdk.zip.sha256
sha256sum -c whost-php-sdk.zip.sha256
unzip whost-php-sdk.zip

The archive holds one folder, whost-php-sdk/: the SDK (src/), four examples, a README, the CHANGELOG, the LICENSE and a vendor/ folder with the SDK's dependencies (Guzzle 7 and the PSR HTTP interfaces, chosen to run on PHP 7.4 and newer).

Licence. The LICENSE lets you use, copy and modify the SDK, its examples included, and distribute it as part of your own integration that connects to WHost (for example a billing-system module) — not on its own. Keep the LICENSE file and the copyright notices in every copy; anything else needs written permission from WISECP LLC.

Without Composer — load the bundled autoloader:

PHP
require __DIR__ . '/whost-php-sdk/vendor/autoload.php';

With Composer — if your project already has its own vendor/, delete the bundled whost-php-sdk/vendor/ folder, point a path repository at the extracted folder and require the package; Composer then installs the dependencies itself:

JSON
{
  "repositories": [
    { "type": "path", "url": "./whost-php-sdk", "options": { "symlink": false } }
  ],
  "require": {
    "wisecp/whost-php-sdk": "^0.1"
  }
}

Run composer update wisecp/whost-php-sdk afterwards. Without the path repository composer require wisecp/whost-php-sdk does not find the package.


API key

Create a key in the WHost admin panel under Settings → API Access and limit it to the addresses your integration calls from. The secret is shown once, when the key is created; keep it outside your code (an environment variable or a secrets store).

A key created there is a server-wide key and reaches the admin API the SDK calls. A key that a reseller or customer creates in the client panel is bound to that account and reaches only the client API (/api/v1/client/...); the SDK's resources answer 403 SCOPE_DENIED for it.


Quickstart

PHP
<?php
require __DIR__ . '/whost-php-sdk/vendor/autoload.php';

use WHost\WHostClient;
use WHost\Exceptions\WHostException;

$whost = new WHostClient(
    (string) getenv('WHOST_API_KEY'),
    (string) getenv('WHOST_API_SECRET'),
    'https://panel.example.com',   // the address you open the WHost panel on
    ['timeout' => 30.0]
);

try {
    $page = $whost->accounts->list(['limit' => 25]);
    foreach ($page['accounts'] as $account) {
        echo $account['username'], "\t", $account['domain'], PHP_EOL;
    }

    $account = $whost->accounts->create([
        'username' => 'demo01',
        'domain'   => 'demo01.example.com',
        'email'    => '[email protected]',
        'password' => $password,   // plain text over TLS; the agent hashes it itself
        'plan_id'  => $planId,     // the "id" of a plan from $whost->plans->list()
    ], 'order-12345');             // idempotency key, see below

    echo 'Created: ', $account['username'], PHP_EOL;
} catch (WHostException $e) {
    fwrite(STDERR, sprintf(
        "[%d %s] %s\n",
        $e->getHttpStatus(),
        $e->getErrorCode(),
        $e->getMessage()
    ));
    exit(1);
}
  • Base URL: the panel's address; the client appends /api/v1 itself. The WHost agent listens only on the server's loopback interface, so requests reach it through the panel's web server.
  • TLS: certificate verification is on by default (verify_ssl); turn it off only for a test server with a self-signed certificate.
  • Answers: each method returns the data field of the agent's answer as an array.

What the SDK covers

One accessor per area of the admin API, 38 in all: accounts, domains, DNS, SSL, e-mail, databases, FTP, files, cron, backups, PHP, plans, Python and Node.js applications, firewall, Fail2Ban, WAF, spam filter, mail queue and sent mail, logs, metrics, system and services, updates, licence, migration, plugins, reseller permissions and white label. The method list of each accessor is in src/Resources/ of the archive; an IDE completes it from there.

Only a signed-in panel session reaches these, not an API key: API key management (apiKeys), the administrator's own profile (profile), the administrator's notification centre (notifications), the panel sign-in calls (auth) and the white-label settings. Over an API key they answer 401 AUTH_FAILED. Changing an account's password (accounts->changePassword()) or the server's root password is refused for API keys too (403 HMAC_FORBIDDEN_FOR_CREDENTIAL_MUTATION); the account owner changes the password in the panel.

No helper method yet: webhook endpoint management (/api/v1/system/webhooks/...), the client API (/api/v1/client/...) and some newer admin endpoints. Call them through the low-level client with the path after /api/v1:

PHP
$events = $whost->http()->request('GET', '/system/webhooks/events');
// request(string $method, string $path, ?array $body = null,
//         array $query = [], ?string $idempotencyKey = null)

Every endpoint is listed in the endpoint catalogue.


Idempotency

Write methods take an optional idempotency key as their last argument: the second for create($data, $key), the third where the method also takes a data array, as in suspend($username, $data, $key). A key is 8–64 characters of letters, digits, - and _; use one key per business event, for example order-12345 for provisioning order 12345.

The agent keeps the answer under that key for 24 hours:

Same key, same method, path and bodyThe stored answer, header X-Idempotent-Replay: true; the action does not run again. A secret shown once (API key or webhook secret, 2FA material, one-time sign-in link, OAuth token) comes back as "***"
Same key, different method, path or body409 IDEMPOTENCY_KEY_REUSED
Same key from another API keyA new request; answers are kept per API key
Malformed key400 IDEMPOTENCY_KEY_INVALID
An earlier attempt ended in a server error (5xx)Not stored — the retry runs the action

Retries

PHP
$whost = $whost->withRetry(3);

retries a request up to three more times on 429, on 5xx and on connection errors, waiting 1 s, 2 s and 5 s. Send an idempotency key with every write call you retry.


Errors

AuthExceptionHTTP 401 or 403, or an error_code that starts with AUTH_
NotFoundExceptionHTTP 404
RateLimitExceptionHTTP 429 or RATE_LIMIT_EXCEEDED; getRetryAfter() gives the wait in seconds when the agent sends one
ValidationExceptionHTTP 400 or 422, or VALIDATION_ERROR; getFieldErrors() lists the fields
WHostExceptionAnything else: other 4xx and 5xx answers, and connection errors (TRANSPORT_ERROR)

All of them are WHost\Exceptions\WHostException subclasses and carry getHttpStatus(), getErrorCode(), getDetails() and getRawBody(). The meaning of each error code is in the error code catalogue.


Request signing

The SDK signs every request with HMAC-SHA256 and sends X-WHost-Key, X-WHost-Timestamp, X-WHost-Nonce and X-WHost-Signature. The signed text is

text
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY

where PATH is the full request path including /api/v1 and the query string. The agent rejects a timestamp more than 300 seconds away from its own clock, so keep the calling server's clock synchronised (NTP).


Webhook verification

PHP
use WHost\Http\WebhookVerifier;
use WHost\Exceptions\WHostException;

try {
    $event = WebhookVerifier::parse(
        file_get_contents('php://input'),
        $_SERVER['HTTP_X_WHOST_WEBHOOK_SIGNATURE'] ?? '',
        $_SERVER['HTTP_X_WHOST_WEBHOOK_TIMESTAMP'] ?? '',
        $endpointSecret
    );
} catch (WHostException $e) {
    http_response_code(401);   // WEBHOOK_SIGNATURE_INVALID, WEBHOOK_TIMESTAMP_OUT_OF_RANGE, ...
    exit;
}

switch ($event['event']) {
    case 'account.created':   /* ... */ break;
    case 'account.suspended': /* ... */ break;
    case 'ssl.failed':        /* ... */ break;
}

The verifier checks X-WHost-Webhook-Signature (sha256=<hex> over "{timestamp}.{raw body}") against the raw request body and rejects a delivery older than 300 seconds. The agent makes up to five delivery attempts and retries any answer other than 2xx, so drop duplicates by X-WHost-Webhook-Event-Id. Events and payloads: Webhooks.


Examples in the archive

examples/01-quickstart.phpList accounts, create one, read it back
examples/02-account-lifecycle.phpCreate → suspend → unsuspend → delete
examples/03-billing-integration-pattern.phpThe order hooks of a billing system: activate, suspend, resume, terminate, change plan
examples/04-webhook-receiver.phpA webhook endpoint with signature check and duplicate filter

Run them from the extracted folder with WHOST_API_KEY, WHOST_API_SECRET and WHOST_BASE_URL set. They create accounts on the server they point at; use a test server.


Why is there no WHMCS, Blesta or WISECP module?

The SDK is deliberately billing-agnostic: the glue to a billing system lives on the billing side. examples/03-billing-integration-pattern.php shows the hooks such a module needs. If you maintain a module that uses this SDK, tell us at [email protected].


Versions and status

The SDK has its own version number, independent of the WHost version. While it is 0.x, a minor release may change behaviour; the CHANGELOG.md in the archive says what changed. The SDK is part of the WHost beta; report problems with a support ticket from your wisecp.com client area (or [email protected] when you cannot open one).

Still Need Help?

Our support team is here around the clock for anything you can't find above.