Licenses
Licenses are the core unit of access control in GeckoGuard. Each license grants access to a specific product for a specific end user.
License Lifecycle
1. Creation
Licenses are created through the dashboard or API:
const response = await fetch('https://api.geckoguard.net/v1/dashboard/orgs/ORG_ID/licenses', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_SESSION_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
productId: 'product-id',
// key: 'CUSTOM-KEY-123', // Optional — auto-generated if omitted
// expiresAt: '2025-12-31T23:59:59Z', // Fixed expiry
// expiresAfterDays: 30, // Relative expiry (days after activation)
// expirationMode: 'never', // 'never' | 'fixed' | 'afterActivation' | 'both'
// policyOverride: { ... }, // Override product default policy
// count: 1 // Bulk create up to 500 at once
})
});
2. Activation
When an end user first validates a license that has expiresAfterDays set, the license is automatically activated — the activatedAt timestamp is recorded and the expiry countdown begins.
const response = await fetch('https://api.geckoguard.net/v1/licenses/authorize', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
productId: 'product-id',
licenseKey: 'LICENSE-KEY-123',
hwid: 'device-hwid', // Optional
ip: '192.168.1.1', // Optional
deviceId: 'device-uuid', // Optional
sessionId: 'session-uuid', // Optional: for concurrency limits
dryRun: false // Optional: test validation without persisting changes
})
});
3. Usage
After activation, licenses are validated on each application launch or periodically. Each validation checks:
- Blacklists — HWID and IP checked against product blacklist
- Hardware binding — HWID checked against policy (sticky/limit mode)
- IP restrictions — IP checked against policy, including region-based limiting
- Concurrency limits — Active session count checked
- Expiration — Both fixed and relative expiry validated
4. Expiration, Revocation, Freeze, or Suspension
Licenses can end or be held in several ways:
- Expire naturally based on their expiration settings
- Revoked manually by an admin (permanent)
- Frozen temporarily to pause the expiration countdown — the license still authorizes
- Suspended temporarily to both pause the countdown and block all use
License States
| Status | Description |
|---|---|
| ACTIVE | Valid and can be authorized |
| EXPIRED | Past expiration date — automatically set on failed validation |
| REVOKED | Manually disabled by admin (permanent) |
| SUSPENDED | Temporarily disabled by admin — authorization is denied and the expiration countdown is paused. Reversible back to ACTIVE. |
Note:
FROZENis not a status — freezing setsfrozenAt/frozenDaysRemainingwhile the status staysACTIVE, so a frozen license keeps authorizing. Use SUSPENDED when you need to lock a user out while also pausing their clock.
License Keys
License keys are unique identifiers. They can be:
- Auto-generated: GeckoGuard creates a secure random key
- Custom: You provide your own key format (must be unique per product)
Dry Run Mode
Pass dryRun: true to the authorize endpoint to test validation without persisting any changes (no bindings created, no sessions tracked, no activation recorded). Useful for testing policies:
const result = await authorize({
productId: 'product-id',
licenseKey: 'KEY-123',
hwid: 'test-hwid',
dryRun: true
});
// result includes debug info about policy evaluation
Policy Overrides
Products have a default license policy, but you can override it per-license:
{
policyOverride: {
v: 1,
limits: {
hwid: { mode: 'sticky' },
ip: { mode: 'limit', maxDistinct: 3, windowDays: 30 },
concurrency: { mode: 'limit', maxActive: 2 },
resetBudget: {
hwid: { max: 3, cooldownHours: 24 },
ip: { max: 5, cooldownHours: 12 }
}
}
}
}
The override is deep-merged with the product default — you only need to specify the fields you want to change.
License Limits
Hardware Binding (HWID)
| Mode | Behavior |
|---|---|
unlimited | No restrictions on hardware IDs |
sticky | First device is permanently bound |
limit | Allow up to maxDistinct distinct devices |
IP Restrictions
| Mode | Behavior |
|---|---|
unlimited | No restrictions |
sticky | First IP is permanently bound |
limit | Allow up to maxDistinct distinct IPs within windowDays |
IP limits also support a limitType field:
'ip'(default) — limit by distinct IP addresses'region'— limit by geographic region (country code via IP geolocation)
Concurrency
| Mode | Behavior |
|---|---|
unlimited | No session limits |
limit | Maximum maxActive concurrent sessions |
Reset Budgets
Reset budgets control how many times HWID or IP bindings can be reset:
- max: Total number of resets allowed (0 = no resets permitted)
- cooldownHours: Minimum hours between resets
Reset endpoints:
POST /v1/dashboard/licenses/:id/reset-hwidPOST /v1/dashboard/licenses/:id/reset-ip
Freezing Licenses
Freeze a license to temporarily pause its expiration countdown:
// Freeze via PATCH
await fetch('/v1/dashboard/licenses/LICENSE_ID', {
method: 'PATCH',
headers: {
'Authorization': 'Bearer YOUR_SESSION_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({ frozen: true })
});
Frozen licenses:
- Stop counting down toward expiration
- Can still be validated (authorization succeeds)
- Track
frozenDaysRemainingfor restoring time when unfrozen - Can be bulk frozen/unfrozen via the org-level endpoints
Suspending Licenses
Suspend a license when you need a temporary hard lock — the user is blocked from using the license, but the suspension is reversible and their remaining time is preserved (the countdown is paused exactly like a freeze):
// Suspend (blocks use + pauses the clock)
await fetch('/v1/dashboard/licenses/LICENSE_ID/suspend', {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_SESSION_TOKEN' }
});
// Unsuspend (restores ACTIVE and resumes the countdown from where it left off)
await fetch('/v1/dashboard/licenses/LICENSE_ID/unsuspend', {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_SESSION_TOKEN' }
});
Suspended licenses:
- Are denied at
/v1/enduser/verifyand/v1/enduser/authorize-download(reasonSUSPENDED) - Cannot be claimed via
/v1/enduser/licenses/activate - Pause the relative-expiry countdown (reusing
frozenAt/frozenDaysRemaining) - Return to
ACTIVEon unsuspend — orEXPIREDif the resumed expiry has already passed - Emit
license.suspended/license.unsuspendedwebhook events
Frozen vs. Suspended: freeze pauses the clock but still authorizes; suspend pauses the clock and locks the user out.
Bulk Operations
GeckoGuard supports bulk operations on licenses via org-scoped endpoints:
- Freeze/Unfreeze —
POST /v1/dashboard/orgs/:orgId/licenses/freeze - Revoke/Unrevoke —
POST /v1/dashboard/orgs/:orgId/licenses/revoke - Extend —
POST /v1/dashboard/orgs/:orgId/licenses/extend - Delete —
DELETE /v1/dashboard/orgs/:orgId/licenses - Import —
POST /v1/dashboard/orgs/:orgId/licenses/import - Export —
GET /v1/dashboard/orgs/:orgId/licenses/export?format=csv|json
Best Practices
- Use auto-generated keys — more secure than predictable patterns
- Set expiration dates — even if far in the future, it's good practice
- Use
dryRunfor testing — validate your policy setup without side effects - Monitor analytics — check the Analytics dashboard for unusual patterns
- Revoke compromised licenses immediately — act quickly if a key is leaked
- Use policy overrides sparingly — prefer product-level defaults for consistency
- Set up blacklists — proactively block known bad HWIDs or IPs