Developer API

API keys for your own bot, store or server: make, look up, reset, ban and delete keys.

The Developer API lets your own tools manage your licence keys: a Discord or Telegram bot of yours, a store, your website or a script. Make keys, look a key up (status, time left, which PCs it is bound to), free it for a new PC, ban it, add time or delete it, without ever giving anyone your panel password. Make API keys on the Developer API page.

Never give your panel login to a bot, an agent or a helper. Make an API key that can do only what that tool needs; if it leaks, delete it and nothing else is touched.

API keys

  • An API key works for one application. Up to 10 per app; give each tool its own.
  • Tick what it may do: See keys, Make keys, Add or remove time, Reset PCs, Ban and pause, Edit keys, Delete keys. Everything else is refused with 403 NOT_ALLOWED.
  • The key (pwf_…) is shown once. PWF Auth keeps only a fingerprint of it, so it cannot be shown again; make a new one if it is lost.
  • Optionally, allow calls only from your server's IP addresses (single addresses or ranges like 198.51.100.0/24).
  • 120 calls a minute per API key. Above that the answer is 429 with a Retry-After header.

Everything an API key changes goes through the same code as the panel: it shows in the key's Logs as api:<name>, reaches your Webhooks with "by": "api", and keys it makes use your app's key format.

Calls

The address is https://pwfauth.com/api/v1. Send the key in Authorization: Bearer pwf_… (or X-API-Key). Bodies are JSON (a form body works too). Every answer is JSON with "success"; a refusal also has "error" (a code) and "message" (a sentence you can show).

CallDoesNeeds
GET /meThe app, the key's name and what it may do. Handy to test a key.any key
GET /keysThe app's keys, newest first. ?status= unused, active, expired, paused, banned or redeemed; ?q= searches key, note and customer; ?limit= 1–100 (50), ?offset=.See keys
GET /keys/{key}One key, with its PCs (hwids).See keys
POST /keys{"count": 1–50, "type": "days|weeks|months|lifetime", "duration": 30, "max_devices": 1, "note": ""}. Answers 201 with "keys". Keys start counting the first time they are used.Make keys
POST /keys/{key}/reset-hwidFrees the key from every PC; the next sign-in binds a new one. Running copies close.Reset PCs
POST /keys/{key}/add-time{"days": 7}; negative removes time (or /remove-time). Same rules as Add time in Licences.Add or remove time
POST /keys/{key}/ban{"reason": "", "refund": false}: signs it out at once; refund gives a reseller who sold it their credits back. Also /unban, /pause (keeps the time left) and /resume.Ban and pause
PATCH /keys/{key}{"note", "customer_name", "customer_contact", "max_devices"} (or POST /keys/{key}/update).Edit keys
DELETE /keys/{key}Deletes it (or POST /keys/{key}/delete). A reseller who sold it gets the credits back.Delete keys

What a key looks like

JSON
{
  "success": true,
  "key": {
    "key": "ABCDE-12345-FGHIJ-67890",
    "status": "active",                 // unused | active | expired | paused | banned | redeemed
    "type": "days", "duration": 30, "lifetime": false,
    "expires_at": "2026-11-11T10:00:00Z", "days_left": 30,
    "max_devices": 1, "devices": 1, "hwid_locked": true,
    "hwids": [{"hwid": "2f5a1c8e-…", "label": "", "first_seen": "…", "last_seen": "…"}],
    "online": true, "sessions": 1, "last_login": "…", "total_logins": 12,
    "note": "", "customer_name": "", "customer_contact": "",
    "source": "panel",                  // panel | api | reseller | store | discord | telegram | trial
    "created_at": "…", "activated_at": "…", "paused_days_left": null, "flagged": false
  }
}

Errors

StatuserrorMeans
401NO_API_KEY · BAD_API_KEYNo key sent, or it is wrong, deleted, or its account no longer has the app.
403NOT_ALLOWED · IP_NOT_ALLOWEDThe key may not do this, or not from this address.
404KEY_NOT_FOUNDNo such licence key in this app.
400 · 409REFUSED, BAD_…, ALREADY_BANNED, NOT_BANNEDThe request can't be done; the message says why (for example "Only an active key can be paused.").
429RATE_LIMITEDOver 120 calls a minute: wait the seconds in Retry-After.

Examples

Python
import os, requests

API = "https://pwfauth.com/api/v1"
H = {"Authorization": "Bearer " + os.environ["PWF_API_KEY"]}

key = requests.get(f"{API}/keys/ABCDE-12345", headers=H, timeout=15).json()
if key["success"]:
    k = key["key"]
    print(k["status"], k["days_left"], "PCs:", k["devices"], "/", k["max_devices"])

made = requests.post(f"{API}/keys", headers=H, json={"count": 1, "type": "days", "duration": 30}, timeout=15).json()
print(made["keys"])
JavaScript
// discord.js v14: /keyinfo key:<text> answers only the person who asked
const API = 'https://pwfauth.com/api/v1';
const H = { Authorization: 'Bearer ' + process.env.PWF_API_KEY };

client.on('interactionCreate', async (i) => {
  if (!i.isChatInputCommand() || i.commandName !== 'keyinfo') return;
  const key = encodeURIComponent(i.options.getString('key'));
  const r = await (await fetch(`${API}/keys/${key}`, { headers: H })).json();
  if (!r.success) return i.reply({ content: r.message, ephemeral: true });
  const k = r.key;
  await i.reply({ ephemeral: true, content:
    `**${k.status}** · ${k.lifetime ? 'lifetime' : (k.days_left ?? k.duration) + ' days'} · ` +
    `PCs ${k.devices}/${k.max_devices}${k.hwid_locked ? ' (locked)' : ''}` });
});

PWF Auth's own Discord bot already does this inside your server without code; the API is for when you want your own bot or system.

Something missing or unclear? Tell us on Telegram.