/ examples

Real code. Copy and ship.

End-to-end examples for every key type — from Roblox game scripts to Node.js API middleware. Each snippet is production-ready and wired to the live API.

script surface

Script key system

Roblox executors, game scripts, Lua tools

Script keys work with HWID binding and optional ad checkpoints. The first time a device validates, its hardware ID is stored — subsequent attempts from other machines get hwid_mismatch.

1. Generate keys (your server / Discord bot)

Always mint keys server-side — never expose your API secret in game scripts.

const res = await fetch("https://keysystem.nabzclan.vip/api/v1/keys/generate", {
  method: "POST",
  headers: {
    "x-project": "p_YOUR_PROJECT_ID",
    "Authorization": "Bearer YOUR_API_SECRET",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    count: 10,
    label: "discord-giveaway-june",
    ttl_minutes: 10080, // 7 days
  }),
});
const { keys } = await res.json();
// keys[0].key => "KS-XXXX-XXXX-XXXX"
console.log(keys.map(k => k.key).join("\n"));

2. Complete a checkpoint (Lua, Roblox)

If your project has checkpoints enabled, the player must complete each step before validation succeeds. Use the checkpoint slug — not its ID.

local HttpService = game:GetService("HttpService")
local PROJECT_ID = "p_YOUR_PROJECT_ID"

local function completeCheckpoint(key: string, checkpointSlug: string)
    local ok, result = pcall(function()
        local response = HttpService:RequestAsync({
            Url = "https://keysystem.nabzclan.vip/api/v1/checkpoints/complete",
            Method = "POST",
            Headers = {
                ["Content-Type"] = "application/json",
                ["x-project"] = PROJECT_ID,
            },
            Body = HttpService:JSONEncode({
                key = key,
                checkpoint = checkpointSlug,
            }),
        })
        return HttpService:JSONDecode(response.Body)
    end)

    if not ok then return false, "Network error" end
    if result.cleared then
        return true, "All checkpoints done!"
    end
    return false, result.remaining .. " checkpoint(s) remaining"
end

-- After the player clicks the ad/link button:
local done, msg = completeCheckpoint("KS-7H2P-VLNX-9KJ4", "step-1-linkvertise")
print(msg)

3. Validate key with HWID (Lua, Roblox)

The first validate call with a new HWID binds the key to that device. Subsequent calls from different HWIDs are rejected.

local HttpService = game:GetService("HttpService")
local PROJECT_ID = "p_YOUR_PROJECT_ID"

local REASONS = {
    checkpoints_required = function(data)
        return "Complete " .. (data.checkpoints_remaining or 1) .. " step(s) first"
    end,
    hwid_mismatch  = "Key is bound to another device",
    hwid_required  = "Key requires a device ID",
    expired        = "Key has expired",
    revoked        = "Key has been revoked",
    paused         = "Key is temporarily paused",
    invalid_key    = "Key not found",
}

local function validateKey(key: string): (boolean, string?)
    -- Use a stable fingerprint — RbxAnalyticsService or a saved GUID
    local hwid = game:GetService("RbxAnalyticsService"):GetClientId()

    local ok, resp = pcall(HttpService.RequestAsync, HttpService, {
        Url    = "https://keysystem.nabzclan.vip/api/v1/keys/validate",
        Method = "POST",
        Headers = {
            ["Content-Type"] = "application/json",
            ["x-project"]    = PROJECT_ID,
        },
        Body = HttpService:JSONEncode({ key = key, hwid = hwid }),
    })

    if not ok then return false, "Network error — try again" end

    local data = HttpService:JSONDecode(resp.Body)
    if data.valid then return true, nil end

    local reason = REASONS[data.reason]
    if type(reason) == "function" then reason = reason(data) end
    return false, reason or ("Blocked: " .. (data.reason or "unknown"))
end

-- Usage:
local granted, err = validateKey(script.Parent.TextBox.Text)
if granted then
    game.Players.LocalPlayer.leaderstats.Access.Value = true
else
    warn("[KeySystem] " .. (err or "denied"))
end
api surface

API access key (sk_xxx)

SaaS products, developer tools, rate-limited services

API keys are prefixed sk_live_ and carry scopes and per-minute rate limits. Use them as drop-in authentication for your own API.

1. Issue a key to a customer

curl -X POST https://keysystem.nabzclan.vip/api/v1/keys/generate \
  -H "x-project: p_YOUR_PROJECT_ID" \
  -H "Authorization: Bearer YOUR_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "count": 1,
    "label": "customer-alice",
    "scopes": ["read", "write"],
    "rate_limit_per_minute": 60
  }'

# Response:
# { "ok": true, "count": 1, "keys": [
#   { "id": "k_xxx", "key": "sk_live_Dp3CEgv4...", "expires_at": null }
# ]}

2. Validate on each incoming request (Express middleware)

import type { Request, Response, NextFunction } from "express";

const PROJECT_ID = process.env.KEYSTATION_PROJECT_ID!;

// Cache validation results briefly to reduce API calls
const cache = new Map<string, { valid: boolean; scopes: string[]; exp: number }>();

async function keystationAuth(req: Request, res: Response, next: NextFunction) {
  const apiKey =
    req.headers["x-api-key"] as string ||
    req.headers.authorization?.replace(/^Bearers+/i, "");

  if (!apiKey) return res.status(401).json({ error: "Missing API key" });

  // Check local cache (10s TTL)
  const cached = cache.get(apiKey);
  if (cached && cached.exp > Date.now()) {
    if (!cached.valid) return res.status(401).json({ error: "Invalid key" });
    req.scopes = cached.scopes;
    return next();
  }

  const resp = await fetch("https://keysystem.nabzclan.vip/api/v1/keys/validate", {
    method: "POST",
    headers: { "x-project": PROJECT_ID, "Content-Type": "application/json" },
    body: JSON.stringify({ key: apiKey }),
  });

  if (resp.status === 429) return res.status(429).json({ error: "Rate limited" });

  const data = await resp.json();
  const scopes: string[] = Array.isArray(data.scopes) ? data.scopes :
    typeof data.scopes === "string" ? JSON.parse(data.scopes) : [];

  cache.set(apiKey, { valid: data.valid, scopes, exp: Date.now() + 10_000 });

  if (!data.valid) return res.status(401).json({ error: data.reason ?? "Invalid key" });

  req.scopes = scopes;
  next();
}

// Scope guard helper
function requireScope(scope: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.scopes?.includes(scope)) {
      return res.status(403).json({ error: `Missing scope: ${scope}` });
    }
    next();
  };
}

export { keystationAuth, requireScope };
license surface

Software license

Desktop apps, CLIs, Electron apps — machine-bound seats

License keys bind to machine fingerprints. Each activation consumes a seat (up to max_activations). Users can deactivate a machine to transfer a seat to another device.

1. Activate on install

import httpx, uuid, platform, sys

PROJECT_ID = "p_YOUR_PROJECT_ID"

def get_machine_id() -> str:
    """Stable fingerprint — MAC address as UUID."""
    return str(uuid.UUID(int=uuid.getnode()))

def activate(license_key: str) -> dict:
    resp = httpx.post(
        "https://keysystem.nabzclan.vip/api/v1/license/activate",
        headers={"x-project": PROJECT_ID, "Content-Type": "application/json"},
        json={
            "key": license_key,
            "machine_id": get_machine_id(),
            "machine_name": platform.node(),
        },
    )
    data = resp.json()
    if not data.get("ok"):
        print("Activation failed. Seat limit may be reached.", file=sys.stderr)
        sys.exit(1)
    print(f"✓ Activated — {data['seats_used']}/{data['seats_max']} seats used")
    return data

activate("ABCD-EFGH-IJKL-MNOP-QRST")

2. Validate on every launch

def check_license(license_key: str) -> bool:
    resp = httpx.post(
        "https://keysystem.nabzclan.vip/api/v1/keys/validate",
        headers={"x-project": PROJECT_ID, "Content-Type": "application/json"},
        json={"key": license_key, "machine_id": get_machine_id()},
    )
    data = resp.json()

    if data.get("valid"):
        return True

    reason = data.get("reason", "unknown")
    messages = {
        "activation_limit_reached": "Seat limit reached — deactivate another machine.",
        "expired": "Your license has expired. Renew at nabzclan.vip.",
        "revoked": "License revoked. Contact support.",
        "hwid_mismatch": "Machine mismatch — run on your registered device.",
    }
    print("License check failed:", messages.get(reason, reason), file=sys.stderr)
    return False

if not check_license("ABCD-EFGH-IJKL-MNOP-QRST"):
    sys.exit(1)

3. Transfer license (deactivate old machine)

def deactivate(license_key: str, machine_id: str | None = None) -> bool:
    mid = machine_id or get_machine_id()
    resp = httpx.post(
        "https://keysystem.nabzclan.vip/api/v1/license/deactivate",
        headers={"x-project": PROJECT_ID, "Content-Type": "application/json"},
        json={"key": license_key, "machine_id": mid},
    )
    data = resp.json()
    if data.get("deactivated"):
        print(f"Deactivated. {data['seats_used']}/{data['seats_max']} seats now used")
        return True
    return False

# Transfer flow:
# 1. Deactivate old machine (run on old machine, or pass its machine_id)
deactivate("ABCD-EFGH-IJKL-MNOP-QRST")
# 2. Activate on new machine
activate("ABCD-EFGH-IJKL-MNOP-QRST")
admin api

Key management

List, edit, revoke — build your own dashboard or bot

Admin endpoints accept either your full project API secret or a scoped kss_… token from the project's Credentials tab. Scoped tokens follow least-privilege — a read-only sync bot only needs keys:read, a moderation bot only needs keys:revoke.

1. List all keys with pagination

Iterate pages until has_next is false. Filter by status to only pull active or expired keys.

const PROJECT_ID = "p_YOUR_PROJECT_ID";
const SECRET     = process.env.KEYSTATION_SECRET; // or a kss_ token with keys:read

async function listKeys({ page = 1, limit = 50, status } = {}) {
  const url = new URL("https://keysystem.nabzclan.vip/api/v1/keys");
  url.searchParams.set("page",  page);
  url.searchParams.set("limit", limit);
  if (status) url.searchParams.set("status", status);

  const res = await fetch(url, {
    headers: {
      "x-project":     PROJECT_ID,
      "Authorization": `Bearer ${SECRET}`,
    },
  });
  return res.json();
}

// Fetch all active keys across every page
async function fetchAllActiveKeys() {
  const all = [];
  let page  = 1;
  while (true) {
    const data = await listKeys({ page, status: "active" });
    all.push(...data.keys);
    if (!data.pagination.has_next) break;
    page++;
  }
  console.log(`Fetched ${all.length} active keys`);
  return all;
}

2. Edit a key

All fields are optional — only the ones you send are changed. Setting hwid: null clears the HWID binding so a key can re-bind to a new device. Status changes (paused / revoked) require keys:revoke; all other edits require keys:write.

const PROJECT_ID = "p_YOUR_PROJECT_ID";
const SECRET     = process.env.KEYSTATION_SECRET;

async function editKey(keyValue, patch) {
  const res = await fetch(
    `https://keysystem.nabzclan.vip/api/v1/keys/${encodeURIComponent(keyValue)}`,
    {
      method: "PATCH",
      headers: {
        "x-project":     PROJECT_ID,
        "Authorization": `Bearer ${SECRET}`,
        "Content-Type":  "application/json",
      },
      body: JSON.stringify(patch),
    },
  );
  return res.json();
}

// Add 30 days to current expiry — safe renewal, never shortens the key
await editKey("KS-7H2P-VLNX-9KJ4", {
  label:          "customer-alice-renewed",
  extend_minutes: 43200,  // adds 30 days to whatever expiry is already set
});

// Set expiry from now (replaces current expiry — use extend_minutes to add instead)
await editKey("KS-7H2P-VLNX-9KJ4", { ttl_minutes: 10080 }); // now + 7 days

// Remove expiry entirely — make the key perpetual
await editKey("KS-7H2P-VLNX-9KJ4", { ttl_minutes: 0 });

// Pause a key (blocks validates without permanent revocation)
await editKey("KS-7H2P-VLNX-9KJ4", { status: "paused" });

// Unpause it later
await editKey("KS-7H2P-VLNX-9KJ4", { status: "active" });

// Clear HWID — let a key re-bind to a new device
await editKey("KS-7H2P-VLNX-9KJ4", { hwid: null });

// Set metadata (replaces existing metadata entirely)
await editKey("KS-7H2P-VLNX-9KJ4", {
  metadata: { customer_id: "cus_abc123", plan: "pro" },
});

3. Scoped tokens — least-privilege integrations

Issue kss_… tokens from the project's Credentials tab instead of sharing your full API secret. Each token only has the permissions you grant — a Discord bot that syncs key data never needs keys:revoke.

// This bot only has keys:read — it can never generate or revoke
const READ_ONLY_TOKEN = process.env.KEYSTATION_READ_TOKEN; // kss_…

async function syncKeysToDatabase() {
  let page = 1;
  while (true) {
    const data = await fetch(
      `https://keysystem.nabzclan.vip/api/v1/keys?page=${page}&limit=100`,
      {
        headers: {
          "x-project":     "p_YOUR_PROJECT_ID",
          "Authorization": `Bearer ${READ_ONLY_TOKEN}`,
        },
      },
    ).then(r => r.json());

    for (const key of data.keys) {
      await db.upsert("keys", {
        keystation_id: key.id,
        value:         key.key,
        status:        key.status,
        expires_at:    key.expires_at,
        last_used_at:  key.last_used_at,
      });
    }

    if (!data.pagination.has_next) break;
    page++;
  }
}
webhooks

Webhook handler

React to events in real-time — always verify the signature

Every webhook POST includes an x-keystation-signature: sha256=<hex> header. Always verify it before processing — use constant-time comparison to prevent timing attacks.

Express (Node.js)

import express from "express";
import crypto from "node:crypto";

const app = express();
// IMPORTANT: use raw body parser to get the exact bytes for HMAC
app.use("/webhooks", express.raw({ type: "application/json" }));

const WEBHOOK_SECRET = process.env.KEYSTATION_WEBHOOK_SECRET!;

function verify(rawBody: Buffer, signatureHeader: string): boolean {
  const expected = "sha256=" +
    crypto.createHmac("sha256", WEBHOOK_SECRET).update(rawBody).digest("hex");
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signatureHeader),
      Buffer.from(expected),
    );
  } catch {
    return false;
  }
}

app.post("/webhooks/keystation", (req, res) => {
  const sig = req.headers["x-keystation-signature"] as string;

  if (!sig || !verify(req.body as Buffer, sig)) {
    return res.status(401).json({ error: "Signature mismatch" });
  }

  const { event, data, sent_at } = JSON.parse((req.body as Buffer).toString());

  switch (event) {
    case "key.validated":
      console.log(`Key ${data.key_id} used from ${data.ip}`);
      // update last_seen in your database
      break;

    case "key.expired":
      console.log(`Key ${data.key_id} expired`);
      // notify the user — send email or Discord DM
      break;

    case "key.activated":
      console.log(`Seat activated for key ${data.key_id} on machine ${data.machine_id}`);
      break;

    case "checkpoint.complete":
      console.log(`Checkpoint ${data.checkpoint} cleared for key ${data.key_id}`);
      break;
  }

  res.json({ ok: true });
});
obfuscator

Code obfuscator API

Upload a file, get back the obfuscated version — 7 engines

Send any source file to POST /api/v1/obfuscate and receive the obfuscated file as a download. Language is auto-detected from the file extension. Auth uses your personal obfuscate key (kob_…) from your Obfuscator dashboard, or a project credential (kss_…) with the obfuscate permission.

VIP 1+API access requires VIP Tier 1 or higher. Free accounts can use the dashboard obfuscator instead.
Lua.lua

XOR strings, anti-tamper, local rename

JavaScript.js

RC4/base64 array, control-flow, self-defend

PHP.php

Multi-pass eval + gzinflate + base64

HTML.html

Entity-encode, inline-script obfuscation

Python.py

Multi-pass exec + zlib + base64

CSS.css

Minify, strip comments, selector rename

JSON.json

Base64 or XOR+base64 envelope

Strength levels

Each level trades speed for resistance. Low is instant; high can increase file size 3–10×.

Languagelowmediumhigh
LuaStrip comments + minifyXOR string encryption + minifyMedium + local rename + loadstring payload wrap
JavaScriptBase64 string array + mangled namesControl-flow flattening + dead code + split stringsRC4 encoding + debug protection + self-defending + unicode escape
PHP1-pass gzinflate+base642-pass + str_rot13 salt3-pass + str_rot13 + strrev + char-shifted decoder
Python1-pass zlib+base64 exec2-pass nested exec3-pass nested exec
CSSStrip comments + minifyStrip comments + minifyMinify + rename class/ID selectors
JSONbase64 envelopeXOR + base64, 12-byte keyXOR + base64, 24-byte key
HTMLMinify + entity-encode textMedium + inline script obfuscationHigh + base64 outer wrap

Obfuscate via API

The file field is required — everything else is optional. Language is inferred from the filename extension automatically.

TOKEN="kob_<your_obfuscate_key>"   # or kss_<project_token>

# Lua — high strength
curl -X POST https://keysystem.nabzclan.vip/api/v1/obfuscate \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@my_script.lua" \
  -F "strength=high" \
  -F "encrypt_strings=true" \
  -F "add_anti_tamper=true" \
  -o obfuscated_my_script.lua

# JavaScript
curl -X POST https://keysystem.nabzclan.vip/api/v1/obfuscate \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  -F "strength=medium" \
  -F "self_defend=true" \
  -o obfuscated_bundle.js

# PHP — 3 wrapping passes
curl -X POST https://keysystem.nabzclan.vip/api/v1/obfuscate \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  -F "multi_pass=3" \
  -o obfuscated_loader.php

Response headers

On success the body is the obfuscated file. These headers tell you what happened:

Content-Disposition: attachment; filename="obfuscated_my_script.lua"
Content-Type: text/plain; charset=utf-8
X-Language: lua
X-Input-Bytes: 4096
X-Output-Bytes: 11203
X-Duration-Ms: 38

Ready to ship?

Create a project and get your API keys in 30 seconds.