Skip to main content

Guides

SaaS Product Licensing

Validate licenses server-side with session management and automatic provisioning via Stripe.

Template used
This guide uses the saas policy template: unlimited devices, daily heartbeat, transferable, no fingerprint required.

1. Create Product and Policy

SaaS products use floating licenses with no device limits. Heartbeats ensure active usage tracking.

setup.py
# Products and policies are management-plane resources, not exposed on the
# SDK client — create them via the REST API (Management API key, *_write scopes).
import httpx

api_key = "lk_live_your_key_here"
headers = {"Authorization": f"Bearer {api_key}"}

product = httpx.post(
    "https://www.licentric.com/api/v1/products",
    headers=headers,
    json={"name": "My SaaS Platform", "code": "SAAS"},
).json()["data"]

policy = httpx.post(
    "https://www.licentric.com/api/v1/policies",
    headers=headers,
    json={
        "name": "SaaS Subscription",
        "productId": product["id"],
        "template": "saas",
        "maxMachines": None,
        "allowUnlimitedActivations": True,  # unlimited devices
        "requireHeartbeat": True,
        "allowMachineTransfer": True,
    },
).json()["data"]

2. Validate on Login

Validate the license once when the user logs in and store the result in their session. No fingerprint is needed for SaaS — use the license key alone.

Python

auth.py
from licentric import Licentric

client = Licentric(api_key="lk_live_your_key_here")

def validate_on_login(license_key: str, user_id: str) -> bool:
    """Validate the license when a user logs in."""
    result = client.validate(key=license_key)

    if not result.valid:
        return False

    # Store validation result in session
    session = get_session(user_id)
    session["license_valid"] = True
    session["license_id"] = result.license.id if result.license else None
    session["entitlements"] = result.entitlements
    session.save()

    return True

TypeScript

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

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

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

  if (!result.valid) {
    return false;
  }

  // Store validation result in session
  const session = await getSession(userId);
  session.licenseValid = true;
  session.licenseId = result.license?.id ?? null;
  session.entitlements = result.entitlements;
  await session.save();

  return true;
}

3. Protect Routes with Middleware

Use middleware to check the session for a valid license on each request. This avoids re-validating against the API on every page load.

middleware.py
def require_valid_license(handler):
    """Middleware: check session for valid license on each request."""
    def wrapper(request):
        session = get_session(request.user_id)

        if not session.get("license_valid"):
            return Response(status=403, body={
                "error": "No valid license. Please subscribe."
            })

        return handler(request)
    return wrapper

# Usage
@require_valid_license
def dashboard_handler(request):
    return render_dashboard(request)

4. Send Heartbeats

The SaaS template enables heartbeats by default. Send a heartbeat periodically to confirm the license is actively in use. If a heartbeat is missed, validation returns HEARTBEAT_REQUIRED.

heartbeat.py
# The SaaS template enables heartbeats by default.
# Heartbeats ensure licenses are actively in use.
#
# Server-side: send heartbeat on each authenticated request
# (or on a schedule, e.g., every 30 minutes)

def send_heartbeat(machine_id: str, license_key: str):
    """Ping Licentric to confirm active usage."""
    client.heartbeat(machine_id, license_key)

5. Pair with Stripe

For the best SaaS experience, connect Stripe to automatically provision licenses when customers subscribe. See the Stripe Integration guide for full setup.

webhook_handler.py
# When a Stripe checkout completes, Licentric automatically:
# 1. Creates a customer (if not exists)
# 2. Issues a license key
# 3. Sends the key via webhook to your app
#
# Your webhook handler:
def handle_license_created(event):
    license = event["data"]
    user = find_user_by_email(license["email"])
    user.license_key = license["key"]
    user.save()
    send_welcome_email(user)
No fingerprinting needed
SaaS products validate server-side, so there is no device to fingerprint. Use entitlements to control feature access across subscription tiers instead. See the Feature Gating guide.