Guides
SaaS Product Licensing
Validate licenses server-side with session management and automatic provisioning via Stripe.
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.
# 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
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 TrueTypeScript
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.
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.
# 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.
# 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)