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.
/api/v1/validateRequest
{
"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
| Field | Type | Required | Description |
|---|---|---|---|
| clientId | string | Yes | Your project's public Client ID (found in dashboard) |
| licenseKey | string | Yes | Format: XXXX-XXXX-XXXX-XXXX |
| nonce | string | No | Random string (max 64 chars). Echoed back in the signed response to prevent replay attacks. |
| fingerprint.cpu | string | No | CPU identifier string |
| fingerprint.mac | string[] | No | Array of MAC addresses |
| fingerprint.disk | string | No | Disk serial number |
| fingerprint.motherboard | string | No | Motherboard serial |
| fingerprint.bios | string | No | BIOS serial |
| fingerprint.uuid | string | No | System UUID |
| fingerprint.gpu | string | No | GPU identifier string |
| fingerprint.ip | string | No | Client 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
{
"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: 18msResponse Fields
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the license is valid |
| licenseId | string | UUID of the license |
| expiresAt | string | null | ISO 8601 expiration datetime, null if never expires |
| timeLeft | number | null | Seconds remaining until expiry. Null if no expiration. 0 if expired. |
| productName | string | Name of the product |
| signedAt | string | ISO 8601 timestamp of when the response was signed |
| nonce | string | Echoed nonce from request (if provided). Verify this matches to prevent replays. |
| signature | string | Base64-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.
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
{
"success": false,
"error": {
"code": "LICENSE_NOT_FOUND",
"message": "License not found: A3F9-KM7X-P2VB-8NQT"
}
}{
"success": false,
"error": {
"code": "LICENSE_INACTIVE",
"message": "License is suspended"
}
}{
"success": false,
"error": {
"code": "LICENSE_EXPIRED",
"message": "License has expired"
}
}{
"success": false,
"error": {
"code": "MAX_ACTIVATIONS_REACHED",
"message": "Maximum activations reached"
}
}{
"success": false,
"error": {
"code": "IP_MISMATCH",
"message": "IP address mismatch — license is IP-locked"
}
}{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again at 2026-01-01T12:00:00.000Z"
}
}{
"success": false,
"error": {
"code": "PROJECT_NOT_FOUND",
"message": "Invalid client ID - project not found"
}
}{
"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