Documentation

Integrate in under five minutes

Activate a key, hit our endpoint, get a session. That's it.

01 · Start here

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).

02 · Users

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.

03 · Developers

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"
}
04 · Schema

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)"
}
05 · Errors

Error messages

License not found
Key doesn't exist in our system. Check for typos.
License is inactive or banned
Status is not active. Renew or contact support.
License has expired
Expiry date passed — renew to continue.
License limit reached (Already bound to another device)
Key was activated on a different device. Reset binding via admin.
06 · Code

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
});
07 · Advanced

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"
}
08 · Troubleshooting

Common issues

  • CORS error in browser: use the /api/validate_license endpoint — it allows cross-origin. The Supabase RPC requires an apikey header.
  • Always "not found": make sure the key is uppercase and includes the VX- prefix.
  • "Already bound" on the same machine: your device_id changes 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.

Need help? We're here.

Contact Support