← Back to Guides
UnifiedLicensing

Unified Licensing SDK

Complete developer guide for license integration. Validate, protect, and monetize your software across every platform.

SDK v4.0 Desktop Web Mobile RSA-2048

1 Quick Start

Get a validated license in under two minutes. You need two values from your dashboard: an API key and a product key.

Never expose API keys in client-side code The API key must stay on your server. For web apps, create a backend endpoint that handles validation. The client sends the license key to your backend, your backend calls the Unified Licensing API.

Web App (Backend + Frontend)

1. Create a backend validation endpoint.

server.js
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.

index.html
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);
    }
}

Server-Side / Desktop App

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.

server.js or desktop app
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);
}

2 SDK Setup

Where can I use the SDK directly? Server-side only: Node.js backend, PHP, Python, .NET, Java. The API key stays on your server.
Client-side (browser): Must use a backend proxy. Never put the API key in HTML, JS bundles, or browser code.

Constructor

v2.1 (Positional Params)

// 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)
);

v3.0 (Options Object)

// 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 (Browser) — Backend Proxy Required

frontend.js
// 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

Validation Modes

ModeBehaviorRecommended For
hybridOffline-first. Validates from local cache, re-checks server in background every 24h.Desktop apps, apps with unreliable internet
onlineAlways calls the server. No offline support.Web apps, SaaS products
offlineValidates only from encrypted local cache. Never contacts server.Air-gapped environments

Platform Types

TypeUse CaseFingerprint
web_clientBrowser-based apps (React, Vue, vanilla JS)Canvas, WebGL, Audio context
web_serverServer-side (Node.js, PHP, Python)IP, Headers, TLS fingerprint
desktopNative desktop apps (.NET, Java, Electron)CPU, MAC address, motherboard
mobileMobile apps (React Native, Flutter)Device ID, App signature

Install via NPM

No npm package required for client-side Client-side apps call your backend endpoint directly via fetch(). The SDK is only needed server-side (Node.js, PHP, Python, .NET). For browser apps, use the API directly.

3 License Validation

Server-side SDK vs Client-side proxy Server-side (Node.js, PHP, .NET, Java): Use the SDK directly — manager.validateLicense(key).
Client-side (browser): Call your backend endpoint — fetch('/api/validate-license', ...). Your backend handles the SDK.

Server-Side Validation

server.js
// 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 Validation (via Backend)

frontend.js
// 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-Specific Validation

platform.js
// Platform is auto-detected, but you can override:
const result = await manager.validateLicense('LICENSE-KEY', {
    platform: 'web_client'
});

Response Format

response.json
// 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"
}

v3.0: Offline Validation (Hybrid Mode)

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.

offline-flow.js
// 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

v3.0: Offline Validation Method

offline.js
// Force offline-only validation (v3.0 only)
const result = await manager.validateOffline();
// Returns cached license without contacting server

4 Trial System

Trial endpoints are API-based Start and check trials via POST /api/v1/start-trial and POST /api/v1/check-trial. See the platform guides for SDK-specific examples.

Start a Trial (v3.0)

trial.js
// 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)

check-trial.js
// 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');
}

Server-Side Trial Validation

For maximum security, validate trials from your backend. The trial token and device fingerprint are verified server-side.

api/validate-trial.js
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 Trial Check

client-trial.js
// 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;
    }
}

5 Analytics & Telemetry

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.

SDK Trial Tracking Methods

Built-in SDK methods These methods are available directly on the UnifiedLicenseManager instance and call the /api/v1/trial/track endpoint.

Track Trial Start

tracking.js
// Record which features the user is trialing
await manager.trackTrialStart(['feature_a', 'feature_b', 'feature_c']);
// Returns true on success, false on error

Track Feature Usage

tracking.js
// 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

Track Trial Conversion

tracking.js
// Record when a trial converts to a paid plan
await manager.trackTrialConversion(99.99, 'Professional');
// Returns true on success, false on error

Telemetry API (Direct Endpoint)

Direct API call for custom telemetry events The Telemetry API is at 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.

Telemetry Endpoint

MethodURLAuth
POST/api/v1/telemetryx-vendor-api-key header

Telemetry Request Body

request.json
{
    "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"
    }
}

Calling the Telemetry API

telemetry.js
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 });

Telemetry via Backend Proxy (Recommended)

server.js
// 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
        })
    });
}

Analytics Methods Summary

MethodSourceEvent TypeParameters
trackTrialStartSDKtrial_startedfeatures: string[]
trackFeatureUsageSDKfeature_usedfeatureName: string, metadata: object
trackTrialConversionSDKtrial_convertedconversionValue: number, licenseTier: string
POST /api/telemetryAPIAny custom eventlicense_key, event_type, event_data

6 Heartbeat & Remote Control

Auto-Heartbeat

heartbeat.js
// Start automatic heartbeat (fires immediately, then on interval)
manager.startAutoHeartbeat(licenseKey, 'weekly', trialToken);

// Stop heartbeat
manager.stopAutoHeartbeat();

Manual Heartbeat

heartbeat.js
// 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);
}

Heartbeat Intervals

Heartbeat Response

response.json
{
  "success": true,
  "cached": false,
  "commands": [
    {
      "action": "revoke",
      "message": "License revoked"
    }
  ],
  "next_allowed": "2024-01-02T12:00:00Z"
}

7 Platform Support

All Validations Powered by Unified Licensing API Storage is for caching only. All license and trial validations are powered by the Unified Licensing API — every validation check calls the server, which verifies license status, expiration, and features.

Desktop

  • Type: desktop
  • Frameworks: .NET, Java, C++, Electron, Qt, Unity
  • Validation: Hybrid (online + offline)
  • Fingerprint: Hardware-based (CPU, MAC, motherboard)

Web Client

  • Type: web_client
  • Frameworks: JavaScript, React, Vue, Angular
  • Validation: Online only
  • Fingerprint: Canvas, WebGL, Audio context

Web Server

  • Type: web_server
  • Frameworks: Node.js, PHP, Python, ASP.NET
  • Validation: Server-side
  • Fingerprint: IP, Headers, TLS fingerprint

Mobile

  • Type: mobile
  • Frameworks: React Native, Flutter, Xamarin
  • Validation: Hybrid (recommended)
  • Fingerprint: Device ID, App signature

Validation Flow by Platform

Desktop (Hybrid)
App → Check Local Cache → If expired/missing → Call API → Update Cache → Validate
Web Client (Online)
Browser → Backend Proxy → API → Verify License → Return Result
Web Server (Server-Side)
Request → Backend → API → Verify License → Cache in DB → Return to Client
Mobile (Hybrid)
App → Check Secure Storage → If expired → Call API → Update Storage → Validate

8 Security Best Practices

License Generation on Vendor Dashboard License generation happens on your vendor dashboard (not via API). Attackers cannot create or generate fake licenses programmatically — license creation requires authenticated dashboard access.
Critical: Never Expose Vendor API Keys Your vendor API key must NEVER be included in client-side code. If leaked, attackers can access customer usage data, perform DoS attacks to exhaust your daily API quota, and poison analytics with fake telemetry.

Correct: Backend Proxy Pattern

server.js
// 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();
}

Environment Variables

.env
# .env file (NEVER commit to git!)
VENDOR_API_KEY=your-secret-vendor-key
PRODUCT_KEY=your-product-key

Architecture Diagram

Client App (No API Key)
↓ License Key only
Your Backend (Node.js / PHP / Python)
↓ API Key (from env var)
Unified Licensing API (Secure)

Additional Security Measures

HTTPS Only

Encrypt all data in transit between client and server

Rate Limiting

Prevent brute force attacks on your validation endpoint

IP Whitelisting

Restrict API access to your server's IP address

Key Rotation

Regularly change your API keys (quarterly recommended)

Monitoring

Alert on unusual API activity or usage spikes

CORS Policy

Restrict cross-origin requests to trusted domains

If Your Key Leaks

  1. Rotate immediately: Generate a new API key in your dashboard
  2. Review logs: Check API logs for unauthorized access
  3. Check usage: Verify unusual API call patterns
  4. Notify customers: If customer data was accessed
  5. Update app: Deploy new key to your backend
  6. Monitor: Watch for suspicious activity for 30 days

9 Platform-Specific Guides

Step-by-step integration guides for every platform. Each guide includes complete code examples, best practices, and platform-specific considerations.

9 API Reference

UnifiedLicenseManager

MethodSDKDescriptionReturns
constructor(apiKey, productKey, ...)All SDKsInitialize SDKUnifiedLicenseManager
validateLicense(key, opts?)All SDKsValidate a license keyPromise<{isValid, licenseInfo?, error?}>
validateOffline()DesktopValidate from local cache onlyPromise<{isValid, licenseInfo?, error?}>
activateLicense(key, deviceInfo?)DesktopActivate license on devicePromise<{success, activation_id?}>
startTrial(productKey?)All SDKsStart a trial periodPromise<{success, trial_token?}>
checkTrial(trialToken)All SDKsCheck trial statusPromise<{valid, remaining_days?}>
trackTrialStart(features)All SDKsRecord trial feature usagePromise<boolean>
trackFeatureUsage(name, meta?)All SDKsTrack feature interactionPromise<boolean>
trackTrialConversion(value, tier)All SDKsRecord trial-to-paid conversionPromise<boolean>
sendHeartbeat(key, trialToken?)All SDKsSend heartbeat (24h rate limit)Promise<{success, commands?}>
startAutoHeartbeat(key, interval, trialToken?)All SDKsStart automatic heartbeatvoid
stopAutoHeartbeat()All SDKsStop automatic heartbeatvoid
loadProductConfig()All SDKsLoad product configurationPromise<object>
loadTiers()All SDKsLoad tier definitionsPromise<object>
isFeatureAvailable(name, license?)All SDKsCheck if feature is in tierPromise<boolean>
getPlatformInfo()All SDKsGet platform and fingerprint infoobject
clearCache()DesktopClear cached license dataPromise<void>

LicenseInfo Properties

PropertyTypeDescription
licenseKeystringThe license key
tierstringLicense tier name
featuresstring[]Available features for this tier
maxActivationsnumberMaximum allowed activations
currentActivationsnumberCurrent activation count
isActivebooleanWhether license is currently active
expiresAtstringExpiration date (ISO 8601)
platformstringPlatform this license was validated on
platformsstring[]Supported platforms

Error Codes

ErrorDescription
License not foundLicense key does not exist
License expiredLicense has passed its expiration date
License revokedLicense has been revoked by the vendor
Maximum activations reachedDevice limit has been exceeded
Platform not supportedLicense does not cover this platform
HARDWARE_MISMATCHDevice fingerprint does not match
INVALID_SIGNATURELicense signature verification failed
OFFLINE_NO_LICENSENo cached license available offline
NETWORK_ERRORServer unreachable

10 Complete Examples

Desktop Application (.NET)

LicenseManager.cs
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;
    }
}

React with Backend Proxy

LicenseProvider.jsx
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>;
};

Game with Trial + Analytics

game.js
// 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();
Need Help?

Our support team is here to help you integrate Unified Licensing.

Email Support Dashboard