Complete developer guide for license integration. Validate, protect, and monetize your software across every platform.
Get a validated license in under two minutes. You need two values from your dashboard: an API key and a product key.
1. Create a backend validation endpoint.
const express = require('express');
const { UnifiedLicenseManager } = require('unifiedlicensing-sdk');
const app = express();
app.use(express.json());
app.post('/api/validate-license', async (req, res) => {
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY, // From environment variable
process.env.PRODUCT_KEY
);
const result = await manager.validateLicense(req.body.licenseKey);
res.json(result);
});
app.listen(3000);
2. Call from your frontend — no keys exposed.
async function validateLicense(licenseKey) {
const response = await fetch('/api/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
const result = await response.json();
if (result.isValid) {
console.log('License valid!', result.licenseInfo);
} else {
console.log('Invalid:', result.error);
}
}
For desktop apps (.NET, Java, Electron) or server-side Node.js, you can use the SDK directly since the code never runs in a browser.
const { UnifiedLicenseManager } = require('unifiedlicensing-sdk');
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY,
process.env.PRODUCT_KEY
);
const result = await manager.validateLicense('LICENSE-KEY-HERE');
if (result.isValid) {
console.log('License valid!', result.licenseInfo);
}
// Server-side only — API key never leaves your server
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY, // API key from env var
'your-product-key', // Product key
'hybrid', // 'hybrid' | 'online' | 'offline'
'web_client' // Platform type (auto-detected if omitted)
);
// Server-side only
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY,
'your-product-key',
{
mode: 'hybrid',
platformType: 'web_client',
baseUrl: 'https://api.unifiedlicensing.com/api/v1',
vendorPublicKey: '-----BEGIN...' // Optional: signature verification
}
);
// Client-side — NO API KEY, just calls your backend
async function validateLicense(licenseKey) {
const response = await fetch('/api/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
return response.json();
}
// Your backend endpoint handles the SDK + API key
| Mode | Behavior | Recommended For |
|---|---|---|
hybrid | Offline-first. Validates from local cache, re-checks server in background every 24h. | Desktop apps, apps with unreliable internet |
online | Always calls the server. No offline support. | Web apps, SaaS products |
offline | Validates only from encrypted local cache. Never contacts server. | Air-gapped environments |
| Type | Use Case | Fingerprint |
|---|---|---|
web_client | Browser-based apps (React, Vue, vanilla JS) | Canvas, WebGL, Audio context |
web_server | Server-side (Node.js, PHP, Python) | IP, Headers, TLS fingerprint |
desktop | Native desktop apps (.NET, Java, Electron) | CPU, MAC address, motherboard |
mobile | Mobile apps (React Native, Flutter) | Device ID, App signature |
fetch(). The SDK is only needed server-side (Node.js, PHP, Python, .NET). For browser apps, use the API directly.
manager.validateLicense(key).fetch('/api/validate-license', ...). Your backend handles the SDK.
// Server-side — API key in environment variable
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY,
process.env.PRODUCT_KEY
);
const result = await manager.validateLicense('LICENSE-KEY');
if (result.isValid) {
const license = result.licenseInfo;
console.log('Tier:', license.tier);
console.log('Features:', license.features);
console.log('Platform:', result.platform);
console.log('Expires:', license.expiresAt);
} else {
console.error('Validation failed:', result.error);
}
// Client-side — no API key, just license key
async function validateLicense(licenseKey) {
const response = await fetch('/api/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
const result = await response.json();
if (result.isValid) {
console.log('License valid!', result.licenseInfo);
} else {
console.log('Invalid:', result.error);
}
}
// Platform is auto-detected, but you can override:
const result = await manager.validateLicense('LICENSE-KEY', {
platform: 'web_client'
});
// Success
{
"isValid": true,
"licenseInfo": {
"licenseKey": "ABC-123-DEF",
"tier": "Professional",
"features": ["advanced_analytics", "priority_support"],
"maxActivations": 5,
"currentActivations": 1,
"isActive": true,
"expiresAt": "2025-12-31T00:00:00Z",
"platforms": ["web_client", "desktop"]
},
"platform": "web_client"
}
// Failure
{
"isValid": false,
"error": "License not found"
}
In hybrid mode, the SDK first checks an encrypted local cache. If valid, it returns immediately and re-validates with the server in the background.
// v3.0 hybrid validation flow:
// 1. Load encrypted license from local storage
// 2. Verify RSA signature (if vendorPublicKey provided)
// 3. Check expiry (with grace period)
// 4. Check hardware binding
// 5. Check revoked status
// 6. If all pass, return cached result
// 7. Background: re-validate with server if 24h+ since last check
const result = await manager.validateLicense('LICENSE-KEY');
// result.offlineMode === true if served from cache
// Force offline-only validation (v3.0 only)
const result = await manager.validateOffline();
// Returns cached license without contacting server
POST /api/v1/start-trial and POST /api/v1/check-trial. See the platform guides for SDK-specific examples.
// Start trial (v3.0 only)
const result = await manager.startTrial();
if (result.success) {
console.log('Trial Token:', result.trial_token);
console.log('Expires:', result.expires_at);
localStorage.setItem('trial_token', result.trial_token);
}
// Check trial status (v3.0 only)
const result = await manager.checkTrial(trialToken);
if (result.valid) {
console.log('Days remaining:', result.remaining_days);
console.log('Expires:', result.expires_at);
} else {
console.log('Trial expired or invalid');
}
For maximum security, validate trials from your backend. The trial token and device fingerprint are verified server-side.
const express = require('express');
const { UnifiedLicenseManager } = require('unifiedlicensing-sdk');
const app = express();
app.use(express.json());
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY,
process.env.PRODUCT_KEY
);
app.post('/api/validate-trial', async (req, res) => {
const { trialToken, fingerprint } = req.body;
const result = await manager.checkTrial(trialToken);
if (result.valid) {
req.session.trial_valid = true;
req.session.trial_expires = result.expires_at;
}
res.json(result);
});
// Client-side (no API key exposed)
async function validateTrial(trialToken) {
const response = await fetch('/api/validate-trial', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ trialToken })
});
const result = await response.json();
if (result.valid) {
localStorage.setItem('trial_days', result.remaining_days);
return true;
} else {
alert('Trial is no longer valid');
return false;
}
}
There are two ways to send analytics: the SDK's built-in tracking methods for trial events, and the direct Telemetry API for custom events.
UnifiedLicenseManager instance and call the /api/v1/trial/track endpoint.
// Record which features the user is trialing
await manager.trackTrialStart(['feature_a', 'feature_b', 'feature_c']);
// Returns true on success, false on error
// Track specific feature interactions
await manager.trackFeatureUsage('export_data', {
format: 'pdf',
size_mb: 2.5,
duration_ms: 1500
});
// Returns true on success, false on error
// Record when a trial converts to a paid plan
await manager.trackTrialConversion(99.99, 'Professional');
// Returns true on success, false on error
POST /api/v1/telemetry. Use this for custom events beyond trial tracking — app launches, feature usage, errors, etc. Requires your vendor API key in the header.
| Method | URL | Auth |
|---|---|---|
POST | /api/v1/telemetry | x-vendor-api-key header |
{
"license_key": "LICENSE-KEY-HERE", // or trial_token
"trial_token": "TRIAL-TOKEN-HERE", // or license_key
"event_type": "app_started",
"event_data": {
"version": "1.0.0",
"platform": "web_client",
"session_id": "session_123"
}
}
async function sendTelemetry(eventType, eventData) {
await fetch('/api/telemetry', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-vendor-api-key': VENDOR_API_KEY // From your backend, not client
},
body: JSON.stringify({
license_key: licenseKey,
event_type: eventType,
event_data: eventData
})
});
}
// Usage
sendTelemetry('app_started', { version: '1.0.0' });
sendTelemetry('feature_used', { feature: 'export', format: 'pdf' });
sendTelemetry('game_level_complete', { level: 5, score: 1250 });
// Backend endpoint — keeps API key secure
app.post('/api/telemetry', async (req, res) => {
const response = await fetch('https://api.unifiedlicensing.com/api/v1/telemetry', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-vendor-api-key': process.env.VENDOR_API_KEY
},
body: JSON.stringify(req.body)
});
const data = await response.json();
res.json(data);
});
// Client-side call — no API key exposed
async function sendTelemetry(eventType, eventData) {
await fetch('/api/telemetry', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
license_key: licenseKey,
event_type: eventType,
event_data: eventData
})
});
}
| Method | Source | Event Type | Parameters |
|---|---|---|---|
trackTrialStart | SDK | trial_started | features: string[] |
trackFeatureUsage | SDK | feature_used | featureName: string, metadata: object |
trackTrialConversion | SDK | trial_converted | conversionValue: number, licenseTier: string |
POST /api/telemetry | API | Any custom event | license_key, event_type, event_data |
// Start automatic heartbeat (fires immediately, then on interval)
manager.startAutoHeartbeat(licenseKey, 'weekly', trialToken);
// Stop heartbeat
manager.stopAutoHeartbeat();
// Send heartbeat manually (rate-limited to once per 24 hours)
const result = await manager.sendHeartbeat(licenseKey, trialToken);
if (result.cached) {
console.log('Using cached response');
console.log('Next allowed:', result.next_allowed);
} else {
console.log('Fresh heartbeat');
console.log('Commands:', result.commands);
}
{
"success": true,
"cached": false,
"commands": [
{
"action": "revoke",
"message": "License revoked"
}
],
"next_allowed": "2024-01-02T12:00:00Z"
}
// Backend (Node.js) - SECURE
app.post('/api/validate-license', async (req, res) => {
const manager = new UnifiedLicenseManager(
process.env.VENDOR_API_KEY, // From environment variable
process.env.PRODUCT_KEY
);
const result = await manager.validateLicense(req.body.licenseKey);
res.json(result);
});
// Client-side - NO KEYS EXPOSED
async function validateLicense(licenseKey) {
const response = await fetch('/api/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
return response.json();
}
# .env file (NEVER commit to git!)
VENDOR_API_KEY=your-secret-vendor-key
PRODUCT_KEY=your-product-key
Encrypt all data in transit between client and server
Prevent brute force attacks on your validation endpoint
Restrict API access to your server's IP address
Regularly change your API keys (quarterly recommended)
Alert on unusual API activity or usage spikes
Restrict cross-origin requests to trusted domains
Step-by-step integration guides for every platform. Each guide includes complete code examples, best practices, and platform-specific considerations.
Vanilla JS, fetch API, localStorage caching, canvas fingerprinting
Composition API, Pinia store, Nuxt SSR, router guards
WinForms, WPF, MAUI, IntegrityGuard, encrypted storage
Flask, Django, FastAPI, desktop apps, keyring storage
Raw PHP, Laravel, WordPress, server-side validation
WooCommerce integration, admin dashboard, REST API
Activity integration, EncryptedSharedPreferences, Play Store rules
SwiftUI & UIKit, Keychain storage, App Store compliance
Expo & bare workflow, AsyncStorage, cross-platform
Dart, flutter_secure_storage, Material Design UI
Manifest V3, service worker, chrome.storage
Canvas, WebGL, Phaser, Three.js integration
| Method | SDK | Description | Returns |
|---|---|---|---|
constructor(apiKey, productKey, ...) | All SDKs | Initialize SDK | UnifiedLicenseManager |
validateLicense(key, opts?) | All SDKs | Validate a license key | Promise<{isValid, licenseInfo?, error?}> |
validateOffline() | Desktop | Validate from local cache only | Promise<{isValid, licenseInfo?, error?}> |
activateLicense(key, deviceInfo?) | Desktop | Activate license on device | Promise<{success, activation_id?}> |
startTrial(productKey?) | All SDKs | Start a trial period | Promise<{success, trial_token?}> |
checkTrial(trialToken) | All SDKs | Check trial status | Promise<{valid, remaining_days?}> |
trackTrialStart(features) | All SDKs | Record trial feature usage | Promise<boolean> |
trackFeatureUsage(name, meta?) | All SDKs | Track feature interaction | Promise<boolean> |
trackTrialConversion(value, tier) | All SDKs | Record trial-to-paid conversion | Promise<boolean> |
sendHeartbeat(key, trialToken?) | All SDKs | Send heartbeat (24h rate limit) | Promise<{success, commands?}> |
startAutoHeartbeat(key, interval, trialToken?) | All SDKs | Start automatic heartbeat | void |
stopAutoHeartbeat() | All SDKs | Stop automatic heartbeat | void |
loadProductConfig() | All SDKs | Load product configuration | Promise<object> |
loadTiers() | All SDKs | Load tier definitions | Promise<object> |
isFeatureAvailable(name, license?) | All SDKs | Check if feature is in tier | Promise<boolean> |
getPlatformInfo() | All SDKs | Get platform and fingerprint info | object |
clearCache() | Desktop | Clear cached license data | Promise<void> |
| Property | Type | Description |
|---|---|---|
licenseKey | string | The license key |
tier | string | License tier name |
features | string[] | Available features for this tier |
maxActivations | number | Maximum allowed activations |
currentActivations | number | Current activation count |
isActive | boolean | Whether license is currently active |
expiresAt | string | Expiration date (ISO 8601) |
platform | string | Platform this license was validated on |
platforms | string[] | Supported platforms |
| Error | Description |
|---|---|
License not found | License key does not exist |
License expired | License has passed its expiration date |
License revoked | License has been revoked by the vendor |
Maximum activations reached | Device limit has been exceeded |
Platform not supported | License does not cover this platform |
HARDWARE_MISMATCH | Device fingerprint does not match |
INVALID_SIGNATURE | License signature verification failed |
OFFLINE_NO_LICENSE | No cached license available offline |
NETWORK_ERROR | Server unreachable |
using UnifiedLicensing;
public class LicenseManager
{
private readonly UnifiedLicenseManager _manager;
public LicenseManager()
{
_manager = new UnifiedLicenseManager(
"ul_YOUR_API_KEY", // From environment variable
"YOUR_PRODUCT_KEY"
);
}
public async Task<bool> ValidateApplicationLicense()
{
var savedLicense = Properties.Settings.Default.LicenseKey;
if (!string.IsNullOrEmpty(savedLicense))
{
var result = await _manager.ValidateLicenseAsync(savedLicense);
if (result.IsValid)
{
UpdateLicenseInfo(result);
return true;
}
}
var trialStatus = _manager.GetTrialStatus();
if (trialStatus.IsValid)
{
ShowTrialDialog(trialStatus.RemainingDays);
return true;
}
ShowLicenseDialog();
return false;
}
public async Task<bool> ActivateLicense(string licenseKey)
{
var result = await _manager.ValidateLicenseAsync(licenseKey);
if (result.IsValid)
{
Properties.Settings.Default.LicenseKey = licenseKey;
Properties.Settings.Default.Save();
UpdateLicenseInfo(result);
return true;
}
MessageBox.Show($"License activation failed: {result.ErrorMessage}");
return false;
}
}
import React, { useState, useEffect } from 'react';
const LicenseProvider = ({ children }) => {
const [isLicensed, setIsLicensed] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
checkLicenseStatus();
}, []);
const checkLicenseStatus = async () => {
const savedLicense = localStorage.getItem('app_license');
if (savedLicense) {
const response = await fetch('/api/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey: savedLicense })
});
const result = await response.json();
if (result.isValid) {
setIsLicensed(true);
}
}
setLoading(false);
};
if (loading) return <div>Loading...</div>;
if (!isLicensed) return <LicensePrompt />;
return <div>{children}</div>;
};
// Client-side game licensing via backend proxy
const API = 'https://api.unifiedlicensing.com/api/v1';
class GameLicensing {
constructor() {
this.sessionId = 'session_' + Date.now();
}
async validateLicense(licenseKey) {
const response = await fetch('/api/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
const result = await response.json();
if (result.isValid) {
localStorage.setItem('game_license', licenseKey);
this.unlockGame();
}
}
async startTrial() {
const response = await fetch('/api/start-trial', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
const result = await response.json();
if (result.success) {
localStorage.setItem('trial_token', result.trial_token);
this.unlockGame();
}
}
async checkTrial() {
const token = localStorage.getItem('trial_token');
if (!token) return false;
const response = await fetch('/api/check-trial', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ trialToken: token })
});
const result = await response.json();
return result.valid;
}
unlockGame() {
document.getElementById('game').style.display = 'block';
document.getElementById('license-prompt').style.display = 'none';
}
}
window.onload = () => new GameLicensing();
Our support team is here to help you integrate Unified Licensing.