API Documentation

Integrate WisPanel with your applications using our comprehensive REST API.

Base URL

https://your-server-ip:2083/api/v1

All API endpoints are relative to this base URL.

Authentication

Authentication

Every WisPanel API request authenticates with an API key sent as a Bearer token. There is no other public auth method β€” username / password only exists for the interactive login screen.

Panel base: https://<your-server>:3082/api/v1


Step 1 β€” Issue an API key

POST /api/v1/users/{username}/api-keys

Who may issue: an admin can issue for any user; a user can issue for themselves (reseller = for themselves). The panel does not currently let a reseller mint keys for their sub-users β€” only self or admin.

POST /api/v1/users/admin/api-keys
Authorization: Bearer <an-existing-api-key>
Content-Type: application/json

{
  "name": "WHMCS Provisioning",
  "permissions": ["domains", "read"],
  "ip_whitelist": [],
  "expires_in": 0
}
Field Type Required Note
name string βœ… label shown in the key list
permissions string[] ❌ scope (see below). Default ["read"]
ip_whitelist string[] ❌ IPs/CIDRs; empty = any source
expires_in int ❌ days; 0 = never

Response 201 (verified live β€” the secret is returned once):

{
  "success": true,
  "message": "API key created. Save the secret key now β€” it won't be shown again!",
  "secret_key": "wsp_56be4cb59e73761bed78686b6c0b37f3bebd929439c20d7d",
  "api_key": "wsp_56be4cb59e73761bed78686b6c0b37f3bebd929439c20d7d",
  "key": {
    "id": "e24ff6f1754c2d9a",
    "name": "WHMCS Provisioning",
    "key_prefix": "wsp_56be4cb5",
    "permissions": ["domains", "read"],
    "ip_whitelist": [],
    "last_used": "0001-01-01T00:00:00Z",
    "created_at": "2026-05-18T17:39:25+07:00",
    "expires_at": "0001-01-01T00:00:00Z",
    "enabled": true
  }
}

Store secret_key now β€” later reads only return key_prefix.


Step 2 β€” Use the key

GET /api/v1/auth/me
Authorization: Bearer wsp_56be4cb59e73761bed78686b6c0b37f3bebd929439c20d7d

Response 200 (verified live):

{ "username": "admin", "email": "[email protected]", "role": "admin",
  "status": "active", "creator": "", "package": "default",
  "max_domains": -1, "max_databases": -1,
  "max_email_accounts": -1, "max_ftp_accounts": -1 }

Permission scope (ENFORCED)

A key's permissions are an enforced narrowing scope on top of the owning user's role. A key can never exceed the user's role (admin endpoints still require an admin user); permissions only restrict further. Vocabulary is flat (not resource.read/resource.write):

Permission Grants
all full access (still bounded by the user's role)
read read-only (GET/HEAD) on any resource
domains full access to /domains, /subdomains
dns full access to /dns
ssl full access to /ssl
database full access to /databases
email full access to /email
files full access to /files
ftp full access to /ftp
backup full access to /backups, /restore, /backup-schedules

Resources without a granular token (users, system, security, …) require all for writes; read allows GET on them. Legacy resource.read / resource.write strings are accepted and normalised to the base resource (so old keys keep working).

Out-of-scope request β†’ 403 (verified live):

{ "success": false, "code": "FORBIDDEN",
  "error": "API key is not scoped for 'databases' on this request",
  "message": "API key is not scoped for 'databases' on this request",
  "status": 403 }

Example: a key with ["domains"] can call /domains but GET /databases β†’ 403; a key with ["read"] can GET anything but any write β†’ 403.


Disabling API access per account (can_use_api)

API access is a per-account capability (default enabled, inherited from the package). An admin can turn it off via the user's features or the package:

PUT /api/v1/users/{username}/features
{ "api": false, "php": true, "ssl": true, ... }

While disabled, both creating and using keys for that account are rejected (verified live):

{ "success": false, "code": "FORBIDDEN",
  "error": "API access is disabled for this account",
  "message": "API access is disabled for this account", "status": 403 }

Re-enable with "api": true. Package field: can_use_api (api in package data).

Reseller delegation β€” can_grant_api

Whether a reseller may grant API access to the users it owns is a separate, admin-controlled lever: can_grant_api on the reseller (reseller-package field api; default enabled).

An admin sets it directly:

PUT /api/v1/resellers/{username}
{ "can_grant_api": false }

While can_grant_api is false, the reseller turning api on for one of its users is rejected (verified live):

{ "success": false, "code": "FORBIDDEN",
  "error": "Permission denied: cannot grant API (not allowed by your reseller package)",
  "status": 403 }

Turning api off is always allowed β€” the gate only blocks granting. admin is unaffected by this lever.


Manage / interactive login

GET /users/{username}/api-keys (secret redacted), PUT /users/{username}/api-keys/{id}, POST /users/{username}/api-keys/{id}/toggle, DELETE /users/{username}/api-keys/{id} β†’ { "success": true, "message": "API key deleted" }.

POST /api/v1/auth/login (login screen only). Verified live: 400 {"code":"VALIDATION_ERROR","error":"Username and password are required"}; 401 {"code":"UNAUTHORIZED","error":"Invalid credentials"}.


Logging in customers from billing β†’ Single Sign-On (SSO). Error envelope catalogue β†’ Error Handling.

Rate Limiting

API requests are limited to 60 requests per minute per API token.

  • X-RateLimit-Limit: Maximum requests per minute
  • X-RateLimit-Remaining: Remaining requests
  • X-RateLimit-Reset: Unix timestamp when limit resets