Skip to main content
RMZ provides a complete software licensing system. Customers buy a license key from your store, and your software verifies it with a single API call. This guide covers setup, implementation, and best practices.

How It Works

1

Create a license product

In your store dashboard, create a product with type Software License. Configure the lock type, max activations, and pricing plans.
2

Customer purchases

When a customer buys the product, a license key is automatically generated and delivered (e.g., MYAPP-XXXX-XXXX-XXXX-XXXX).
3

Your software verifies

Your application calls the RMZ license verification API with the key. The API validates the key and auto-activates the device.
4

API responds

You receive the license status, plan details, activation count, and expiry information.

Reference Repository

Full working examples in Python, PHP, JavaScript, and Lua (FiveM) are available at:
https://github.com/Rmz-App/rmz-license-examples

Step 1: Create a License Product

In your store dashboard, go to Products > New Product and configure:
SettingDescription
TypeSoftware License
Key prefixA short prefix for generated keys (e.g., MYAPP produces MYAPP-XXXX-XXXX-XXXX-XXXX)
Lock typeHow keys are bound to devices (see below)
Max activationsHow many devices can use a single key (0 = unlimited)
PlansDuration-based pricing tiers (monthly, yearly, lifetime, etc.)
E2EEOptional end-to-end encryption for API responses

Lock Types

none

Key-only verification. No device binding. Any device can use the key. Best for simple products.

hwid

Locks to a hardware ID (machine fingerprint). Each unique HWID uses an activation slot. Best for desktop software.

ip

Locks to the caller’s IP address. Each unique IP uses a slot. Best for server-side applications.

Step 2: Implement Verification

The verification endpoint validates the key and auto-activates the device in a single call.
POST https://license.rmz.gg/verify

Request

FieldTypeRequiredDescription
product_idintegerYesYour product ID from the dashboard
license_keystringYesThe customer’s license key
hwidstringConditionalRequired when lock type is hwid

Implementation Examples

async function verifyLicense(licenseKey, hwid) {
  const response = await fetch("https://license.rmz.gg/verify", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      product_id: YOUR_PRODUCT_ID,
      license_key: licenseKey,
      hwid: hwid // Optional for 'none' and 'ip' lock types
    })
  });

  const result = await response.json();

  if (result.success) {
    console.log("License valid!");
    console.log("Product:", result.data.product.name);
    console.log("Status:", result.data.status);
    console.log("Expires:", result.data.expires_at || "Never (lifetime)");
    console.log("Activations:", `${result.data.activations.current}/${result.data.activations.max || "unlimited"}`);
    return result.data;
  } else {
    console.error("License invalid:", result.error, result.message);
    return null;
  }
}

Success Response

{
  "success": true,
  "data": {
    "status": "active",
    "lock_type": "hwid",
    "product": {
      "id": 123,
      "name": "My Script Pro"
    },
    "plan": {
      "duration": "annually",
      "duration_label": "سنه",
      "start_date": "23-03-2026",
      "end_date": "23-03-2027",
      "is_active": true
    },
    "expires_at": "2027-03-23T12:00:00.000000Z",
    "expires_in_days": 365,
    "activations": {
      "current": 1,
      "max": 3,
      "remaining": 2
    },
    "metadata": null
  }
}

Error Codes

CodeHTTP StatusDescription
LICENSE_NOT_FOUND404Key does not exist for this product
LICENSE_EXPIRED403License has passed its expiry date
LICENSE_REVOKED403Permanently revoked by the store owner
LICENSE_SUSPENDED403Temporarily suspended
HWID_REQUIRED403Lock type is hwid but no hwid was provided
ACTIVATION_LIMIT429All device activation slots are used

Step 3: Generate Hardware IDs

For hwid lock type, generate a consistent machine fingerprint by combining hardware-specific values:
// Node.js example
const crypto = require("crypto");
const os = require("os");

function generateHWID() {
  const components = [
    os.hostname(),
    os.cpus()[0]?.model,
    os.totalmem().toString(),
    os.platform(),
    os.arch()
  ];

  return crypto
    .createHash("sha256")
    .update(components.join("|"))
    .digest("hex");
}
Choose hardware components that are stable across reboots but unique per machine. Avoid components that change frequently (like available memory or uptime).

Step 4: Handle E2EE (Optional)

When E2EE is enabled on your license product, all API responses are encrypted with AES-256-GCM. This prevents response tampering and man-in-the-middle spoofing.

Encrypted Response Format

{
  "encrypted": true,
  "payload": "base64-encoded-ciphertext",
  "nonce": "base64-encoded-12-byte-nonce",
  "tag": "base64-encoded-16-byte-auth-tag"
}

Decryption

const crypto = require("crypto");

function decryptResponse(encrypted, encryptionKey) {
  const key = Buffer.from(encryptionKey, "hex"); // 64 hex chars = 32 bytes
  const nonce = Buffer.from(encrypted.nonce, "base64");
  const tag = Buffer.from(encrypted.tag, "base64");
  const ciphertext = Buffer.from(encrypted.payload, "base64");

  const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
  decipher.setAuthTag(tag);

  const decrypted = Buffer.concat([
    decipher.update(ciphertext),
    decipher.final()
  ]);

  return JSON.parse(decrypted.toString("utf8"));
}
Never hardcode the encryption key as a plain string in your source code. Derive it at runtime from multiple values or use an obfuscation layer.

Best Practices

Verification Frequency

  • Verify the license once on application startup
  • Optionally re-verify periodically (e.g., every 24 hours) for subscription products
  • Cache the verification result locally to handle temporary network outages
  • Rate limit: The API allows 60 requests per minute per IP

Security Recommendations

  1. Use HWID lock type for desktop applications to prevent key sharing
  2. Enable E2EE to prevent response tampering (someone injecting "success": true via a local proxy)
  3. Obfuscate your code to make it harder to find and bypass the verification logic
  4. Embed verification deep in your application flow, not in a single easily-patchable function
  5. Combine with integrity checks to detect binary modification
  6. Do not store the encryption key as a visible string literal

Graceful Degradation

Handle network failures gracefully:
async function verifyWithFallback(licenseKey, hwid) {
  try {
    const result = await verifyLicense(licenseKey, hwid);
    if (result) {
      // Cache the result locally
      saveToCache(licenseKey, result);
    }
    return result;
  } catch (networkError) {
    // Network failure — check local cache
    const cached = loadFromCache(licenseKey);
    if (cached && cached.status === "active") {
      console.log("Using cached license validation");
      return cached;
    }
    // No cache available — block or allow with warning
    return null;
  }
}

Managing Licenses in the Dashboard

From your store dashboard under License Keys, you can:
  • View all issued licenses with search and filters
  • Create manual licenses (for testing, promotional use)
  • Revoke, suspend, or reactivate individual licenses
  • Reset device activations (frees all slots)
  • View activation history and verification logs

License Lifecycle

EventEffect
Customer purchasesKey auto-generated and delivered
Order completedLicense activated, expiry set from plan
Order cancelledAll order licenses revoked
Order refundedAll order licenses revoked
Expiry date passesMarked expired (checked hourly)
For complete API reference details, see the Licensing documentation.