Policies
Policies define the rules and constraints for licenses. Each license is governed by exactly one policy, which determines machine limits, expiration behavior, heartbeat requirements, offline support, and more.
Create Policy
POST
/policiesCreate a new licensing policy. Requires API Key auth with policies:write scope.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| productId | uuid | Required | ID of the product this policy belongs to |
| name | string | Required | Policy name (1-255 characters) |
| template | enum | Optional | Policy template: desktop_app, cli_tool, saas, trial, or custom |
| maxMachines | integer | null | Required | Maximum machine activations per license. Required: a positive integer pins the limit; null requires paired allowUnlimitedActivations:true (explicit-consent gate, integrator 360° audit Issue 1 — prevents silent unbounded-seat policies). |
| allowUnlimitedActivations | boolean | Optional | Pair with maxMachines:null to opt the policy into unlimited per-license activations. Request-time guardrail; not persisted (stripped at the route boundary). Default: false. |
| maxUses | integer | null | Optional | Maximum validation count per license (null = unlimited) |
| floating | boolean | Optional | Enable floating licenses (default: false) |
| durationDays | integer | null | Optional | Auto-expiration in days from license creation (null = no auto-expiry) |
| expirationStrategy | enum | Optional | Behavior on expiry: RESTRICT_ACCESS (default), ALLOW_ACCESS, or REVOKE |
| requireHeartbeat | boolean | Optional | Require periodic machine heartbeats (default: false) |
| heartbeatIntervalMinutes | integer | Optional | Heartbeat interval in minutes (default: 60, min: 1) |
| heartbeatCullStrategy | enum | Optional | Dead machine handling: DEACTIVATE_DEAD (default) or KEEP_DEAD |
| requireFingerprint | boolean | Optional | Require machine fingerprint on validation (default: true) |
| fingerprintUniqueness | enum | Optional | Fingerprint scope: PER_LICENSE (default), PER_POLICY, or PER_ACCOUNT |
| offlineAllowed | boolean | Optional | Allow offline license checkout (default: true) |
| offlineMaxDays | integer | Optional | Max days a license can operate offline (default: 14, min: 1) |
| allowMachineTransfer | boolean | Optional | Allow deactivating and reactivating on a different machine (default: false) |
| isTrial | boolean | Optional | Mark this as a trial policy (default: false) |
| trialDurationDays | integer | null | Optional | Trial duration in days (null = use durationDays) |
| overridable | boolean | Optional | Allow per-license overrides of maxMachines and maxUses (default: true) |
| entitlementIds | uuid[] | Optional | Entitlements automatically granted to licenses using this policy |
| metadata | object | Optional | Arbitrary key-value metadata |
Request
{
"productId": "d9e5f0a1-4c82-5e6f-a023-9b7c4d8e5f60",
"name": "Standard Desktop License",
"template": "desktop_app",
"maxMachines": 3,
"durationDays": 365,
"requireFingerprint": true,
"offlineAllowed": true,
"offlineMaxDays": 14,
"entitlementIds": ["a1b2c3d4-1111-2222-3333-444455556666"],
"metadata": {}
}201Policy created
json
{
"data": {
"id": "e0f1a2b3-5d93-6f70-b134-ac8d5e9f6071",
"productId": "d9e5f0a1-4c82-5e6f-a023-9b7c4d8e5f60",
"name": "Standard Desktop License",
"template": "desktop_app",
"maxMachines": 3,
"maxUses": null,
"floating": false,
"durationDays": 365,
"expirationStrategy": "RESTRICT_ACCESS",
"requireHeartbeat": false,
"heartbeatIntervalMinutes": 60,
"heartbeatCullStrategy": "DEACTIVATE_DEAD",
"requireFingerprint": true,
"fingerprintUniqueness": "PER_LICENSE",
"offlineAllowed": true,
"offlineMaxDays": 14,
"allowMachineTransfer": false,
"isTrial": false,
"trialDurationDays": null,
"overridable": true,
"entitlementIds": ["a1b2c3d4-1111-2222-3333-444455556666"],
"createdAt": "2026-01-12T09:00:00.000Z",
"updatedAt": "2026-01-12T09:00:00.000Z",
"deletedAt": null,
"metadata": {}
}
}List Policies
GET
/policiesList all policies with cursor-based pagination. Requires API Key auth with policies:read scope.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| cursor | uuid | Optional | Pagination cursor from previous response |
| limit | integer | Optional | Results per page (1-100, default 25) |
Request
GET /api/v1/policies?limit=25200Paginated list
json
{
"data": [
{
"id": "e0f1a2b3-5d93-6f70-b134-ac8d5e9f6071",
"productId": "d9e5f0a1-4c82-5e6f-a023-9b7c4d8e5f60",
"name": "Standard Desktop License",
"template": "desktop_app",
"maxMachines": 3,
"maxUses": null,
"floating": false,
"durationDays": 365,
"expirationStrategy": "RESTRICT_ACCESS",
"requireHeartbeat": false,
"requireFingerprint": true,
"offlineAllowed": true,
"overridable": true,
"createdAt": "2026-01-12T09:00:00.000Z",
"updatedAt": "2026-01-12T09:00:00.000Z",
"deletedAt": null,
"metadata": {}
}
],
"pagination": {
"nextCursor": null,
"hasMore": false
}
}Policy Templates
Templates provide sensible defaults for common licensing models. You can override any field when creating a policy.
| Template | Use Case | Key Defaults |
|---|---|---|
| desktop_app | Native desktop applications | Fingerprint required, offline allowed, 3 machines |
| cli_tool | Command-line tools and utilities | Fingerprint required, no machine limit |
| saas | Web-based SaaS applications | No fingerprint, no machine limit, heartbeat optional |
| trial | Time-limited free trials | 14-day duration, 1 machine, not overridable |
| custom | Fully custom configuration | No defaults applied |
Overridable Policies
When
overridable is true (the default), individual licenses can override maxMachines and maxUses using the per-license override fields.Explicit consent for unlimited seats
maxMachines requires a positive integer on create. To opt a policy into unlimited per-license activations, send "maxMachines": null AND "allowUnlimitedActivations": true in the same request. The same gate fires on PATCH when widening a finite limit to null. This prevents a typo or omitted field from silently shipping an unbounded-seat license; the flag is request-time only and is never persisted.