End User Portal

The End User Portal provides a self-service experience for your customers. End users can register, log in, activate license keys, view their licenses, and verify licenses from your application loader.

How It Works

End users are scoped to an organization. Each organization can have its own portal, identified by its slug (e.g., your-company). End users register with their email and password, then link license keys to their account.

Portal Authentication

Register

const response = await fetch('https://api.geckoguard.com/v1/enduser/auth/register', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    orgSlug: 'your-company',
    email: 'user@example.com',
    password: 'securepassword123'
  })
});

const { data } = await response.json();
// data.endUser = { id, email, createdAt }
// data.token   = JWT session token
// data.org     = { id, slug, name }

Login

const response = await fetch('https://api.geckoguard.com/v1/enduser/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    orgSlug: 'your-company',
    email: 'user@example.com',
    password: 'securepassword123'
  })
});

Logout

POST /v1/enduser/auth/logout
Authorization: Bearer END_USER_SESSION_TOKEN

License Management

End users link a license key to their account:

await fetch('https://api.geckoguard.com/v1/enduser/licenses/activate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer END_USER_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    orgSlug: 'your-company',
    licenseKey: 'LICENSE-KEY-123'
  })
});

A license can only be linked to one end user account. If it's already linked to a different account, the request returns 409 ALREADY_CLAIMED.

List Licenses

const response = await fetch('https://api.geckoguard.com/v1/enduser/licenses', {
  headers: { 'Authorization': 'Bearer END_USER_TOKEN' }
});

const { data } = await response.json();
// data.licenses = [{ licenseId, status, expiresAt, hwidBound, product: { id, name }, linkedAt }]

Get Profile

GET /v1/enduser/me
Authorization: Bearer END_USER_TOKEN

Returns the end user's profile and organization info.

Loader Authentication

For desktop/native application loaders, there's a dedicated auth flow that returns a loader token — a lightweight JWT for license verification:

Loader Login

const response = await fetch('https://api.geckoguard.com/v1/enduser/loader/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    orgSlug: 'your-company',
    email: 'user@example.com',
    password: 'securepassword123'
  })
});

const { data } = await response.json();
// data.token = loader JWT token
// data.org   = { id, slug, name }

Loader Verify

Verify a license from your application using the loader token. This endpoint resolves the product by the license key (no productId needed) and returns a signed response for tamper detection:

const response = await fetch('https://api.geckoguard.com/v1/enduser/verify', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer LOADER_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    orgSlug: 'your-company',
    licenseKey: 'LICENSE-KEY-123',
    hwid: 'device-hardware-id',
    meta: {                            // Optional
      ip: '192.168.1.1',
      userAgent: 'MyApp/2.0',
      version: '2.0.0'
    }
  })
});

const { data } = await response.json();
// data.decision = { allow: true } or { allow: false, reason: 'HWID_MISMATCH' }
// data.licenseId, data.productId, data.expiresAt, data.hwid
// data.sig = cryptographic signature for tamper detection
// data.kid = signing key ID

Signed Responses

The loader verify endpoint returns a sig field — an Ed25519 signature of the response payload. Your application can verify this signature to detect response tampering (man-in-the-middle attacks):

// The response includes:
// - data: the payload (decision, licenseId, expiresAt, etc.)
// - sig: Ed25519 signature of the stable-stringified payload
// - kid: signing key ID for key rotation support

Decision Reasons

ReasonDescription
NOT_FOUNDLicense key not found or doesn't belong to the org
REVOKEDLicense is revoked or expired
EXPIREDLicense has passed its expiry date
HWID_MISMATCHHardware ID doesn't match the bound device

Custom Domains

Organizations can configure custom domains for their portal. The resolve endpoint maps hostnames to organizations:

GET /v1/enduser/orgs/resolve?hostname=portal.yourcompany.com

Rate Limiting

Portal endpoints are rate-limited per IP and per email:

ActionLimit
Registration20/min per IP, 10/min per email
Login60/min per IP, 20/min per email
Loader login60/min per IP
VerifyConfigurable per IP and per license

Use Cases

  • Customer self-service — let users manage their own licenses without contacting support
  • Desktop app loaders — authenticate users and verify licenses with signed responses
  • White-label portals — custom domains for a branded experience
  • License activation — let users activate purchased license keys on their account