Skip to main content

Guides

Desktop App Licensing

Lock your desktop application to specific devices and validate licenses on startup.

Template used
This guide uses the desktop_app policy template, which sets: 2 devices, offline 14 days, fingerprint required.

1. Create a Product

A product represents your desktop application. The code becomes the license key prefix (e.g., DSK-XXXX-XXXX-XXXX-XXXX).

Python

create_product.py
from licentric import Licentric

client = Licentric(api_key="lk_live_your_key_here")

product = client.products.create(
    name="My Desktop App",
    code="DSK"
)

TypeScript

create-product.ts
import { Licentric } from "@licentric/sdk";

const client = new Licentric({ apiKey: "lk_live_your_key_here" });

const product = await client.products.create({
  name: "My Desktop App",
  code: "DSK",
});

2. Create a Policy

Use the desktop_app template to get sensible defaults for device-locked desktop software.

Python

create_policy.py
policy = client.policies.create(
    name="Desktop License",
    product_id=product.id,
    template="desktop_app"  # 2 devices, offline 14 days, fingerprint required
)

TypeScript

create-policy.ts
const policy = await client.policies.create({
  name: "Desktop License",
  productId: product.id,
  template: "desktop_app", // 2 devices, offline 14 days, fingerprint required
});

3. Issue License Keys

Issue a license key to each customer. The key uses the product code as its prefix and is auto-generated in the format PREFIX-XXXX-XXXX-XXXX-XXXX.

issue_license.py
license = client.licenses.create(
    policy_id=policy.id,
    email="customer@example.com"
)

# Give this key to your customer:
# DSK-XXXX-XXXX-XXXX-XXXX
print(f"License key: {license.key}")

4. Validate on Launch

Validate the license key when your application starts. If the device has not been activated yet, the SDK handles activation automatically.

Python

startup.py
import sys
from licentric import Licentric, ValidationError

client = Licentric(api_key="lk_live_your_key_here")

def validate_license(license_key: str) -> bool:
    """Validate license on application launch."""
    try:
        result = client.validate(
            key=license_key,
            fingerprint=client.fingerprint()
        )

        if result.valid:
            return True

        if result.code == "FINGERPRINT_NOT_FOUND":
            # First launch on this device — activate it
            client.machines.activate(
                license_id=result.license_id,
                fingerprint=client.fingerprint()
            )
            return True

        print(f"License invalid: {result.code}")
        return False

    except ValidationError as e:
        print(f"Validation error: {e.message}")
        return False

# On app startup
LICENSE_KEY = load_key_from_config()
if not validate_license(LICENSE_KEY):
    sys.exit(1)

TypeScript

startup.ts
import { Licentric } from "@licentric/sdk";

const client = new Licentric({ apiKey: "lk_live_your_key_here" });

async function validateLicense(licenseKey: string): Promise<boolean> {
  const result = await client.validate({
    key: licenseKey,
    fingerprint: client.fingerprint(),
  });

  if (result.valid) {
    return true;
  }

  if (result.code === "FINGERPRINT_NOT_FOUND") {
    // First launch on this device — activate it
    await client.machines.activate({
      licenseId: result.licenseId,
      fingerprint: client.fingerprint(),
    });
    return true;
  }

  throw new Error(`License invalid: ${result.code}`);
}

5. Re-validate Periodically

Desktop apps should re-validate in the background to detect revocations, suspensions, and expiration. A 24-hour interval works well for most applications.

revalidation.py
import threading

def start_periodic_validation(license_key: str, interval_hours: int = 24):
    """Re-validate license in the background every N hours."""
    def _revalidate():
        result = client.validate(
            key=license_key,
            fingerprint=client.fingerprint()
        )
        if not result.valid:
            notify_user("License is no longer valid. Please renew.")
        # Schedule next check
        timer = threading.Timer(interval_hours * 3600, _revalidate)
        timer.daemon = True
        timer.start()

    _revalidate()

6. Handle Offline Mode

The desktop_app template allows up to 14 days of offline use. Cache the validation result locally so the app works without a network connection.

offline_fallback.py
from licentric import Licentric, ApiError

def validate_with_offline_fallback(license_key: str) -> bool:
    """Validate online, fall back to cached result if offline."""
    try:
        result = client.validate(
            key=license_key,
            fingerprint=client.fingerprint()
        )
        # Cache the result locally for offline use
        save_to_cache(result)
        return result.valid

    except ApiError:
        # Network unreachable — check local cache
        cached = load_from_cache()
        if cached and not cached.expired:
            return cached.valid
        return False
Offline license files
For extended offline periods, use the Offline Licensing feature to check out a signed license file with a configurable TTL.