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.

Webhooks, Idempotency & OpenAPI

Webhooks, Idempotency & OpenAPI

The canonical reference for cross-cutting behaviours. Other docs point here. Auth: Authorization: Bearer wsp_….

Webhook endpoints have no granular permission token β€” an API key needs the all scope (a scoped key β†’ 403 "API key is not scoped for 'webhooks'"). See Authentication.

Webhook responses use the lean envelope { "success": …, "error": …, "code": … } β€” no message/status fields (differs from most panel endpoints; see Error Handling).


Webhooks β€” /api/v1/webhooks

List β€” GET /api/v1/webhooks/

Response 200 (verified live β€” paginated; non-admin sees only own):

{ "success": true, "data": [],
  "meta": { "total": 0, "per_page": 50, "current_page": 1, "last_page": 1 } }

Create β€” POST /api/v1/webhooks/

The secret is server-generated (not supplied by you) and shown only in the create response.

Request:

{ "url": "https://example.com/hook",
  "events": ["user.created", "domain.*", "backup.completed"],
  "enabled": true }
Field Required Note
url βœ… scheme must be http or https
events βœ… β‰₯1 pattern; * and prefix.* wildcards allowed
enabled ❌ default true

Response 201 (verified live):

{ "success": true,
  "data": {
    "id": "76f9d4cca73ac9c9ee05649421570c01",
    "owner": "admin",
    "url": "https://example.com/hook",
    "events": ["user.created", "domain.*", "backup.completed"],
    "secret": "96bc7a66c6e9e57bee747a20c072f3f1c85f1fc740b237e7fc6e679a486b59e0",
    "enabled": true,
    "created_at": "2026-05-19T07:11:15+07:00",
    "last_fired_at": "0001-01-01T00:00:00Z",
    "failure_count": 0 } }

Validation 400 (verified live): URL is required / At least one event pattern is required / scheme must be http or https:

{ "success": false, "error": "URL is required", "code": "VALIDATION_ERROR" }

Get / update / delete

GET /api/v1/webhooks/:id β€” same shape, but secret is redacted (verified live): "secret": "<redacted>".

PATCH /api/v1/webhooks/:id β€” send only changed keys (url, events, enabled); returns the updated (redacted) hook.

DELETE /api/v1/webhooks/:id β†’ 200 (verified live):

{ "success": true, "message": "Webhook deleted" }

Missing id β†’ 404 { "success": false, "error": "Webhook not found", "code": "NOT_FOUND" }. POST/PATCH/DELETE also require step-up re-auth for session callers (API-key callers are exempt).

Delivery signature

Each delivery is POSTed to your url with header X-WisPanel-Signature: sha256=<hmac> where the HMAC is HMAC-SHA256(secret, raw_body). Verify before trusting:

$calc = hash_hmac('sha256', $rawBody, $secret);
hash_equals($calc, substr($_SERVER['HTTP_X_WISPANEL_SIGNATURE'], 7));

Idempotency-Key

Send Idempotency-Key: <uuid> on POST/PUT/PATCH/DELETE. Verified live behaviour within the 24h window:

Replay Result (verified live)
Same key + same body the original response is replayed (same id, HTTP 201) with header Idempotency-Replayed: true β€” no duplicate created
Same key + different body 409 { "success": false, "code": "IDEMPOTENCY_KEY_CONFLICT", "error": "Idempotency-Key reused with a different request" }
After 24h treated as a fresh request

Only 2xx are cached; failures always re-run.


OpenAPI

GET /openapi.yaml and GET /api/v1/openapi.yaml (no auth) β†’ 200, Content-Type: application/yaml, ~13.6 KB, beginning openapi: 3.0.3 (title "WisPanel API"). Use it to generate clients.


Rate limits

/api/v1/auth/* responses carry (verified live, exact casing):

X-Ratelimit-Limit: 20
X-Ratelimit-Window: 60
X-Ratelimit-Remaining: 19
X-Ratelimit-Reset: 60

X-Ratelimit-Reset is seconds until the window resets (not a unix timestamp). On 429 a Retry-After header is also sent.


Auth & key scope β†’ Authentication. Error envelopes β†’ 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