API Documentation
Complete reference for the License Validator API
License Validation
/api/validateValidate a license key with device fingerprint
Request Body
{
"licenseKey": "XXXX-XXXX-XXXX-XXXX",
"fingerprint": "unique-device-identifier"
}Success Response (200)
{
"valid": true,
"license": {
"name": "Customer License",
"expiresAt": "2025-12-31T00:00:00.000Z"
}
}Error Response
{
"valid": false,
"error": "License not found"
}Common Error Messages
- 404License not found
- 403License is disabled
- 403License has expired
- 403Maximum activations reached
Quick Test with cURL
curl -X POST https://yourdomain.com/api/validate \
-H "Content-Type: application/json" \
-d '{
"licenseKey": "YOUR-LICENSE-KEY-HERE",
"fingerprint": "test-device-123"
}'Offline License Validation
Validate licenses without internet connectivity using cryptographic signatures and hardware binding.
What is Offline Validation?
Offline validation allows your application to verify license keys locally without an internet connection. Each offline key contains cryptographically signed license data that can be verified client-side.
- No internet required for validation
- Hardware-bound at generation time
- Cryptographic HMAC-SHA256 signatures
- Works in air-gapped environments
Online vs Offline Validation
| Feature | Online | Offline |
|---|---|---|
| Internet Required | Yes | No |
| Hardware Binding | At first activation | At generation |
| Revocation | Immediate | Only at expiration |
| Speed | 50-300ms | <1ms |
| Use Case | Connected apps | Air-gapped systems |
Getting Offline License Keys
/api/licenses/[id]/offlineRetrieve offline key and verification secret
Success Response
{
"offlineLicenseKey": "eyJsaWQ...-a8f2...",
"boundFingerprint": "abc123...",
"verificationSecret": "def456...",
"format": {
"description": "Offline key format: ENCODED_DATA.SIGNATURE",
"usage": "Validate locally without internet",
"binding": "Bound to device: abc123..."
}
}Client-Side Validation
Use the code samples below to validate offline keys in your application:
JavaScript/Node.js
const validator = new LicenseValidator(
'https://yourapi.com',
'verification-secret-here'
);
const offlineKey = 'eyJsaWQ...-a8f2...';
const result = validator.validateOffline(offlineKey);
if (result.valid) {
console.log('Valid offline license!');
} else {
console.log('Invalid:', result.error);
}💡 Best Practice: Use the hybrid validation approach with validateWithFallback() to try offline first, then fall back to online if offline fails.
Security Considerations
- ✓The verification secret is safe to distribute with your application
- ⚠Offline keys cannot be revoked remotely - they only expire
- ✓Keys are cryptographically bound to device fingerprints
- ✓Tampering with keys causes validation to fail
- ⚠Always set expiration dates for offline licenses
Technical Details
Signature Algorithm
HMAC-SHA256
Encoding
Base64url
Key Length
~40-60 characters
Validation Speed
<1ms (local)
Multi-Product Support
Manage licenses for multiple software products within a single account.
Overview
Multi-product support allows you to organize your licenses by product, making it easier to manage multiple applications or software offerings from a single dashboard.
- Create separate products for each application (Desktop, Mobile, Web)
- Filter licenses by product
- Set a default product for quick access
- Add custom icons and descriptions
Creating a Product
POST /api/products
Content-Type: application/json
{
"name": "My Desktop App",
"description": "Desktop application for Windows and Mac",
"icon": "🚀",
"isDefault": true
}Or create products directly from the dashboard at /products
Creating Licenses for a Product
POST /api/licenses
Content-Type: application/json
{
"name": "Pro License",
"productId": "prod_abc123",
"maxActivations": 5,
"expiresAt": "2027-12-31T23:59:59Z"
}Managing Products
List Products
GET /api/productsUpdate Product
PATCH /api/products/:idDelete Product
DELETE /api/products/:idNote: Cannot delete the default product
💡 Pro Tip: Existing licenses are automatically migrated to a “Default Product” for backward compatibility.
Feature-Based Entitlements
Control which features each license can access with granular feature flags.
Overview
Feature entitlements allow you to create tiered licensing models by enabling or disabling specific features per license. This is perfect for Basic/Pro/Enterprise pricing tiers.
Creating a License with Features
POST /api/licenses
Content-Type: application/json
{
"name": "Pro License",
"features": ["analytics", "export", "api"],
"maxActivations": 5,
"expiresAt": "2027-12-31T23:59:59Z"
}Standard Validation (includes features)
The standard validation endpoint now returns enabled features:
POST /api/validate
Content-Type: application/json
{
"licenseKey": "XXXX-XXXX-XXXX-XXXX",
"fingerprint": "device-id"
}
// Response:
{
"valid": true,
"license": {
"key": "XXXX-XXXX-XXXX-XXXX",
"features": ["analytics", "export", "api"],
"expiresAt": "2027-12-31T23:59:59Z",
...
}
}Feature Validation Endpoint
Check if a license has access to a specific feature:
POST /api/licenses/validate-feature
Content-Type: application/json
{
"licenseKey": "XXXX-XXXX-XXXX-XXXX",
"feature": "analytics",
"fingerprint": "device-id" // optional
}
// Response (has access):
{
"valid": true,
"feature": "analytics",
"hasAccess": true,
"enabledFeatures": ["analytics", "export", "api"]
}
// Response (no access):
{
"valid": true,
"feature": "webhooks",
"hasAccess": false,
"enabledFeatures": ["analytics", "export"]
}Common Features
analyticsAnalytics and reporting
exportData export capabilities
apiAPI access
webhooksWebhook integrations
custom_brandingWhite-label features
priority_supportPriority customer support
Wildcard Access
Grant access to all features (current and future) using the wildcard:
{
"features": ["*"] // Grants all features
}Client-Side Implementation
// Validate license and check features
const response = await fetch('/api/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
licenseKey: 'XXXX-XXXX-XXXX-XXXX',
fingerprint: getDeviceFingerprint()
})
});
const data = await response.json();
if (data.valid) {
const features = data.license.features;
// Enable features based on license
if (features.includes('analytics')) {
enableAnalytics();
}
if (features.includes('export')) {
enableExport();
}
}
// Or check a specific feature
const featureCheck = await fetch('/api/licenses/validate-feature', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
licenseKey: 'XXXX-XXXX-XXXX-XXXX',
feature: 'api'
})
});
const featureData = await featureCheck.json();
if (featureData.hasAccess) {
enableAPIAccess();
}⚠️ Security Note: Always validate features server-side. Never trust client-only feature checks as they can be manipulated.
Feature Naming Best Practices
- Use lowercase with underscores:
api_access - Be descriptive and clear:
priority_support - Avoid spaces, hyphens, or camelCase
- Plan your feature names upfront for consistency
Customer Management
Organize licenses by assigning them to customers or companies.
Benefits
- Track which licenses belong to which customer
- Store customer contact information and notes
- View all licenses assigned to a specific customer
- Organize licenses by company for better management
Creating a Customer
Create a customer profile with company details:
POST /api/customers
Content-Type: application/json
{
"name": "Acme Corporation",
"email": "contact@acme.com",
"contactPerson": "John Doe",
"phone": "+1 (555) 123-4567",
"address": "123 Main St, City, State, ZIP",
"notes": "Enterprise customer - priority support"
}Assigning Licenses
Assign a license to a customer when creating:
POST /api/licenses
Content-Type: application/json
{
"name": "Production License",
"maxActivations": 5,
"customerId": "customer-id-here",
"expiresAt": "2025-12-31T23:59:59Z"
}Or update an existing license:
PATCH /api/licenses
Content-Type: application/json
{
"id": "license-id-here",
"customerId": "customer-id-here"
}Viewing Customer Details
Get a customer with all assigned licenses:
GET /api/customers/{customer-id}
Response:
{
"id": "customer-id",
"name": "Acme Corporation",
"email": "contact@acme.com",
"contactPerson": "John Doe",
"phone": "+1 (555) 123-4567",
"licenses": [
{
"id": "license-id",
"licenseKey": "XXXX-XXXX-XXXX-XXXX",
"name": "Production License",
"isActive": true,
"currentActivations": 2,
"maxActivations": 5
}
]
}Using License Templates
Templates allow you to create reusable license configurations for quick license generation.
What are Templates?
License templates are pre-configured settings that you can reuse to quickly generate multiple licenses with the same properties. Instead of manually entering license details every time, create a template once and use it repeatedly.
Use Cases:
- • Standard customer licenses (e.g., "Professional Plan")
- • Trial licenses with 30-day expiration
- • Enterprise licenses with unlimited activations
- • Different product tiers with varying limits
How Templates Work
- 1Create a Template: Define settings like max activations, expiration days, and metadata. Give it a descriptive name like "Standard Annual License".
- 2Apply Template: Use the template to generate one or more licenses. Each license gets a unique key but shares the template's settings.
- 3Quick Create: From the dashboard, use the "Quick Create from Template" dropdown to instantly generate licenses from your saved templates.
Template Properties
| Property | Description |
|---|---|
| name | Template name (e.g., "Pro Plan Template") |
| maxActivations | Max devices that can use licenses from this template |
| expirationDays | Days until license expires (from creation date) |
| metadata | Custom JSON data (product tier, features, etc.) |
API Examples
Create a Template
POST /api/templates
{
"name": "Professional Annual",
"maxActivations": 5,
"expirationDays": 365,
"metadata": {
"tier": "professional",
"features": ["analytics", "priority-support"]
}
}Generate Licenses from Template
POST /api/templates/{templateId}/apply
{
"name": "Acme Corp License",
"count": 10 // Generate 10 licenses
}
// Response: 10 licenses with identical settings but unique keysUsing Webhooks
Webhooks let you receive real-time notifications when license events occur in your account.
💡 New! Check out our comprehensive webhook documentation with integration examples, troubleshooting guides, and best practices. Read the full guide →
What are Webhooks?
Webhooks are HTTP callbacks that notify your server when specific events happen. Instead of constantly polling the API for changes, webhooks push data to your server in real-time when events occur.
Benefits:
- • Real-time event notifications
- • No need to poll the API repeatedly
- • Integrate license events into your own systems
- • Automate workflows based on license activity
How Webhooks Work
- 1Register Your Webhook: Provide a URL where you want to receive notifications. This must be a publicly accessible HTTPS endpoint.
- 2Select Events: Choose which events to subscribe to (e.g., license.validated, license.activated).
- 3Receive Events: When events occur, we send a POST request to your URL with event details and a signature for verification.
- 4Verify & Process: Verify the webhook signature using your secret, then process the event in your application.
Available Events
| Event | When It Fires |
|---|---|
| license.validated | Every time a license is successfully validated |
| license.activated | When a new device activates a license for the first time |
| license.expired | When a license reaches its expiration date |
| license.limit_reached | When a license reaches its maximum activations |
Webhook Security (Signature Verification)
Every webhook request includes an x-webhook-signature header containing an HMAC-SHA256 signature. Always verify this signature to ensure the webhook came from our servers.
// Node.js example
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return digest === signature;
}
// In your webhook endpoint
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhook(payload, signature, YOUR_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook event
console.log('Event:', req.body.event);
console.log('Data:', req.body.data);
res.status(200).send('OK');
});Retry Logic
If your webhook endpoint fails to respond or returns an error, we automatically retry with exponential backoff:
- •Attempt 1: Immediate
- •Attempt 2: After 1 minute
- •Attempt 3: After 5 minutes
- •Attempt 4: After 30 minutes
View delivery logs in the Webhooks page to troubleshoot failed deliveries.
Example Webhook Payload
{
"event": "license.validated",
"timestamp": "2025-01-02T10:30:00.000Z",
"data": {
"licenseKey": "ABCD-1234-EFGH-5678",
"licenseName": "Production License",
"fingerprint": "device-abc-123",
"deviceType": "desktop",
"ipAddress": "192.168.1.100"
}
}Device Management
Track and manage devices that have activated licenses
/api/licenses/[id]/devicesGet all devices for a specific license
{
"devices": [
{
"id": "device-id",
"fingerprint": "unique-fingerprint",
"deviceName": "John's MacBook",
"deviceType": "desktop",
"lastSeenAt": "2025-01-02T10:00:00.000Z",
"isActive": true
}
],
"maxActivations": 5,
"currentActivations": 2
}/api/devices/[activationId]Update device name
// Request
{ "deviceName": "New Device Name" }
// Response
{ "success": true }/api/devices/[activationId]Revoke device access (deactivates the device)
Analytics
Access usage statistics and trends
/api/analytics/validations?range=30dGet validation time series data (30d or 90d)
/api/analytics/activations?range=30dGet activation trends over time
/api/analytics/overview?range=30dGet comprehensive overview including top licenses, success rates, and device distribution
{
"topLicenses": [
{ "name": "License A", "validations": 1250 }
],
"successRate": 98.5,
"totalValidations": 5000,
"deviceDistribution": [
{ "type": "desktop", "count": 150 },
{ "type": "mobile", "count": 75 }
]
}Complete Endpoint Reference
License Validation
/api/validatePublicValidate a license key with device fingerprint
License Management
/api/licensesProtectedGet all licenses for current user
/api/licensesProtectedCreate a new license
/api/licenses/bulkProtectedBulk create licenses or import from CSV
/api/licensesProtectedUpdate license (status, customer assignment)
/api/licenses?id={id}ProtectedDelete a license
/api/licenses/[id]/logsProtectedGet validation logs for a license
/api/licenses/[id]/devicesProtectedGet all devices for a license
Device Management
/api/devices/[activationId]ProtectedUpdate device name
/api/devices/[activationId]ProtectedRevoke device access
Customer Management
/api/customersProtectedGet all customers with license counts
/api/customersProtectedCreate a new customer
/api/customers/[id]ProtectedGet customer details with all assigned licenses
/api/customersProtectedUpdate customer details
/api/customers?id={id}ProtectedDelete customer (unassigns all licenses)
Webhooks
/api/webhooksProtectedGet all webhooks
/api/webhooksProtectedCreate a new webhook
/api/webhooks/[id]/logsProtectedGet webhook delivery logs
/api/webhooks/testProtectedSend a test webhook
Products
/api/productsProtectedGet all products
/api/productsProtectedCreate a new product
/api/products/[id]ProtectedUpdate product details or set as default
/api/products/[id]ProtectedDelete product (cannot delete default product)
Templates
/api/templatesProtectedGet all templates
/api/templatesProtectedCreate a new template
/api/templates/[id]/applyProtectedCreate licenses from template
Analytics
/api/analytics/validationsProtectedGet validation time series data
/api/analytics/activationsProtectedGet activation trends
/api/analytics/overviewProtectedGet comprehensive analytics overview
Email Preferences
/api/email-preferencesProtectedGet email notification preferences
/api/email-preferencesProtectedUpdate email notification preferences
Production Recommendations
For production use, consider implementing rate limiting on the validation endpoint to prevent abuse. Use HTTPS for all API requests to ensure data security.