Skip to main content

Validate License

Validate a license artifact and check its current status.


Endpoint

POST /api/v1/v2/validate

Request

Headers

Authorization: Bearer ql_your_api_key
Content-Type: application/json

Body

Provide either artifact_compact or artifact_json:

{
"artifact_compact": "eyJ0eXAiOiAiUUwtTElDRU5TRSIsIC...",
"device_fingerprint": {
"cpu_id": "Intel-Core-i9-12900K",
"disk_serial": "WD-ABC123456",
"mac_address": "00:1A:2B:3C:4D:5E"
}
}

Or with full JSON:

{
"artifact_json": "{\"header\":{...},\"payload\":{...},\"signatures\":{...}}",
"device_fingerprint": null
}

Parameters

ParameterTypeRequiredDescription
artifact_compactstringCompact license token (preferred)
artifact_jsonstringFull JSON artifact
device_fingerprintobjectDevice properties for binding verification

⚡ One of artifact_compact or artifact_json is required.


Response

Success - Valid License (200 OK)

{
"is_valid": true,
"status": "valid",
"error": null,
"error_message": null,
"license_id": "b28a923a-c747-49b2-bc90-d87004c10379",
"entitlements": [
"feature:api",
"feature:export",
"seats:max"
],
"expires_at": "2026-12-30T15:30:00Z",
"in_grace_period": false,
"binding_verified": true,
"revocation_epoch": 5
}

Success - Invalid License (200 OK)

{
"is_valid": false,
"status": "expired",
"error": "EXPIRED",
"error_message": "License expired at 2025-01-15T00:00:00Z",
"license_id": "b28a923a-c747-49b2-bc90-d87004c10379",
"entitlements": [],
"expires_at": "2025-01-15T00:00:00Z",
"in_grace_period": false,
"binding_verified": false,
"revocation_epoch": 5
}

Response Fields

FieldTypeDescription
is_validbooleanWhether the license is currently valid
statusstringLicense status code
errorstringError code if invalid
error_messagestringHuman-readable error message
license_idstringUnique license identifier (JTI)
entitlementsarrayList of active entitlement IDs
expires_atdatetimeLicense expiration timestamp
in_grace_periodbooleanWhether license is in grace period
binding_verifiedbooleanWhether device binding was verified
revocation_epochintegerCurrent revocation epoch

Status Codes

StatusDescriptionis_valid
validLicense is active and validtrue
grace_periodExpired but within grace periodtrue
expiredPast expiration and grace periodfalse
revokedLicense has been revokedfalse
not_yet_validBefore the nbf (not before) datefalse
binding_mismatchDevice fingerprint doesn't matchfalse
signature_invalidCryptographic signature failedfalse

Error Codes

ErrorDescription
EXPIREDLicense has expired
REVOKEDLicense was administratively revoked
NOT_YET_VALIDLicense activation date is in the future
BINDING_MISMATCHDevice fingerprint doesn't match license
SIGNATURE_INVALIDRSA or ML-DSA signature verification failed
EPOCH_ROLLBACKRevocation epoch is older than minimum

Examples

Basic Validation

curl -X POST "https://quantumlock.softquantus.com/api/v1/v2/validate" \
-H "Authorization: Bearer ql_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"artifact_compact": "eyJ0eXAiOiAiUUwtTElDRU5TRSIsIC..."
}'

Validation with Device Binding

curl -X POST "https://quantumlock.softquantus.com/api/v1/v2/validate" \
-H "Authorization: Bearer ql_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"artifact_compact": "eyJ0eXAiOiAiUUwtTElDRU5TRSIsIC...",
"device_fingerprint": {
"cpu_id": "Intel-Core-i9-12900K",
"motherboard_serial": "MS-12345",
"disk_serial": "WD-ABC123456"
}
}'

Python SDK Example

from quantumlock_sdk import QuantumLockClient

client = QuantumLockClient(api_key="ql_your_api_key")

result = client.validate(
artifact_compact=stored_license_token,
device_fingerprint={
"cpu_id": get_cpu_id(),
"disk_serial": get_disk_serial()
}
)

if result.is_valid:
print(f"✅ License valid until {result.expires_at}")

if result.in_grace_period:
print("⚠️ License is in grace period - please renew!")

# Check specific entitlements
if "feature:api" in result.entitlements:
enable_api_access()
else:
print(f"❌ License invalid: {result.error_message}")

match result.error:
case "EXPIRED":
show_renewal_dialog()
case "REVOKED":
show_revoked_message()
case "BINDING_MISMATCH":
show_device_mismatch_error()

Best Practices

1. Cache Validation Results

Don't validate on every operation. Cache results for a reasonable period:

import time

CACHE_DURATION = 300 # 5 minutes

cached_result = None
cache_time = 0

def check_license(token):
global cached_result, cache_time

if cached_result and time.time() - cache_time < CACHE_DURATION:
return cached_result

cached_result = client.validate(artifact_compact=token)
cache_time = time.time()
return cached_result

2. Handle Grace Periods

Allow continued access during grace period but notify users:

if result.is_valid and result.in_grace_period:
days_left = (result.grace_until - datetime.now()).days
show_notification(f"License expires in {days_left} days. Please renew.")

3. Offline Fallback

Use StatusProofs for offline validation. See Status Proof.


Error Responses

400 Bad Request

{
"detail": "Must provide artifact_json or artifact_compact"
}

400 Parse Error

{
"detail": "Failed to parse artifact: Invalid base64 encoding"
}