PWF Auth PWF Auth
Features How it works Pricing
Getting started API reference SDKs & tools
FAQ Blog Changelog Contact
English English العربية العربية Deutsch Deutsch Nederlands Nederlands Português Português Русский Русский Türkçe Türkçe 简体中文 简体中文
Customer Portal Log in Sign up free ··
Developers · API

API Reference

Everything is a plain JSON request over HTTPS. Validate licenses, run sessions, push updates, and mint keys from any language that speaks HTTP.

REST · JSON 0 endpoints TLS 1.3 only Updated August 20, 2026
/
Reference
Introduction Authentication Encrypted envelope Responses & errors Quickstart Licenses & sessions Trials & HWID User accounts App content & remote OTA updates Webhooks Admin API SDKs

No matching endpoints

Nothing here matches

The 30-second version

Send your app's X-App-Secret header and POST JSON to /api/auth/* to validate keys and run sessions. Generate keys from /api/admin/* with a Bearer token. Responses are always { "success": true|false, … }.

Introduction

The PWF Auth API is organised around REST. All requests and responses are JSON over HTTPS. There are three families of endpoints:

  • Client API (/api/auth/*, /api/update/*) — called from your shipped application using your app's secret.
  • Admin API (/api/admin/*) — key & app management, authenticated with a Bearer token.
  • Customer / reseller API (/api/customer/*, /api/reseller/*) — self-service portals.
Base URL https://pwfauth.com

All paths below are relative to that base URL. Every endpoint requires HTTPS; plain-HTTP requests are redirected.

Prefer machines? The whole surface is described in openapi.json (OpenAPI 3.1) — import it into Postman, Insomnia, or a code generator.

API and platform changes are announced in the changelog — breaking changes are always flagged there first.

Authentication

Client endpoints — App Secret

Send your app's secret in the X-App-Secret header. You'll find it in Dashboard → your app. The secret identifies which application a request belongs to.

bash
curl https://pwfauth.com/api/auth/check-key.php \
  -H "X-App-Secret: 9f02532d9c60…  # 64-char hex, from your dashboard" \
  -H "Content-Type: application/json" \
  -d '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}'

Admin endpoints — Bearer token

Authenticate to /api/admin/login.php to receive a JWT, then send it as Authorization: Bearer <token> on every admin call.

bash
TOKEN=$(curl -s https://pwfauth.com/api/admin/login.php \
  -d '{"username":"admin","password":"…"}' | jq -r .token)

curl https://pwfauth.com/api/admin/keys.php \
  -H "Authorization: Bearer $TOKEN"

The encrypted envelope

SDK-grade endpoints exchange an AES-256-CBC + HMAC-SHA256 envelope instead of plain JSON. The wire format is {"p": base64(IV || ciphertext), "t": unix_timestamp, "s": hmac_sha256_hex(p + t)} — encryption and MAC keys are both derived from your app secret, so no extra key exchange is needed.

json · envelope
{
  "p": "aXYxNmJ5dGVzY2lwaGVydGV4dA…",   // base64( IV || AES-256-CBC ciphertext )
  "t": 1785671000,                       // unix timestamp (±300s accepted)
  "s": "b7fd09…"                        // hmac_sha256_hex( p + t, mac_key )
}

Endpoints marked App secret + encrypted envelope require it for the request and reply encrypted. check-key.php auto-detects: send plain JSON and you get plain JSON back (that is what makes the curl examples on this page work); send an envelope and the reply is encrypted.

Envelopes older than ±300 seconds are rejected, so keep the client clock reasonably accurate. You never need to hand-roll this: the open-source example clients (VB.NET, C#, Python) ship a ready CryptoEnvelope implementation.

How the envelope is built

Both directions use the same recipe. Two keys are derived from your app secret, so every app gets distinct encryption and signing keys:

envelope · pseudocode
enc_key = SHA256("enc:" + app_secret)          # 32 bytes
mac_key = SHA256("mac:" + app_secret)          # 32 bytes

# client → server (and server → client — same recipe)
iv = random_bytes(16)
ct = AES-256-CBC(enc_key, iv, json_body)       # PKCS#7 padding
p  = base64(iv || ct)
t  = unix_time()                               # ±300 s accepted
s  = hex(HMAC-SHA256(p + str(t), mac_key))
send { "p": p, "t": t, "s": s }

# receiving: verify s FIRST (constant-time), check |now - t| ≤ 300,
# then base64-decode p, split off the 16-byte IV, decrypt the rest.

Which endpoints require it

Each endpoint card carries a colour-coded auth pill; this table is the same information at a glance:

Auth modelEndpoints
Public app/changelog · app/pricing · admin/login
App secret · plain or envelope auth/check-key
App Secret auth/trial · auth/request-hwid-reset · auth/account-register · auth/account-login · auth/change-password · update/validate · update/download
App secret + encrypted envelope auth/login · auth/heartbeat · auth/logout · app/text · app/slides · app/info · app/social-click · update/check
Bearer admin/*
Fulfillment secret app/fulfill

Responses & errors

Every response carries a boolean success. Errors add a human message and a stable, machine-readable error_code:

json · error
{
  "success": false,
  "message": "This license key has been banned.",
  "error_code": "BANNED"
}
Common error codes
error_codeMeaning
MISSING_FIELDSA required body field is absent.
INVALID_KEYThe license key does not exist for this app.
INVALID_CREDENTIALSUsername/password pair rejected (user accounts + admin login).
HWID_MISMATCHThe key is bound to another device.
EXPIREDThe license has passed its expiry date.
BANNED / PAUSEDThe key was banned or paused by the owner.
MAINTENANCEThe app is in maintenance mode.
SESSION_EXPIRED / SESSION_MISMATCHThe heartbeat session is invalid.
TOO_MANY_ATTEMPTSLogin brute-force lockout — the account is locked temporarily; retry_after in the response says for how many seconds.
TRIAL_DISABLED / TRIAL_USED / TRIAL_LIMITTrial creation refused — disabled for this app, already used on this device, or the per-IP cap was reached.

HTTP status codes

error_codeMeaning
200Success — the JSON body carries the result.
400Validation failed — missing or malformed fields (see error_code).
401Missing or invalid X-App-Secret / Bearer token.
403Authenticated but not allowed (wrong app, revoked token…).
404Resource not found.
405Wrong HTTP method for this endpoint.
429Rate limited — wait for the Retry-After header before retrying.
500Server error — generic message on purpose; retry later.

Rate limits & fair use

A general per-IP request limiter protects every endpoint — flood it and you get 429 with a Retry-After header (authenticated admins get a higher ceiling). On top of that, a few targeted guards:

  • Admin login is brute-force protected — repeated failures lock the account temporarily.
  • Trials are capped per device (HWID) and per IP; exceeding them returns TRIAL_LIMIT.
  • Secret regeneration is rate-limited per app to stop accidental rotation loops.
  • Keep heartbeats at the interval the login response gives you (default 30s) — hammering faster adds nothing.

Quickstart

The minimal integration: validate a key when your app launches, then heartbeat to keep the session alive.

bash
# Validate a key from the terminal (plain JSON works here)
curl -X POST https://pwfauth.com/api/auth/check-key.php \
  -H "X-App-Secret: $APP_SECRET" \
  -d '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}'
# → { "success": true, "valid": true, "key": { "status": "active", … } }

Note: the full session flow (login → heartbeat → logout) uses the encrypted envelope, so it is not hand-curl-able. Wire it in minutes with the in-panel Quick Start (generates snippets in 9 languages with your real credentials) or the open-source example clients.

The same flow with an official SDK

Login, heartbeat and the server-side kill switch in a few lines — the SDK handles the encrypted envelope for you:

// dotnet add package PWFAuth
using PWFAuth;

var client = new PwfClient(APP_SECRET);
client.SessionEnded += (s, e) =>              // ban / pause / expiry / revoke / offline
{
    Console.WriteLine($"{e.ErrorCode}: {e.Message}");
    Environment.Exit(0);
};

var login = await client.LoginAsync("XXXXX-XXXXX-XXXXX-XXXXX");
if (!login.Success) { Console.WriteLine(login.Message); return; }

client.StartHeartbeat();   // keeps the session alive AND enforces the kill switch
' dotnet add package PWFAuth
Dim client As New PwfClient(APP_SECRET)
AddHandler client.SessionEnded, Sub(s, e)     ' ban / pause / expiry / revoke / offline
                                    MessageBox.Show(e.Message)
                                    Application.Exit()
                                End Sub

Dim login = Await client.LoginAsync("XXXXX-XXXXX-XXXXX-XXXXX")
If login.Success Then client.StartHeartbeat()
# pip install pwfauth
import sys
from pwfauth import PwfClient

client = PwfClient(APP_SECRET)
client.on_session_ended = lambda code, msg: sys.exit(msg)   # kill switch

login = client.login("XXXXX-XXXXX-XXXXX-XXXXX")
if not login.success:
    sys.exit(login.message)

client.start_heartbeat()   # keeps the session alive AND enforces the kill switch
// npm install pwfauth
import { PwfClient } from 'pwfauth';

const client = new PwfClient(process.env.PWFAUTH_SECRET);
client.on('sessionEnded', ({ errorCode, message }) => {     // kill switch
    console.error(`${errorCode}: ${message}`);
    process.exit(1);
});

const login = await client.login('XXXXX-XXXXX-XXXXX-XXXXX');
if (!login.success) { console.error(login.message); process.exit(1); }

client.startHeartbeat();   // keeps the session alive AND enforces the kill switch

Licenses & sessions

POST/api/auth/login.php App secret + encrypted envelope#

Activate a key on first use, bind it to the device's HWID, and open a session. Re-validates and starts a new session on subsequent logins. Enforces max_devices by retiring the oldest session.

Body fieldReq.Description
license_keyrequiredThe license key to activate / validate.
hwidrequiredStable hardware fingerprint of the device.
Response
json
{
  "success": true,
  "session_id": "8f2c…",
  "user": {
    "license_key": "XXXXX-XXXXX-XXXXX-XXXXX",
    "key_type": "days", "duration": 30,
    "expires_at": "2026-09-01T12:00:00Z",
    "days_remaining": 30, "status": "active"
  },
  "features": { "pro_tier": true },
  "heartbeat_interval": 30
}
POST/api/auth/heartbeat.php App secret + encrypted envelope#

Keep a session marked active. Call it every heartbeat_interval seconds (returned by login). Stop calling it and the session is reaped server-side.

Body fieldReq.Description
session_idrequiredSession returned by login.
license_keyoptionalIf sent, the session must belong to this key.
Response
json
{ "success": true, "message": "Heartbeat received" }

// the moment the key is banned / paused / expired / reset, the SAME call answers:
{ "success": false, "error_code": "BANNED",
  "message": "This license key has been banned." }   // session is dropped — log the user out
POST/api/auth/logout.php App secret + encrypted envelope#

Close a session immediately (e.g. on app exit). Body: session_id required, license_key optional.

POST/api/auth/check-key.php App secret · plain or envelope#

Lightweight status lookup — no session created, no HWID binding. Useful for a "is this key still valid?" check.

Body fieldReq.Description
license_keyrequiredThe key to inspect.
curl -X POST https://pwfauth.com/api/auth/check-key.php \
  -H "X-App-Secret: $APP_SECRET" \
  -d '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}'
var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-App-Secret", APP_SECRET);
var res = await http.PostAsync("https://pwfauth.com/api/auth/check-key.php",
    new StringContent("{\"license_key\":\"XXXXX-XXXXX-XXXXX-XXXXX\"}"));
import urllib.request, json
req = urllib.request.Request("https://pwfauth.com/api/auth/check-key.php",
    data=json.dumps({"license_key": "XXXXX-XXXXX-XXXXX-XXXXX"}).encode(),
    headers={"X-App-Secret": APP_SECRET})
print(json.load(urllib.request.urlopen(req)))
const res = await fetch('https://pwfauth.com/api/auth/check-key.php', {
  method: 'POST',
  headers: { 'X-App-Secret': process.env.APP_SECRET },
  body: JSON.stringify({ license_key: 'XXXXX-XXXXX-XXXXX-XXXXX' })
});
console.log(await res.json());
$ch = curl_init('https://pwfauth.com/api/auth/check-key.php');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['X-App-Secret: ' . $APP_SECRET],
    CURLOPT_POSTFIELDS => '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}']);
$res = json_decode(curl_exec($ch), true);
Response
json
{
  "success": true, "valid": true,
  "key": {
    "status": "active", "key_type": "days", "duration": 30,
    "activated_at": "2026-08-02T12:00:00Z",
    "expires_at": "2026-09-01T12:00:00Z",
    "days_remaining": 30, "max_devices": 1
  }
}
POST/api/update/validate.php App Secret#

Lightweight license validation without HWID binding or a session — ideal for a pre-flight check before the full login. Plain JSON in, plain JSON out.

Body fieldReq.Description
license_keyrequiredThe key to inspect.
POST/api/auth/request-hwid-reset.php App Secret#

Submit a HWID-reset request from your in-app dialog when a user changes machines. The app owner approves it from the dashboard.

Body fieldReq.Description
license_key or usernamerequiredIdentifies whose binding to reset.
reasonoptionalNote shown to the owner.

Free Trials

POST/api/auth/trial.php App Secret#

Issue a time-limited trial key bound to the requesting device. One trial per HWID per app. Trial duration is configured by the app owner.

Body fieldReq.Description
hwidrequiredDevice fingerprint requesting the trial.
Response
json
{
  "success": true, "message": "Trial activated! You have 3 days.",
  "trial_key": "TRIAL-1A2B3-C4D5E-F6A7B",
  "session_id": "8f2c…",
  "expires_at": "2026-08-23T12:00:00Z", "days_remaining": 3,
  "user": { "key_type": "days", "status": "active", … },
  "app":  { "name": "…", "version": "…" }
}

User accounts

Prefer username/password logins over raw keys? PWF Auth ships a full account model with bcrypt hashing and HWID binding.

POST/api/auth/account-register.php App Secret#

Create an end-user account. Body: username, password required, email optional.

Body fieldReq.Description
usernamerequiredDesired username (unique per app).
passwordrequiredPassword (stored hashed server-side).
emailoptionalContact e-mail for the account.
POST/api/auth/account-login.php App Secret#

Authenticate and bind a device. Body: username, password, hwid required. Returns the same session shape as login.

Body fieldReq.Description
usernamerequiredAccount username.
passwordrequiredAccount password.
hwidrequiredDevice fingerprint to bind the session to.
POST/api/auth/change-password.php App Secret#

Rotate a user's password. Body: username, current_password, new_password required.

Body fieldReq.Description
usernamerequiredAccount username.
current_passwordrequiredCurrent password (verified before the change).
new_passwordrequiredNew password.

App Content & Remote Control

These endpoints let your shipped app pull content you edit live in the panel — texts, slides, pricing, changelog — without releasing an update.

GET/api/app/text.php App secret + encrypted envelope#

Fetch remote text(s). With ?name=… returns one entry; without it returns every text of the app. Per-key overrides win over app defaults. The license key rides in Authorization: Bearer (or ?key=). Response is enveloped.

Body fieldReq.Description
?nameoptionalText key to fetch (omit for all).
POST/api/app/slides.php App secret + encrypted envelope#

Returns the app's active announcement slides. Send the envelope body {"action":"get_slides"}; the reply is encrypted.

GET/api/app/info.php App secret + encrypted envelope#

App metadata + social links (name, branding, URLs). Enveloped response.

POST/api/app/social-click.php App secret + encrypted envelope#

Increment the click counter of a social link (for the panel's engagement stats).

Body fieldReq.Description
link_idrequiredNumeric id of the social link.
GET/api/app/changelog.php Public#

Public, no-auth changelog feed for one app — render a "What's new" widget anywhere. Published entries only.

Body fieldReq.Description
?app_idrequiredThe app whose changelog to read.
?limitoptional1–50, default 20.
?sinceoptionalISO date — only entries published on/after.
?categoryoptionalFilter: new / improved / fixed / removed / security / deprecated.
GET/api/app/pricing.php Public#

Public pricing surface: subscription levels and legacy plans for an app; POST creates a purchase order; ?action=check reads a user's active subscription.

Body fieldReq.Description
?app_idrequiredThe app whose pricing to read.
?typeoptionallevels or plans (omit for both).
POST/api/app/fulfill.php Fulfillment secret#

Server-to-server order fulfillment for automated payment gateways — a verified IPN completes the order and triggers key generation + delivery. Authenticated by the fulfillment shared secret from your panel settings, never by a user session.

Body fieldReq.Description
order_idrequiredThe order to fulfill (ORD-…).
secretrequiredYour order_fulfill_secret.
payment_refoptionalOptional gateway payment reference.

OTA updates

POST/api/update/check.php App secret + encrypted envelope#

Ask whether a newer build is available for the caller — honouring channel, OS/arch filters, minimum-version gates, and staged rollouts (the same caller is stably bucketed).

Body fieldReq.Description
vrequiredCaller's current version (semver).
channeloptionalstable (default), beta, or alpha.
os / archoptionalwindows·macos·linux / x64·x86·arm64.
hwid / license_keyoptionalUsed for stable rollout bucketing.
Response
json
{
  "success": true, "update_available": true,
  "update": {
    "version": "1.4.0", "channel": "stable",
    "is_mandatory": false, "file_size": 5242880,
    "sha256": "…", "changelog": "…",
    "download_url": "api/update/download.php?v=1.4.0"
  }
}
GET/api/update/download.php?v=<version> App Secret#

Stream the binary for a given version. Use the download_url returned by check.php, and verify the sha256 after download.

Webhooks

Turn events into HTTP calls to your server: key activations, bans, expiries, orders, new updates and more. Configure destinations per app (or for all apps) in the panel under Application → Webhooks, pick the events you care about, and PWF Auth POSTs a signed JSON body to your URL. Delivery is queue-based — expect it within a minute or two of the event, not in the same instant.

Delivery payload

json · POST body
{
  "event": "key.activated",
  "timestamp": "2026-08-20T14:07:02Z",
  "data": {
    "app_id": "01af6643-…",
    "license_key": "XXXXX-XXXXX-XXXXX-XXXXX",
    "hwid": "A1B2C3…"
  }
}

Delivery headers

HeaderDescription
X-Webhook-IdUnique delivery id (whd_…). Stays the same across retries of one event — use it to deduplicate.
X-Webhook-TimestampUnix timestamp of the delivery attempt.
X-Webhook-Attempt1-based attempt counter.
X-Webhook-SignatureLegacy signature: hex(HMAC-SHA256(body, secret)).
X-Webhook-Signature-V2Preferred signature: t=<ts>,v1=hex(HMAC-SHA256(ts + "." + body, secret)) — the timestamp inside the MAC makes replays detectable.

Verifying the signature

Always verify before trusting a delivery: compute the V2 MAC over timestamp + "." + raw body with your endpoint secret, compare in constant time, and reject stale timestamps (±5 minutes is plenty). Answer 2xx fast — heavy work belongs in your own queue.

// X-Webhook-Signature-V2: t=<ts>,v1=<hex>
[$t, $v1] = explode(',', $_SERVER['HTTP_X_WEBHOOK_SIGNATURE_V2'] ?? ',');
$t = substr($t, 2);  $v1 = substr($v1, 3);
$raw = file_get_contents('php://input');

$ok = hash_equals(hash_hmac('sha256', $t . '.' . $raw, $secret), $v1)
   && abs(time() - (int)$t) < 300;
if (!$ok) { http_response_code(400); exit; }
http_response_code(200);   // answer fast — queue heavy work
// X-Webhook-Signature-V2: t=<ts>,v1=<hex>
const [t, v1] = req.headers['x-webhook-signature-v2']
    .split(',').map(kv => kv.split('=')[1]);
const mac = crypto.createHmac('sha256', secret)
    .update(`${t}.${rawBody}`).digest('hex');

const ok = crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1))
        && Math.abs(Date.now() / 1000 - Number(t)) < 300;
res.sendStatus(ok ? 200 : 400);   // answer fast — queue heavy work
# X-Webhook-Signature-V2: t=<ts>,v1=<hex>
import hmac, time
t, v1 = (kv.split("=", 1)[1]
         for kv in request.headers["X-Webhook-Signature-V2"].split(","))
mac = hmac.new(secret, f"{t}.{raw_body}".encode(), "sha256").hexdigest()

ok = hmac.compare_digest(mac, v1) and abs(time.time() - int(t)) < 300
return ("", 200) if ok else ("", 400)   # answer fast — queue heavy work

Retries & failure policy

  • A delivery counts as successful on any 2xx/3xx answered within 5 seconds.
  • Failures retry up to 5 attempts with widening gaps: 1 m → 5 m → 30 m → 2 h → 6 h.
  • X-Webhook-Id is minted once per event and reused on every retry — idempotency is one seen? lookup away.
  • A destination that fails 20 times in a row is paused automatically; re-enable it from the panel (a test event button is right there).

Events

Subscribe to specific events or leave the filter empty to receive them all. event in the body tells you which one fired:

EventFires when
key.createdKeys are generated (panel or admin API).
key.activatedA key's first successful login binds it to a device.
key.banned / key.unbannedA key is banned / unbanned.
key.deletedA key is deleted.
key.rotatedA key string is rotated (the old string stops working).
key.suspected_leakLeak detection flags a key as suspected shared/leaked.
key_expiringA key approaches expiry (scheduled sweep).
key_expiredA key expires (scheduled sweep).
trial.createdA trial key is issued to a new device.
hwid_reset.requestedA client requests a device (HWID) reset.
new_orderA purchase order is created.
order.approvedAn order is approved and fulfilled.
subscription.*Subscription lifecycle: created / renewed / cancelled / renewal order created.
update.publishedA new application version is published.
changelog.publishedA changelog entry goes live.
app.revokedAn application is revoked / disabled.
suspicious_activityThe suspicious-activity heuristic fires.
testManual test delivery from the panel.

Admin API

Server-side endpoints for managing apps and keys. All require an Authorization: Bearer token from the login endpoint below.

Prefer not to store your password in a script? Mint a Personal Access Token in Panel → Profile → API Tokens and use it as the Bearer value (pwf_…). It never expires until you revoke it, works on every /api/admin/* endpoint, and is locked out of credential management (password, e-mail, 2FA, minting more tokens) by design.

POST/api/admin/login.php Public#

Exchange credentials for a JWT. If 2FA is enabled, returns requires_2fa + a step1_token to complete via /api/admin/totp.php.

Body fieldReq.Description
usernamerequiredAdmin username.
passwordrequiredAdmin password.
POST/api/admin/keys.php Bearer#

Generate license keys in bulk. The same endpoint also supports GET (list), PUT (edit), DELETE, and action queries such as ?action=ban|pause|extend|reset-hwid and ?action=export.

bash
curl -X POST https://pwfauth.com/api/admin/keys.php \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"app_id":"01af6643-cf3a-4280-b9ce-cb5fa8ae987c",
       "count":100,"key_type":"days","duration_value":30}'

The admin surface also covers apps, sessions, updates, feature-flags, webhooks, analytics, audit, resellers, and more — each under /api/admin/ with the same Bearer auth. Sign in to the dashboard to explore them.

SDKs

The REST API works from anything that can send an HTTP request. Official packages are live on the public registries — PWFAuth on NuGet (C# / VB.NET), pwfauth on npm (JavaScript / Node.js) and on PyPI (Python) — plus a VS Code extension and first-class PHP examples. Every SDK wires the encrypted envelope, the heartbeat and the kill switch for you.

  • Open-source example clients (VB.NET · C# · Python) — full login/heartbeat/envelope flows, ready to copy
  • In-panel Quick Start — 5 guided steps that generate working snippets in 9 languages with your real credentials
  • OpenAPI 3.1 spec (openapi.json) — one-click import into Postman / Insomnia, or feed it to your code generator

Ready to integrate?

Create a free account, register an app, and copy your X-App-Secret from the dashboard.

Get your API key — free
PWF Auth PWF Auth

The license server & user authentication backend developers actually want to use. Self-serve, hosted, secure.

Product

  • Features
  • Pricing
  • How it works
  • FAQ
  • KeyAuth alternative
  • Cryptolens alternative
  • Keygen alternative
  • License keys for .NET

Developers

  • Getting started
  • API reference
  • SDKs & tools
  • API status (JSON)
  • Changelog
  • Dashboard
  • Get an API key

Company

  • About us
  • Contact
  • System status
  • Report a security issue

Legal

  • Privacy policy
  • Terms of service
  • GDPR
  • Cookies
© 2026 PWF Auth. All rights reserved.
All systems operational