Quick start
You'll need a license key (looks like VX-XXXX-XXXX-XXXX) and a stable device identifier from your app (any unique string per installation works).
Activating a license
After purchase, your license key appears in your dashboard and is emailed to you. In your app's license dialog, paste the key. The app sends it (with the device identifier) to our validation endpoint, which binds the key to that device on first activation.
Once bound, the key is locked to that device. If you need to switch machines, ask support or use the admin reset.
Validation API
Endpoint: POST https://luminous-license-hub.lovable.app/api/validate_license
Send a JSON body with license_key and device_id. No authentication headers needed.
POST /api/validate_license
Content-Type: application/json
{
"license_key": "VX-XXXX-XXXX-XXXX",
"device_id": "unique-per-install-hash"
}Response schema
On success:
{
"valid": true,
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"user_name": "Jane Doe",
"status": "active",
"expires_at": "2027-01-01T00:00:00Z", // or null for Lifetime
"activated_at": "2026-06-24T10:30:00Z",
"message": "License validated successfully!"
}On failure:
{
"valid": false,
"message": "License limit reached (Already bound to another device)"
}Error messages
Code examples
JavaScript / TypeScript
const res = await fetch(
"https://luminous-license-hub.lovable.app/api/validate_license",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
license_key: userInput,
device_id: await getDeviceId(),
}),
},
);
const data = await res.json();
if (!data.valid) throw new Error(data.message);
saveSession(data.session_id);Python
import requests
r = requests.post(
"https://luminous-license-hub.lovable.app/api/validate_license",
json={"license_key": key, "device_id": device_id},
timeout=10,
)
data = r.json()
if not data["valid"]:
raise RuntimeError(data["message"])Chrome Extension (manifest v3)
// In background.js
chrome.runtime.onMessage.addListener(async (msg, _sender, sendResponse) => {
if (msg.type !== "validate-license") return;
const res = await fetch(
"https://luminous-license-hub.lovable.app/api/validate_license",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(msg.payload),
}
);
sendResponse(await res.json());
return true; // keep channel open
});Supabase RPC (direct DB call)
If your client already speaks Supabase, you can skip the HTTP endpoint and call the validate_license RPC directly. Same logic, same response shape.
POST /rest/v1/rpc/validate_license
apikey: <publishable-anon-key>
Content-Type: application/json
{
"license_key": "VX-XXXX-XXXX-XXXX",
"device_id": "unique-per-install-hash"
}Common issues
- CORS error in browser: use the
/api/validate_licenseendpoint — it allows cross-origin. The Supabase RPC requires anapikeyheader. - Always "not found": make sure the key is uppercase and includes the
VX-prefix. - "Already bound" on the same machine: your
device_idchanges between runs. Persist it (localStorage / chrome.storage / disk file) and reuse it. - Session ID changes every call: that's expected when no session existed; cache it client-side and reuse.