Developer Portal

Updated Sep 26, 2026 Markdown

This is the single entry point for integrating with WHost. Pick the path that matches what you're trying to build:

Integrate WHost with any billing platformQuick start → PHP SDK → examples/03-billing-integration-pattern.php in the SDK archive
Receive lifecycle events instead of pollingQuick start → Webhooks guide
Call the REST API directly (any language)Quick start → API reference → HMAC signers (5 languages)
Read every endpoint's schemaapi-reference-generated.md (629 operations)
Understand the version stability promiseversioning-policy.md

Quick Start

Every WHost API request needs four HMAC headers. Below is the canonical payload format and a one-shot cURL probe — once it returns 200, you have a working integration foundation.

The API is served under the panel's own address: https://<panel-address>/api/v1/…. The agent itself listens only on 127.0.0.1:2000; the panel's web server forwards /api/v1/ requests to it.

1. Get credentials

In the admin panel:

text
Settings (gear icon in the top bar) → API Access → Create API Key

Pick the Full Access scope for a general integration — an Audit Read-Only key reaches only /api/v1/audit-logs. Copy the API Key AND the API Secret immediately — the secret is shown once. Restrict the key with Allowed IPs (one IP or CIDR per line) if the consuming server has a stable address.

2. Compute v2 HMAC signature

Canonical payload — every component newline-separated:

text
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY
  • METHOD — uppercase HTTP verb (GET, POST, …)
  • PATH — the request path exactly as sent, including the /api/v1/... prefix: not URL-decoded, and followed by ? and the query string when the request has one (/api/v1/accounts?limit=10)
  • TIMESTAMP — current Unix time, seconds (string); must be within 300 s of the server clock
  • NONCE — 16–256 characters, unique per request (the examples use 32 hex characters); a nonce already seen in the last 11 minutes is refused
  • BODY — the exact request body bytes (empty for a request without a body)

X-WHost-Signature carries the lowercase hex HMAC-SHA256 of this payload, keyed with the API secret.

Working signers in five languages — each one signs GET /api/v1/system/info:

3. First request

shell
# Using the Bash signer from the table above:
export WHOST_BASE_URL="https://panel.example.com"   # the panel address
export WHOST_API_KEY="..."
export WHOST_API_SECRET="..."
bash hmac-example.sh
# Expected: the system/info envelope — {"status":"success","data":{…}}

If the request is refused, the error_code in the response body names the check that refused it (the Bash signer runs curl with -f, which hides the body — the PHP, Node.js, Go and Ruby signers print the status and body):

  • 401 AUTH_EXPIRED — clock drift: X-WHost-Timestamp is more than 300 s away from the server clock. Run timedatectl status on the calling host.
  • 401 AUTH_FAILED — the key, the signature or the nonce was refused. The most common causes:
    • Body byte mismatch — sign exactly the bytes you send; a proxy on your side that recompresses or re-encodes the request body breaks the signature.
    • Wrong PATH — it must start with /api/v1, keep the exact case and any percent-encoding, and include the query string when there is one.
    • A nonce shorter than 16 characters, or one already used.
    • The signature sent in uppercase hex.
  • 403 IP_NOT_ALLOWED — the calling address is not in the key's Allowed IPs.
  • 403 SCOPE_DENIED — the key's scope does not cover the path.
  • 429 AUTH_RATE_LIMITED — 30 failed verifications from one address within 5 minutes block that address for an hour.

4. Make it idempotent

Every mutating call (POST / PUT / PATCH / DELETE) accepts an optional X-Idempotency-Key header — 8–64 characters of A–Z a–z 0–9 _ - (anything else → 400 IDEMPOTENCY_KEY_INVALID). Use a UUID v4 per logical operation:

shell
curl -X POST … \
     -H "X-Idempotency-Key: $(uuidgen)" \
     -d '{"username":"alice","password":"<account password>","domain":"alice.example.com","email":"[email protected]","plan_id":"starter"}' \
     "$WHOST_BASE_URL/api/v1/accounts"

(… stands for -H "Content-Type: application/json" and the four X-WHost-* headers from step 2, signed over the exact body string.)

Same key + same body within 24 h → the stored response is replayed (X-Idempotent-Replay: true). 2xx and 4xx answers are stored; a 5xx answer is not, so a retry after a 5xx runs the call again. Same key + a different method, path or body → 409 IDEMPOTENCY_KEY_REUSED (caller bug). A key belongs to the API key (or session) that sent it: another credential sending the same key runs a new request and never receives your stored answer.

Full semantics: api-reference.md → Idempotency.

5. Subscribe to lifecycle events

Polling for state changes? Don't — register a webhook:

shell
curl -X POST … \
     -d '{
       "url": "https://your-system.example.com/whost-hook",
       "events": ["account.created","account.suspended","account.terminated","ssl.failed"],
       "description": "billing platform"
     }' \
     "$WHOST_BASE_URL/api/v1/system/webhooks"

Save the returned secret — it's used to HMAC-verify inbound payloads. Full subscriber recipe (PHP SDK helper, signature formula, retry semantics, SSRF guards): webhooks.md.


Reference documents

Topic Document
Authentication, idempotency, pagination, rate limits, error catalog api-reference.md
Full per-endpoint schemas (629 operations) api-reference-generated.md
Webhook event catalog (25 events), payload, headers, signing, retry webhooks.md
Version stability promise (no breaking change inside /api/v1; at least 15 months from a /api/v2 announcement to v1 removal) versioning-policy.md
PHP SDK: download, install, what it covers sdk-php.md
Python app hosting (Django, Flask, FastAPI runtime pipeline) python-apps.md
HMAC signer examples (5 languages) Quick Start → step 2

Common integration patterns

Pattern A — billing platform provisioning hook

For WISECP, WHMCS, Blesta and custom billing systems. Plain-PHP reference: examples/03-billing-integration-pattern.php in the SDK archive.

text
order_activated  → POST   /api/v1/accounts                     (idempotency key: "order-create-$id")
order_suspended  → POST   /api/v1/accounts/{u}/suspend         (key: "order-suspend-$id")
order_resumed    → POST   /api/v1/accounts/{u}/unsuspend       (key: "order-resume-$id")
order_cancelled  → DELETE /api/v1/accounts/{u}                 (key: "order-terminate-$id")
package_changed  → POST   /api/v1/accounts/{u}/change-package  (key: "pkg-change-$changeId")

Idempotency keys mean network retries on timeout are safe — same logical order will not double-provision.

Pattern B — out-of-band billing reconciliation

Subscribe to the account. events (name each one — the events filter takes exact names, not wildcards); your billing system stays in sync when an operator manually suspends / re-activates via the panel.

text
account.suspended         → mark order "suspended" in billing
account.unsuspended       → flip back to "active"
account.terminated        → reconcile cancellation
account.package_changed   → recompute pricing
account.password_changed  → audit log entry only

Pattern C — SSL certificate events

text
ssl.failed   → page on-call + open support ticket
ssl.renewed  → silence the open ticket (if any)
ssl.issued   → record the new validity window (data.extra.valid_to)

These events come from certificate requests and uploads made through the panel or the API. Renewals that certbot runs on its own schedule send no event, and not every automatic certificate request does (see the event catalog); to watch expiry, poll GET /api/v1/accounts/{username}/ssl/{domain} (valid_to).

Pattern D — backup verification

text
backup.completed → record success + size + timing (data.extra.size_mb)
backup.failed    → page on-call (operator's responsibility, not customer's)

A backup whose archive was created but whose remote upload failed arrives as backup.completed with data.extra.remote_upload_error set.

Pattern E — capacity planning via metrics

The /api/v1/system/info endpoint returns CPU / memory / disk usage. Poll on a 5-minute cadence (within the read tier rate limit) and trend in your own observability stack; GET /api/v1/system/metrics?period=24h (24h, 7d or 30d) returns the history the agent samples every 15 minutes. WHost itself doesn't expose Prometheus metrics today.


Stability & deprecation

/api/v1/* is stable — no breaking change ships under this prefix. Backwards-incompatible work goes through /api/v2/, served in parallel with v1: at least 15 months pass between the v2 announcement and the removal of v1 (a 3-month beta plus a deprecation window of at least 12 months).

This means an integration written today against v1 keeps working unchanged until v1's announced sunset date. Read versioning-policy.md for the full deprecation timeline, header conventions, and how Sunset headers will surface when v2 is introduced.


Support

Support ticket (wisecp.com client area)Bugs, integration questions, license issues, PHP SDK issues (name the SDK version from its CHANGELOG.md, PHP SDK page) — the main channel
[email protected]The same, when you cannot open a ticket
Security disclosure[email protected] — responsible disclosure expected

When reporting an integration bug, please include:

  1. The full request (headers + body, with secrets redacted)
  2. The full response (status, headers, body)
  3. The agent version (GET /api/v1/system/update/status → data.current_version)
  4. The SDK version, if using one
Still Need Help?

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