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.

Single Sign-On (SSO)

Single Sign-On (SSO)

Log a customer straight into the panel from a trusted backend (WHMCS, billing, internal tools) without ever handling the panel password. The flow uses a single-use, server-side nonce β€” the session JWT is never placed in a URL.

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


When to use what

You want to… Endpoint
Log a customer in from WHMCS/billing POST /api/v1/auth/sso/mint
Build a dashboard of N deep-link tiles in one call POST /api/v1/auth/sso/mint-batch
(browser, automatic) redeem the link GET /sso/consume/:nonce
(browser, automatic) hand the token to the SPA POST /api/v1/auth/sso/exchange

You only ever call mint. consume and exchange run in the customer's browser β€” you just redirect to consume_url.


1. Mint a login link

Request β€” POST /api/v1/auth/sso/mint (auth = an API key; caller must be admin or reseller):

POST /api/v1/auth/sso/mint
Authorization: Bearer wsp_…
Content-Type: application/json

{
  "username": "john",
  "target_path": "/dashboard",
  "expires_in": 300,
  "reason": "WHMCS SSO"
}
Field Required Note
username βœ… the panel user to log in (must not be admin/suspended)
target_path ❌ panel-relative landing path; default /
expires_in ❌ seconds, clamped to [30, 900], default 300
reason ❌ audit-log note

Response 200 (verified live):

{
  "nonce": "41NxfMurW1WCe80eamDZU8VLbjk_Mj5E5qbgs5UpH4I",
  "consume_url": "https://161.248.4.182:3082/sso/consume/41NxfMurW1WCe80eamDZU8VLbjk_Mj5E5qbgs5UpH4I",
  "expires_in": 300,
  "target_path": "/dashboard"
}

Hand consume_url to the customer's browser (302 or a button). There is no JWT yet β€” the session is only minted when the browser hits consume_url.

Error responses (all verified live)

// 401 β€” no Authorization header
{ "success": false, "code": "UNAUTHORIZED",
  "error": "Missing authorization", "message": "Missing authorization",
  "status": 401 }

// 403 β€” authenticated but NOT with an API key (e.g. a plain admin JWT)
{ "success": false, "code": "FORBIDDEN",
  "error": "Cross-system SSO mint requires API-key authentication",
  "message": "…", "status": 403 }

// 403 β€” target is an admin account
{ "success": false, "code": "FORBIDDEN",
  "error": "Cannot mint SSO for admin accounts",
  "message": "Cannot mint SSO for admin accounts", "status": 403 }

// 400 β€” username missing
{ "success": false, "code": "VALIDATION_ERROR",
  "error": "username is required",
  "message": "username is required", "status": 400 }

Also 403 for a suspended target, or a reseller minting for a user they don't own.


2. Mint many links at once (batch)

Request β€” POST /api/v1/auth/sso/mint-batch:

{ "username": "john",
  "targets": ["/dashboard", "/files", "/databases", "/email"],
  "expires_in": 300 }

targets = panel-relative paths, max 50.

Response 200 β€” one entry per target, order preserved:

{ "items": [
    { "nonce": "a1…", "consume_url": "https://…/sso/consume/a1…",
      "expires_in": 300, "target_path": "/dashboard" },
    { "nonce": "b2…", "consume_url": "https://…/sso/consume/b2…",
      "expires_in": 300, "target_path": "/files" } ] }

3. Consume (browser, automatic)

GET /sso/consume/:nonce β€” public, no auth header. The customer's browser is redirected here. The panel atomically pops the nonce (single-use), mints the session, and:

  1. Sets wp_sso_token β€” HttpOnly, Secure, SameSite=Strict, 5-min cookie carrying the JWT (never in the URL or page body).
  2. Sets wp_sso_pending=1 β€” JS-readable flag for the SPA guard.
  3. Issues an HTTP 302 to target_path.

A bad / used / expired nonce renders an SSO error page instead.

Cross-system mints (API-key callers like WHMCS) produce a plain user token β€” not a login-as token β€” so a billing customer can never step up into admin context.


4. Exchange (browser, automatic)

POST /api/v1/auth/sso/exchange β€” public; the HttpOnly cookie IS the credential. The SPA router guard calls this when it sees wp_sso_pending=1. The panel reads wp_sso_token, wipes both SSO cookies, and returns:

{ "token": "eyJhbGciOiJIUzI1Ni…" }

No cookie β†’ 401 { "error": "No SSO cookie present" }.


End-to-end (WHMCS pattern)

WHMCS (holds API key, server-side)
  β”‚  POST /api/v1/auth/sso/mint { username, target_path }
  β–Ό
WisPanel β†’ { consume_url }                    (no JWT yet)
  β”‚  302 customer browser β†’ consume_url
  β–Ό
GET /sso/consume/:nonce  (public, single-use)
  β”‚  Set-Cookie wp_sso_token (HttpOnly 5m) + wp_sso_pending=1
  β”‚  302 β†’ target_path
  β–Ό
SPA boots, guard sees wp_sso_pending
  β”‚  POST /api/v1/auth/sso/exchange  (cookie = credential)
  β–Ό
{ token } β†’ stored β†’ customer is logged in

Notes

  • TTL is clamped server-side to [30s, 900s] (default 300s).
  • target_path is sanitised: must start with a single /, no scheme/host, no //, no CR/LF, ≀ 200 chars β€” else falls back to /.
  • Every mint / consume is written to the panel audit log.

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