No matching endpoints
Nothing here matches
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.
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.
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>.
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.
{
"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:
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 model | Endpoints |
|---|---|
| Ö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:
{
"success": false,
"message": "This license key has been banned.",
"error_code": "BANNED"
}
| error_code | Bedeutung |
|---|---|
MISSING_FIELDS | Ein erforderliches Body-Feld fehlt. |
INVALID_KEY | Der Lizenzschlüssel existiert für diese App nicht. |
INVALID_CREDENTIALS | Benutzername/Passwort abgelehnt (Benutzerkonten + Admin-Login). |
HWID_MISMATCH | Der Schlüssel ist an ein anderes Gerät gebunden. |
EXPIRED | Die Lizenz hat ihr Ablaufdatum überschritten. |
BANNED / PAUSED | Der Schlüssel wurde vom Eigentümer gesperrt oder pausiert. |
MAINTENANCE | Die App befindet sich im Wartungsmodus. |
SESSION_EXPIRED / SESSION_MISMATCH | Die Heartbeat-Sitzung ist ungültig. |
TOO_MANY_ATTEMPTS | Login brute-force lockout — the account is locked temporarily; retry_after in the response says for how many seconds. |
TRIAL_DISABLED / TRIAL_USED / TRIAL_LIMIT | Testversion verweigert — für diese App deaktiviert, auf diesem Gerät bereits genutzt oder das IP-Limit ist erreicht. |
HTTP-Statuscodes
| error_code | Bedeutung |
|---|---|
200 | Erfolg — der JSON-Body enthält das Ergebnis. |
400 | Validierung fehlgeschlagen — fehlende oder fehlerhafte Felder (siehe error_code). |
401 | X-App-Secret oder Bearer-Token fehlt oder ist ungültig. |
403 | Authentifiziert, aber nicht erlaubt (falsche App, widerrufenes Token …). |
404 | Ressource nicht gefunden. |
405 | Falsche HTTP-Methode für diesen Endpunkt. |
429 | Rate limited — wait for the Retry-After header before retrying. |
500 | Serverfehler — 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.
# 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
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-Feld | Pflicht? | Beschreibung |
|---|---|---|
license_key | erforderlich | Der zu aktivierende / zu validierende Lizenzschlüssel. |
hwid | erforderlich | Stabiler Hardware-Fingerabdruck des Geräts. |
{
"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
}
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-Feld | Pflicht? | Beschreibung |
|---|---|---|
session_id | erforderlich | Von login zurückgegebene Sitzung. |
license_key | optional | Falls gesendet, muss die Sitzung zu diesem Schlüssel gehören. |
{ "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
Beende eine Sitzung sofort (z. B. beim App-Ende). Body: session_id erforderlich, license_key optional.
Leichte Statusabfrage — keine Sitzung, keine HWID-Bindung. Nützlich für eine Prüfung „ist dieser Schlüssel noch gültig?“.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
license_key | erforderlich | Der 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);
{
"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
}
}
Leichte Lizenzprüfung ohne HWID-Bindung oder Session — ideal als Vorab-Check vor dem vollen Login. Einfaches JSON rein, einfaches JSON raus.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
license_key | erforderlich | Der zu prüfende Schlüssel. |
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-Feld | Pflicht? | Beschreibung |
|---|---|---|
license_key or username | erforderlich | Legt fest, wessen Bindung zurückgesetzt wird. |
reason | optional | Dem Eigentümer angezeigte Notiz. |
Kostenlose Testversionen
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-Feld | Pflicht? | Beschreibung |
|---|---|---|
hwid | erforderlich | Geräte-Fingerabdruck, der die Testversion anfordert. |
{
"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.
Erstelle ein Endnutzer-Konto. Body: username, password erforderlich, email optional.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
username | erforderlich | Gewünschter Benutzername (eindeutig pro App). |
password | erforderlich | Passwort (serverseitig gehasht gespeichert). |
email | optional | Kontakt-E-Mail für das Konto. |
Authentifiziere dich und binde ein Gerät. Body: username, password, hwid erforderlich. Gibt dieselbe Sitzungsstruktur wie login zurück.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
username | erforderlich | Benutzername des Kontos. |
password | erforderlich | Passwort des Kontos. |
hwid | erforderlich | Geräte-Fingerprint, an den die Session gebunden wird. |
Ändere das Passwort eines Nutzers. Body: username, current_password, new_password erforderlich.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
username | erforderlich | Benutzername des Kontos. |
current_password | erforderlich | Aktuelles Passwort (wird vor der Änderung geprüft). |
new_password | erforderlich | Neues 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.
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
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
?name | optional | Abzurufender Textschlüssel (weglassen für alle). |
Liefert die aktiven Ankündigungs-Slides der App. Sende den Umschlag-Body {"action":"get_slides"}; die Antwort ist verschlüsselt.
App-Metadaten + Social-Links (Name, Branding, URLs). Antwort im Umschlag.
Erhöht den Klickzähler eines Social-Links (für die Engagement-Statistiken im Panel).
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
link_id | erforderlich | Numerische ID des Social-Links. |
Öffentlicher Changelog-Feed ohne Auth für eine App — rendere überall ein "Was ist neu"-Widget. Nur veröffentlichte Einträge.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
?app_id | erforderlich | Die App, deren Changelog gelesen wird. |
?limit | optional | 1–50, Standard 20. |
?since | optional | ISO-Datum — nur Einträge ab diesem Datum. |
?category | optional | Filter: new / improved / fixed / removed / security / deprecated. |
Öffentliche Preis-Schnittstelle: Abo-Stufen und Legacy-Pläne einer App; POST erstellt eine Bestellung; ?action=check liest das aktive Abo eines Nutzers.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
?app_id | erforderlich | Die App, deren Preise gelesen werden. |
?type | optional | levels oder plans (weglassen für beide). |
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-Feld | Pflicht? | Beschreibung |
|---|---|---|
order_id | erforderlich | Die abzuschließende Bestellung (ORD-…). |
secret | erforderlich | Dein order_fulfill_secret. |
payment_ref | optional | Optionale Zahlungsreferenz des Gateways. |
OTA-Updates
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-Feld | Pflicht? | Beschreibung |
|---|---|---|
v | erforderlich | Aktuelle Version des Aufrufers (semver). |
channel | optional | stable (Standard), beta oder alpha. |
os / arch | optional | windows·macos·linux / x64·x86·arm64. |
hwid / license_key | optional | Wird für das stabile Rollout-Bucketing verwendet. |
{
"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"
}
}
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
{
"event": "key.activated",
"timestamp": "2026-08-20T14:07:02Z",
"data": {
"app_id": "01af6643-…",
"license_key": "XXXXX-XXXXX-XXXXX-XXXXX",
"hwid": "A1B2C3…"
}
}
Delivery headers
| Header | Beschreibung |
|---|---|
X-Webhook-Id | Unique delivery id (whd_…). Stays the same across retries of one event — use it to deduplicate. |
X-Webhook-Timestamp | Unix timestamp of the delivery attempt. |
X-Webhook-Attempt | 1-based attempt counter. |
X-Webhook-Signature | Legacy signature: hex(HMAC-SHA256(body, secret)). |
X-Webhook-Signature-V2 | Preferred 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/3xxanswered within 5 seconds. - Failures retry up to 5 attempts with widening gaps: 1 m → 5 m → 30 m → 2 h → 6 h.
X-Webhook-Idis minted once per event and reused on every retry — idempotency is oneseen?lookup away.- A destination that fails 20 times in a row is paused automatically; re-enable it from the panel (a
testevent 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:
| Event | Fires when |
|---|---|
key.created | Keys are generated (panel or admin API). |
key.activated | A key's first successful login binds it to a device. |
key.banned / key.unbanned | A key is banned / unbanned. |
key.deleted | A key is deleted. |
key.rotated | A key string is rotated (the old string stops working). |
key.suspected_leak | Leak detection flags a key as suspected shared/leaked. |
key_expiring | A key approaches expiry (scheduled sweep). |
key_expired | A key expires (scheduled sweep). |
trial.created | A trial key is issued to a new device. |
hwid_reset.requested | A client requests a device (HWID) reset. |
new_order | A purchase order is created. |
order.approved | An order is approved and fulfilled. |
subscription.* | Subscription lifecycle: created / renewed / cancelled / renewal order created. |
update.published | A new application version is published. |
changelog.published | A changelog entry goes live. |
app.revoked | An application is revoked / disabled. |
suspicious_activity | The suspicious-activity heuristic fires. |
test | Manual 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.
Tausche Anmeldedaten gegen ein JWT. Ist 2FA aktiviert, wird requires_2fa + ein step1_token zurückgegeben, abzuschließen über /api/admin/totp.php.
| Body-Feld | Pflicht? | Beschreibung |
|---|---|---|
username | erforderlich | Admin-Benutzername. |
password | erforderlich | Admin-Passwort. |
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.
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.