License Validation

The core validation endpoint. This is the hot path, optimized for sub-50ms response times. All responses are cryptographically signed with Ed25519 for tamper-proof verification.

POST/api/v1/validate

Request

Request bodyjson
{
  "clientId": "proj_aBcDeFgHiJkLmNoPqRsTuVwX",
  "licenseKey": "A3F9-KM7X-P2VB-8NQT",
  "fingerprint": {
    "cpu": "Intel Core i7-12700K",
    "mac": ["00:11:22:33:44:55"],
    "disk": "S5GXNG0N123456",
    "motherboard": "MS-7D25",
    "bios": "BIOS-V1.40",
    "uuid": "4C4C4544-0042-4810-8053-C2C04F305132",
    "gpu": "NVIDIA GeForce RTX 4090"
  },
  "nonce": "a1b2c3d4e5f6789012345678abcdef01"
}

Parameters

FieldTypeRequiredDescription
clientIdstringYesYour project's public Client ID (found in dashboard)
licenseKeystringYesFormat: XXXX-XXXX-XXXX-XXXX
noncestringNoRandom string (max 64 chars). Echoed back in the signed response to prevent replay attacks.
fingerprint.cpustringNoCPU identifier string
fingerprint.macstring[]NoArray of MAC addresses
fingerprint.diskstringNoDisk serial number
fingerprint.motherboardstringNoMotherboard serial
fingerprint.biosstringNoBIOS serial
fingerprint.uuidstringNoSystem UUID
fingerprint.gpustringNoGPU identifier string
fingerprint.ipstringNoClient IP address. Auto-populated by the server if not provided.

All fingerprint fields are individually optional, but at least one component must be provided.

Response

200 OK - Valid license (signed)json
{
  "success": true,
  "data": {
    "valid": true,
    "licenseId": "550e8400-e29b-41d4-a716-446655440000",
    "expiresAt": "2026-12-31T23:59:59.000Z",
    "timeLeft": 31536000,
    "productName": "My App",
    "signedAt": "2026-02-12T10:30:00.000Z",
    "nonce": "a1b2c3d4e5f6789012345678abcdef01",
    "signature": "base64-encoded-ed25519-signature..."
  }
}

// Response headers
X-Response-Time: 18ms

Response Fields

FieldTypeDescription
validbooleanWhether the license is valid
licenseIdstringUUID of the license
expiresAtstring | nullISO 8601 expiration datetime, null if never expires
timeLeftnumber | nullSeconds remaining until expiry. Null if no expiration. 0 if expired.
productNamestringName of the product
signedAtstringISO 8601 timestamp of when the response was signed
noncestringEchoed nonce from request (if provided). Verify this matches to prevent replays.
signaturestringBase64-encoded Ed25519 signature over the response payload. Verify with your project's public key.

Signature Verification

Every response is signed with your project's Ed25519 private key. Your public key is available in the dashboard. To verify, reconstruct the canonical payload (all fields except signature) as JSON and verify against the signature using Ed25519. See Security Best Practices for details.

Code Examples

Integrate license validation with signature verification into your application. Replace YOUR_PROJECT_PUBLIC_KEY with the base64 public key from your dashboard.

JavaScriptjavascript
import crypto from 'crypto';

const PUBLIC_KEY = 'YOUR_PROJECT_PUBLIC_KEY'; // Base64 DER from dashboard

async function validateLicense(clientId, licenseKey, fingerprint) {
  const nonce = crypto.randomBytes(16).toString('hex');

  const response = await fetch('https://onyxauth.xyz/api/v1/validate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ clientId, licenseKey, fingerprint, nonce })
  });

  const { success, data, error } = await response.json();
  if (!success) {
    console.error('Validation failed:', error.code);
    return false;
  }

  // Verify the Ed25519 signature
  const payload = JSON.stringify({
    licenseId: data.licenseId,
    valid: data.valid,
    expiresAt: data.expiresAt,
    timeLeft: data.timeLeft,
    productName: data.productName,
    signedAt: data.signedAt,
    nonce: data.nonce,
  });

  const key = crypto.createPublicKey({
    key: Buffer.from(PUBLIC_KEY, 'base64'),
    format: 'der',
    type: 'spki',
  });
  const valid = crypto.verify(null, Buffer.from(payload), key, Buffer.from(data.signature, 'base64'));

  if (!valid || data.nonce !== nonce) {
    console.error('Signature verification failed - possible tampering');
    return false;
  }

  return data.valid;
}

Error Responses

404 - License not foundjson
{
  "success": false,
  "error": {
    "code": "LICENSE_NOT_FOUND",
    "message": "License not found: A3F9-KM7X-P2VB-8NQT"
  }
}
403 - License inactivejson
{
  "success": false,
  "error": {
    "code": "LICENSE_INACTIVE",
    "message": "License is suspended"
  }
}
403 - License expiredjson
{
  "success": false,
  "error": {
    "code": "LICENSE_EXPIRED",
    "message": "License has expired"
  }
}
403 - Max activations reachedjson
{
  "success": false,
  "error": {
    "code": "MAX_ACTIVATIONS_REACHED",
    "message": "Maximum activations reached"
  }
}
403 - IP mismatch (IP-locked license)json
{
  "success": false,
  "error": {
    "code": "IP_MISMATCH",
    "message": "IP address mismatch — license is IP-locked"
  }
}
429 - Rate limitedjson
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Try again at 2026-01-01T12:00:00.000Z"
  }
}
404 - Invalid client IDjson
{
  "success": false,
  "error": {
    "code": "PROJECT_NOT_FOUND",
    "message": "Invalid client ID - project not found"
  }
}
403 - License belongs to another projectjson
{
  "success": false,
  "error": {
    "code": "LICENSE_PROJECT_MISMATCH",
    "message": "License does not belong to this project"
  }
}

Hardware Fingerprint Matching

The system uses moderate tolerance matching to prevent false rejections from minor hardware changes:

  • With 4+ matching components: requires 75% to match
  • With 1-3 matching components: allows at most 1 mismatch
  • Components are compared case-insensitively; the full fingerprint is hashed with BLAKE3 for storage
  • MAC addresses are compared as sets, so any overlap counts as a match

Validation Flow

1Rate limit check (Redis, <5ms)
2Cache lookup (Redis, <10ms), 85%+ hit rate
3If cache miss: database query (<50ms)
4Status & expiration checks
5Hardware fingerprint validation (tolerance matching)
6Ed25519 signature applied & response returned