Docs / License API / Activate and Verify
License API: Activate and Verify
Public integration guide for third-party applications. Includes parameter mapping, canonical signing rules, anti-replay requirements, and response behavior.
Endpoints
GET /api/license/activateandPOST /api/license/activateGET /api/license/verifyandPOST /api/license/verify- GET support may be enabled or disabled by server configuration.
Parameter mapping
Use the public key (pk_test_... or pk_live_...). These endpoints accept only public keys—safe for client apps that may be distributed publicly. Pass it via X-Api-Key, Authorization: Bearer, or body/query (apiKey, ak, key).
Activate fields
Required canonical: licenseKey, fingerprint, machineId, username, ts, nonce, sig
Legacy aliases: lk, fp, m, un, signature
Verify fields
Required canonical: licenseKey, hash, username, ts, nonce, sig
Legacy aliases: lk, h, un, signature
hash is required for verify; raw fingerprint and machineId are not used directly.
Field-by-field guide (beginner)
Each field explained. Example activate request:
{
"lk": "lic_7h3k9p2r4t6v8x1z",
"fp": "deviceFingerprint",
"m": "cpuOrMachineId",
"un": "john.doe",
"ts": "1739160000",
"nonce": "4f8f8f30e5ca4f5ab560f95c7f8f5301",
"sig": "<computedSig>"
}lk (licenseKey)
The license key string the customer received after purchase. Unique per license. Example: lic_7h3k9p2r4t6v8x1z.
fp (fingerprint)
A stable identifier for the device. Create it by hashing hardware/OS info (e.g. CPU ID, MAC address, disk serial, hostname). Must be the same on activate and verify. Example: hash CPU + MAC + hostname with SHA256 and take first 32 chars, or use a library like node-machine-id / fingerprint2.
m (machineId)
A stable identifier for the machine. Often derived from hardware (e.g. motherboard serial, disk UUID). Must be the same on activate and verify. Can be combined with fingerprint or kept separate for multi-machine detection.
un (username)
The user identifier (e.g. login name, email). Used to bind the license to a user. Must match between activate and verify.
ts (timestamp)
Unix timestamp in seconds when the request was created. Use Math.floor(Date.now() / 1000) or equivalent. Server rejects requests older than ~5 minutes to prevent replay.
nonce
A random one-time value (e.g. 32 hex chars). Generate fresh for every request. Never reuse. Example: crypto.randomBytes(16).toString("hex").
sig (signature)
HMAC-SHA256 hex of the signed payload. See below for what to sign.
What to sign
Build a string: METHOD + "\n" + PATH + "\n" + ts + "\n" + nonce + "\n" + canonicalBody. Keys in canonical body must be sorted alphabetically; values URL-encoded. Then: sig = hmac_sha256_hex(apiKey, payload) (use your public key as the HMAC secret).
Example for activate:
POST
/api/license/activate
1739160000
4f8f8f30e5ca4f5ab560f95c7f8f5301
fingerprint=deviceFingerprint&licenseKey=lic_7h3k9p2r4t6v8x1z&machineId=cpuOrMachineId&username=john.doeVerify: what to hash
For verify, do not send fp and m. Instead, compute hash = sha256_hex(fingerprint + machineId + username) (concatenate as strings, then SHA256, output hex). Use the same fingerprint, machineId, and username from activation. Send hash in the verify request.
Verify example
{
"lk": "lic_7h3k9p2r4t6v8x1z",
"un": "john.doe",
"hash": "<sha256(fp + m + un) in hex>",
"ts": "1739160060",
"nonce": "8ac32585b8ef4ef2a8d63f5fd8ad6ef0",
"sig": "<computedSig>"
}Activate Endpoint
Activate registers machine and user identity against a license key. Success returns plain text: "License activated successfully", "License key is already activated", or "Max allowed users exceeded".
Required: licenseKey, fingerprint, machineId, username, ts, nonce, sig, and API key. Pass the API key via X-Api-Key header (recommended), Authorization: Bearer, or in body/query as apiKey, ak, or key.
GET with X-Api-Key header:
GET /api/license/activate?lk=lic_7h3k9p2r4t6v8x1z&fp=deviceFingerprint&m=cpuOrMachineId&un=john.doe&ts=1739160000&nonce=4f8f8f30e5ca4f5ab560f95c7f8f5301&sig=<computedSig>
X-Api-Key: <your-api-key>POST with X-Api-Key header:
POST /api/license/activate
Content-Type: application/json
X-Api-Key: <your-api-key>
{
"lk": "lic_7h3k9p2r4t6v8x1z",
"fp": "deviceFingerprint",
"m": "cpuOrMachineId",
"un": "john.doe",
"ts": "1739160000",
"nonce": "4f8f8f30e5ca4f5ab560f95c7f8f5301",
"sig": "<computedSig>"
}Verify Endpoint
Verify checks whether a registered machine and user can still use the license.
Required: licenseKey, hash, username, ts, nonce, sig, and API key. Pass the API key via X-Api-Key header (recommended), Authorization: Bearer, or in body/query as apiKey, ak, or key.
GET with X-Api-Key header:
GET /api/license/verify?lk=lic_7h3k9p2r4t6v8x1z&un=john.doe&hash=<sha256(fp+m+un)>&ts=1739160060&nonce=8ac32585b8ef4ef2a8d63f5fd8ad6ef0&sig=<computedSig>
X-Api-Key: <your-api-key>POST with X-Api-Key header:
POST /api/license/verify
Content-Type: application/json
X-Api-Key: <your-api-key>
{
"lk": "lic_7h3k9p2r4t6v8x1z",
"un": "john.doe",
"hash": "<sha256(fp+m+un)>",
"ts": "1739160060",
"nonce": "8ac32585b8ef4ef2a8d63f5fd8ad6ef0",
"sig": "<computedSig>"
}Signing
Canonical body keys are sorted alphabetically, and values are URL-encoded. The API key is used as the HMAC secret.
fingerprint=<urlencoded(fp)>&licenseKey=<urlencoded(lk)>&machineId=<urlencoded(m)>&username=<urlencoded(un)>hash=<urlencoded(hash)>&licenseKey=<urlencoded(lk)>&username=<urlencoded(un)>METHOD + "\n" + PATH + "\n" + ts + "\n" + nonce + "\n" + canonicalBodySignature: sig = hmac_sha256_hex(apiKey, payload). Use the public key (pk_...) as the HMAC secret. Pass it via X-Api-Key, Authorization: Bearer, or body/query (apiKey, ak, key).
Code examples
Copy-ready examples. Use the public key (pk_...) and implement canonical body ordering for production.
import hmac
import hashlib
import time
import secrets
import urllib.parse
import requests
BASE_URL = "https://your-domain.com"
API_KEY = "pk_live_..." # Use public key (pk_) for client apps
def sign_payload(method: str, path: str, ts: str, nonce: str, body: str) -> str:
payload = f"{method}\n{path}\n{ts}\n{nonce}\n{body}"
return hmac.new(API_KEY.encode(), payload.encode(), hashlib.sha256).hexdigest()
def activate(license_key: str, fingerprint: str, machine_id: str, username: str):
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
body = urllib.parse.urlencode({
"fingerprint": fingerprint,
"licenseKey": license_key,
"machineId": machine_id,
"username": username,
}, quote_via=urllib.parse.quote)
sig = sign_payload("POST", "/api/license/activate", ts, nonce, body)
resp = requests.post(
f"{BASE_URL}/api/license/activate",
json={"lk": license_key, "fp": fingerprint, "m": machine_id, "un": username, "ts": ts, "nonce": nonce, "sig": sig},
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
)
return resp
def verify(license_key: str, username: str, fingerprint: str, machine_id: str):
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
h = hashlib.sha256((fingerprint + machine_id + username).encode()).hexdigest()
body = urllib.parse.urlencode({"hash": h, "licenseKey": license_key, "username": username}, quote_via=urllib.parse.quote)
sig = sign_payload("POST", "/api/license/verify", ts, nonce, body)
resp = requests.post(
f"{BASE_URL}/api/license/verify",
json={"lk": license_key, "un": username, "hash": h, "ts": ts, "nonce": nonce, "sig": sig},
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
)
return resp.json()Security
ts: Unix timestamp in secondsnonce: random one-time token per requestsig: HMAC-SHA256 hex signature over canonical payloadhash(verify only): SHA256 hex offingerprint + machineId + username
Generate fresh ts and nonce on every request. Never reuse nonce across activate/verify or retries.
Security advice
- Use the public key (
pk_...); these endpoints accept only public keys. - Store the public key in env or config; it is safe for client apps but avoid logging it.
- Prefer
X-Api-Keyheader over query/body to avoid leaking in URLs or logs. - Use HTTPS only; never send the key over plain HTTP.
- Rotate keys periodically and revoke compromised keys immediately.
- Keep device fingerprint and machineId hashed or obfuscated where possible.
Errors
Activate errors: JSON { "error": "CODE" }. Verify errors: JSON { "error": true, "status", "message", "errorCode" }. Rate-limited responses include Retry-After header.
Common codes: INVALID_REQUEST, INVALID_JSON, UNSUPPORTED_MEDIA_TYPE, METHOD_NOT_ALLOWED, INVALID_TIMESTAMP, STALE_REQUEST, REPLAY_DETECTED, INVALID_SIGNATURE, INVALID_API_KEY, RATE_LIMITED.
Activate
Success: plain text. Errors: JSON.
{ "error": "INVALID_SIGNATURE" }Verify
{
"isValid": true,
"demo": false,
"error": false,
"expiresInDays": 45
}{
"error": true,
"status": 401,
"message": "Unauthorized",
"errorCode": "INVALID_SIGNATURE"
}