← Plugin SDK

POST /api/v1/license/activate

Register a machine against a license key on first run. Idempotent for known machines.

POST /api/v1/license/activate

Call this once when the plugin starts up on a new machine. The server records the (licenseKey, machineId) pair and returns the license metadata the plugin caches locally.

The call is idempotent for a known machine: a repeat request with the same machineId returns 200 with the existing activation refreshed (lastSeenAt updated, machineName optionally replaced). A repeat with a new machineId either creates a new activation or returns 409 if the cap is reached.

Request

Headers: see HMAC Signing.

Body (JSON):

{
  "licenseKey": "uuid-v4",        // required, the customer's license key
  "machineId":  "8-128 chars",    // required, your stable per-machine identifier
  "machineName": "optional label" // optional, max 100 chars, shown on dashboard
}

machineId guidance

Pick something stable across reboots and Unity Editor restarts. Suggested derivation: SHA-256 of the OS machine GUID + the Unity Editor install path. Plugins must not use values that change at install time (e.g. Process.GetCurrentProcess().Id).

Responses

200 OK — created or refreshed

{
  "ok": true,
  "product": { "slug": "procgenerator", "name": "Procedural Generator" },
  "status": "ACTIVE",
  "expiresAt": null,
  "activations": 1,
  "max": 3
}

activations is the current count of distinct machines including this one. max is the per-license cap.

400 LICENSE_API_MALFORMED (1702)

Body did not match the schema (missing field, wrong type, machineId too short, etc.).

401 LICENSE_API_BAD_SIGNATURE (1700) / STALE_TIMESTAMP (1701)

Signature, headers, or timestamp failed verification. See HMAC Signing for the order in which checks fire.

403 LICENSE_API_INACTIVE (1704)

The license exists but is not in the ACTIVE state. Response includes the current status field so the plugin can surface a precise reason (refunded, suspended, etc.).

404 LICENSE_API_NOT_FOUND (1703)

The license key is unknown or has been soft-deleted.

409 LICENSE_API_LIMIT_REACHED (1705)

Adding this machine would exceed the activation cap. Body includes the current count and the cap:

{
  "error": "Maximum 3 activations reached",
  "code": 1705,
  "activations": 3,
  "max": 3
}

The user can free a slot from https://ntkfoundry.com/dashboard/activations. Plugins should surface a hint pointing to that URL.

429 LICENSE_API_RATE_LIMITED (1706)

Per-license-key or per-IP bucket tripped. The response includes the bucket that fired ("key" or "ip") and a Retry-After: <seconds> header. Honour the header.

500 internal

Unexpected server-side failure. Logged + reported to Sentry. Plugins should retry with exponential backoff capped at the activation timeout.

Concurrency

The activation flow runs in a transaction with a SELECT … FOR UPDATE row lock on the License row. Two concurrent activations for the same license queue at the lock — neither can race past the cap.

Audit log

On accept the server writes an audit record (action: "license.activate" for new machines, action: "license.heartbeat" with refresh: true for repeats). These are visible to the license owner and to the support team.