PWF Auth PWF Auth
Funktionen So funktioniert's Preise
Getting started API-Referenz SDKs & tools
FAQ Blog Changelog Kontakt
English English العربية العربية Deutsch Deutsch Nederlands Nederlands Português Português Русский Русский Türkçe Türkçe 简体中文 简体中文
Kundenportal Anmelden Kostenlos registrieren ··
Entwickler · API

API-Referenz

Alles ist eine einfache JSON-Anfrage über HTTPS. Validiere Lizenzen, führe Sitzungen, verteile Updates und erzeuge Schlüssel aus jeder Sprache, die HTTP spricht.

REST · JSON 0 endpoints Nur TLS 1.3 Aktualisiert August 20, 2026
/
Referenz
Einführung Authentifizierung Verschlüsselter Umschlag Antworten & Fehler Schnellstart Lizenzen & Sitzungen Test­versionen & HWID Benutzerkonten App-Inhalte & Remote OTA-Updates Webhooks Admin-API SDKs

No matching endpoints

Nothing here matches

Die 30-Sekunden-Version

Sende den X-App-Secret-Header deiner App und POST-e JSON an /api/auth/*, um Schlüssel zu validieren und Sitzungen zu führen. Erzeuge Schlüssel über /api/admin/* mit einem Bearer-Token. Antworten sind stets { "success": true|false, … }.

Einführung

Die PWF-Auth-API ist nach REST aufgebaut. Alle Anfragen und Antworten sind JSON über HTTPS. Es gibt drei Endpunkt-Familien:

  • Client-API (/api/auth/*, /api/update/*) — aus deiner ausgelieferten Anwendung mit dem App-Secret aufgerufen.
  • Admin-API (/api/admin/*) — Schlüssel- & App-Verwaltung, per Bearer-Token authentifiziert.
  • Kunden-/Reseller-API (/api/customer/*, /api/reseller/*) — Self-Service-Portale.
Basis-URL https://pwfauth.com

Alle Pfade unten sind relativ zu dieser Basis-URL. Jeder Endpunkt erfordert HTTPS; reine HTTP-Anfragen werden umgeleitet.

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.

Authentifizierung

Client-Endpunkte — App-Secret

Sende das App-Secret im X-App-Secret-Header. Du findest es im Dashboard → deine App. Das Secret bestimmt, zu welcher Anwendung eine Anfrage gehört.

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-Endpunkte — Bearer-Token

Authentifiziere dich an /api/admin/login.php, um ein JWT zu erhalten, und sende es bei jedem Admin-Aufruf als Authorization: Bearer <token>.

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"

Der verschlüsselte Umschlag

SDK-Endpunkte tauschen statt Klartext-JSON einen AES-256-CBC + HMAC-SHA256-Umschlag aus. Das Wire-Format ist {"p": base64(IV || ciphertext), "t": unix_timestamp, "s": hmac_sha256_hex(p + t)} — Verschlüsselungs- und MAC-Schlüssel werden beide aus deinem App-Secret abgeleitet, ein zusätzlicher Schlüsselaustausch ist nicht nötig.

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 )
}

Endpunkte mit App-Secret + verschlüsselter Umschlag verlangen ihn in der Anfrage und antworten verschlüsselt. check-key.php erkennt automatisch: Sende einfaches JSON und du bekommst einfaches JSON zurück (deshalb funktionieren die curl-Beispiele auf dieser Seite); sende einen Umschlag und die Antwort kommt verschlüsselt.

Umschläge, die älter als ±300 Sekunden sind, werden abgelehnt — halte die Client-Uhr also einigermaßen genau. Du musst das nie von Hand bauen: Die Open-Source-Beispielclients (VB.NET, C#, Python) bringen eine fertige CryptoEnvelope-Implementierung mit.

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
Öffentlich app/changelog · app/pricing · admin/login
App-Secret · einfach oder Umschlag 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 + verschlüsselter Umschlag auth/login · auth/heartbeat · auth/logout · app/text · app/slides · app/info · app/social-click · update/check
Bearer admin/*
Fulfillment-Secret app/fulfill

Antworten & Fehler

Jede Antwort enthält ein boolesches success. Fehler ergänzen eine menschliche message und einen stabilen, maschinenlesbaren error_code:

json · error
{
  "success": false,
  "message": "This license key has been banned.",
  "error_code": "BANNED"
}
Häufige Fehlercodes
error_codeBedeutung
MISSING_FIELDSEin erforderliches Body-Feld fehlt.
INVALID_KEYDer Lizenzschlüssel existiert für diese App nicht.
INVALID_CREDENTIALSBenutzername/Passwort abgelehnt (Benutzerkonten + Admin-Login).
HWID_MISMATCHDer Schlüssel ist an ein anderes Gerät gebunden.
EXPIREDDie Lizenz hat ihr Ablaufdatum überschritten.
BANNED / PAUSEDDer Schlüssel wurde vom Eigentümer gesperrt oder pausiert.
MAINTENANCEDie App befindet sich im Wartungsmodus.
SESSION_EXPIRED / SESSION_MISMATCHDie Heartbeat-Sitzung ist ungültig.
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_LIMITTestversion verweigert — für diese App deaktiviert, auf diesem Gerät bereits genutzt oder das IP-Limit ist erreicht.

HTTP-Statuscodes

error_codeBedeutung
200Erfolg — der JSON-Body enthält das Ergebnis.
400Validierung fehlgeschlagen — fehlende oder fehlerhafte Felder (siehe error_code).
401X-App-Secret oder Bearer-Token fehlt oder ist ungültig.
403Authentifiziert, aber nicht erlaubt (falsche App, widerrufenes Token …).
404Ressource nicht gefunden.
405Falsche HTTP-Methode für diesen Endpunkt.
429Rate limited — wait for the Retry-After header before retrying.
500Serverfehler — die Meldung ist absichtlich generisch; später erneut versuchen.

Rate-Limits & Fair Use

Es gibt kein pauschales Anfragekontingent, aber einige Missbrauchsschutz-Mechanismen sind aktiv:

  • Admin-Login ist gegen Brute-Force geschützt — wiederholte Fehlversuche sperren das Konto vorübergehend.
  • Testversionen sind pro Gerät (HWID) und pro IP begrenzt; Überschreitungen liefern TRIAL_LIMIT.
  • Secret-Regenerierung ist pro App rate-limitiert, um versehentliche Rotationsschleifen zu stoppen.
  • Halte Heartbeats im Intervall, das die Login-Antwort vorgibt (Standard 30 s) — schnelleres Feuern bringt nichts.

Schnellstart

Die minimale Integration: validiere einen Schlüssel beim App-Start und halte die Sitzung dann per Heartbeat am Leben.

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", … } }

Hinweis: Der komplette Session-Flow (Login → Heartbeat → Logout) nutzt den verschlüsselten Umschlag und lässt sich daher nicht von Hand mit curl testen. Binde ihn in Minuten ein — mit dem Quick Start im Panel (erzeugt funktionierende Snippets in 9 Sprachen mit deinen echten Zugangsdaten) oder den Open-Source-Beispielclients.

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

Lizenzen & Sitzungen

POST/api/auth/login.php App-Secret + verschlüsselter Umschlag#

Aktiviere einen Schlüssel bei der ersten Nutzung, binde ihn an die HWID des Geräts und öffne eine Sitzung. Bei folgenden Logins wird neu validiert und eine neue Sitzung gestartet. Erzwingt max_devices, indem die älteste Sitzung beendet wird.

Body-FeldPflicht?Beschreibung
license_keyerforderlichDer zu aktivierende / zu validierende Lizenzschlüssel.
hwiderforderlichStabiler Hardware-Fingerabdruck des Geräts.
Antwort
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 + verschlüsselter Umschlag#

Hält eine Sitzung aktiv. Rufe ihn alle heartbeat_interval Sekunden auf (von login zurückgegeben). Hörst du auf, wird die Sitzung serverseitig beendet.

Body-FeldPflicht?Beschreibung
session_iderforderlichVon login zurückgegebene Sitzung.
license_keyoptionalFalls gesendet, muss die Sitzung zu diesem Schlüssel gehören.
Antwort
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 + verschlüsselter Umschlag#

Beende eine Sitzung sofort (z. B. beim App-Ende). Body: session_id erforderlich, license_key optional.

POST/api/auth/check-key.php App-Secret · einfach oder Umschlag#

Leichte Statusabfrage — keine Sitzung, keine HWID-Bindung. Nützlich für eine Prüfung „ist dieser Schlüssel noch gültig?“.

Body-FeldPflicht?Beschreibung
license_keyerforderlichDer zu prüfende Schlüssel.
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);
Antwort
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#

Leichte Lizenzprüfung ohne HWID-Bindung oder Session — ideal als Vorab-Check vor dem vollen Login. Einfaches JSON rein, einfaches JSON raus.

Body-FeldPflicht?Beschreibung
license_keyerforderlichDer zu prüfende Schlüssel.
POST/api/auth/request-hwid-reset.php App-Secret#

Sende eine HWID-Reset-Anfrage aus deinem In-App-Dialog, wenn ein Nutzer das Gerät wechselt. Der App-Eigentümer genehmigt sie im Dashboard.

Body-FeldPflicht?Beschreibung
license_key or usernameerforderlichLegt fest, wessen Bindung zurückgesetzt wird.
reasonoptionalDem Eigentümer angezeigte Notiz.

Kostenlose Testversionen

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

Stelle einen zeitlich begrenzten Test-Schlüssel aus, gebunden an das anfragende Gerät. Eine Testversion pro HWID pro App. Die Testdauer legt der App-Eigentümer fest.

Body-FeldPflicht?Beschreibung
hwiderforderlichGeräte-Fingerabdruck, der die Testversion anfordert.
Antwort
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": "…" }
}

Benutzerkonten

Lieber Benutzername/Passwort statt roher Schlüssel? PWF Auth liefert ein vollständiges Kontomodell mit bcrypt-Hashing und HWID-Bindung.

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

Erstelle ein Endnutzer-Konto. Body: username, password erforderlich, email optional.

Body-FeldPflicht?Beschreibung
usernameerforderlichGewünschter Benutzername (eindeutig pro App).
passworderforderlichPasswort (serverseitig gehasht gespeichert).
emailoptionalKontakt-E-Mail für das Konto.
POST/api/auth/account-login.php App-Secret#

Authentifiziere dich und binde ein Gerät. Body: username, password, hwid erforderlich. Gibt dieselbe Sitzungsstruktur wie login zurück.

Body-FeldPflicht?Beschreibung
usernameerforderlichBenutzername des Kontos.
passworderforderlichPasswort des Kontos.
hwiderforderlichGeräte-Fingerprint, an den die Session gebunden wird.
POST/api/auth/change-password.php App-Secret#

Ändere das Passwort eines Nutzers. Body: username, current_password, new_password erforderlich.

Body-FeldPflicht?Beschreibung
usernameerforderlichBenutzername des Kontos.
current_passworderforderlichAktuelles Passwort (wird vor der Änderung geprüft).
new_passworderforderlichNeues Passwort.

App-Inhalte & Fernsteuerung

Über diese Endpunkte zieht deine ausgelieferte App Inhalte, die du live im Panel bearbeitest — Texte, Slides, Preise, Changelog — ohne ein Update zu veröffentlichen.

GET/api/app/text.php App-Secret + verschlüsselter Umschlag#

Remote-Text(e) abrufen. Mit ?name=… kommt ein einzelner Eintrag zurück; ohne Parameter alle Texte der App. Schlüssel-Overrides schlagen App-Defaults. Der Lizenzschlüssel reist in Authorization: Bearer (oder ?key=). Antwort im Umschlag.

Body-FeldPflicht?Beschreibung
?nameoptionalAbzurufender Textschlüssel (weglassen für alle).
POST/api/app/slides.php App-Secret + verschlüsselter Umschlag#

Liefert die aktiven Ankündigungs-Slides der App. Sende den Umschlag-Body {"action":"get_slides"}; die Antwort ist verschlüsselt.

GET/api/app/info.php App-Secret + verschlüsselter Umschlag#

App-Metadaten + Social-Links (Name, Branding, URLs). Antwort im Umschlag.

POST/api/app/social-click.php App-Secret + verschlüsselter Umschlag#

Erhöht den Klickzähler eines Social-Links (für die Engagement-Statistiken im Panel).

Body-FeldPflicht?Beschreibung
link_iderforderlichNumerische ID des Social-Links.
GET/api/app/changelog.php Öffentlich#

Öffentlicher Changelog-Feed ohne Auth für eine App — rendere überall ein "Was ist neu"-Widget. Nur veröffentlichte Einträge.

Body-FeldPflicht?Beschreibung
?app_iderforderlichDie App, deren Changelog gelesen wird.
?limitoptional1–50, Standard 20.
?sinceoptionalISO-Datum — nur Einträge ab diesem Datum.
?categoryoptionalFilter: new / improved / fixed / removed / security / deprecated.
GET/api/app/pricing.php Öffentlich#

Öffentliche Preis-Schnittstelle: Abo-Stufen und Legacy-Pläne einer App; POST erstellt eine Bestellung; ?action=check liest das aktive Abo eines Nutzers.

Body-FeldPflicht?Beschreibung
?app_iderforderlichDie App, deren Preise gelesen werden.
?typeoptionallevels oder plans (weglassen für beide).
POST/api/app/fulfill.php Fulfillment-Secret#

Server-zu-Server-Bestellabschluss für automatisierte Zahlungs-Gateways — eine verifizierte IPN schließt die Bestellung ab und stößt Schlüsselerzeugung + Zustellung an. Authentifiziert über das gemeinsame Fulfillment-Secret aus deinen Panel-Einstellungen, nie über eine Benutzersession.

Body-FeldPflicht?Beschreibung
order_iderforderlichDie abzuschließende Bestellung (ORD-…).
secreterforderlichDein order_fulfill_secret.
payment_refoptionalOptionale Zahlungsreferenz des Gateways.

OTA-Updates

POST/api/update/check.php App-Secret + verschlüsselter Umschlag#

Frage, ob für den Aufrufer ein neuerer Build verfügbar ist — unter Berücksichtigung von Kanal, OS-/Arch-Filtern, Mindestversions-Gates und gestaffelten Rollouts (derselbe Aufrufer landet stabil im selben Slice).

Body-FeldPflicht?Beschreibung
verforderlichAktuelle Version des Aufrufers (semver).
channeloptionalstable (Standard), beta oder alpha.
os / archoptionalwindows·macos·linux / x64·x86·arm64.
hwid / license_keyoptionalWird für das stabile Rollout-Bucketing verwendet.
Antwort
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#

Streame die Binärdatei einer bestimmten Version. Nutze die von check.php zurückgegebene download_url und prüfe nach dem Download den sha256.

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

HeaderBeschreibung
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

Serverseitige Endpunkte zur Verwaltung von Apps und Schlüsseln. Alle erfordern ein Authorization: Bearer-Token vom Login-Endpunkt unten.

Du willst dein Passwort nicht in einem Skript speichern? Erzeuge im Panel unter Profil → API Tokens ein Personal Access Token und nutze es als Bearer-Wert (pwf_…). Es läuft nie ab, bis du es widerrufst, funktioniert mit jedem /api/admin/*-Endpunkt und ist konstruktionsbedingt von der Verwaltung von Zugangsdaten (Passwort, E-Mail, 2FA, weitere Tokens) ausgeschlossen.

POST/api/admin/login.php Öffentlich#

Tausche Anmeldedaten gegen ein JWT. Ist 2FA aktiviert, wird requires_2fa + ein step1_token zurückgegeben, abzuschließen über /api/admin/totp.php.

Body-FeldPflicht?Beschreibung
usernameerforderlichAdmin-Benutzername.
passworderforderlichAdmin-Passwort.
POST/api/admin/keys.php Bearer#

Erzeuge Lizenzschlüssel in großer Menge. Derselbe Endpunkt unterstützt auch GET (auflisten), PUT (bearbeiten), DELETE sowie Aktions-Queries wie ?action=ban|pause|extend|reset-hwid und ?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}'

Die Admin-Oberfläche deckt außerdem apps, sessions, updates, feature-flags, webhooks, analytics, audit, resellers und mehr ab — jeweils unter /api/admin/ mit derselben Bearer-Auth. Melde dich im Dashboard an, um sie zu erkunden.

SDKs

Die REST-API funktioniert aus allem, was eine HTTP-Anfrage senden kann. Offizielle Ein-Datei-SDKs gibt es für VB.NET / C#, mit erstklassigen Beispielen für Python und PHP. Alles andere — Node, Go, Rust, Java, Swift — spricht mit denselben JSON-Endpunkten oben.

  • Open-Source-Beispielclients (VB.NET · C# · Python) — komplette Login/Heartbeat/Umschlag-Flows, fertig zum Kopieren
  • Quick Start im Panel — 5 geführte Schritte erzeugen funktionierende Snippets in 9 Sprachen mit deinen echten Zugangsdaten
  • OpenAPI 3.1 spec (openapi.json) — one-click import into Postman / Insomnia, or feed it to your code generator

Bereit zur Integration?

Erstelle ein kostenloses Konto, registriere eine App und kopiere dein X-App-Secret aus dem Dashboard.

Hol dir deinen API-Key — kostenlos
PWF Auth PWF Auth

Der Lizenzserver und das Benutzer-Authentifizierungs-Backend, das Entwickler wirklich nutzen wollen. Self-Service, gehostet, sicher.

Produkt

  • Funktionen
  • Preise
  • So funktioniert's
  • FAQ
  • KeyAuth alternative
  • Cryptolens alternative
  • Keygen alternative
  • License keys for .NET

Entwickler

  • Getting started
  • API-Referenz
  • SDKs & tools
  • API-Status (JSON)
  • Änderungsprotokoll
  • Dashboard
  • API-Key holen

Unternehmen

  • About us
  • Kontakt
  • Systemstatus
  • Sicherheitsproblem melden

Rechtliches

  • Datenschutzerklärung
  • Nutzungsbedingungen
  • DSGVO
  • Cookies
© 2026 PWF Auth. Alle Rechte vorbehalten.
Alle Systeme betriebsbereit