License Server — Client API Reference

Public REST API that client applications and the Node.js SDK call to activate licenses, validate status, and refresh policy tokens.

Base path: /v1/client — no admin JWT required. Authentication uses license key + product ID (slug or UUID).

Endpoints

MethodPathDescriptionUsed by SDK?
POST/v1/client/activateFirst-time license activation, create/record instance, return policy tokenYes — first boot
POST/v1/client/checkPeriodic license validation, return new policy token if activeYes — background sync
POST/v1/client/refreshAlias of check on the serverYes — license.refresh()
POST/v1/client/statusFast status read, does not return policy tokenNo — custom clients

Note: Business errors (revoked, expired, etc.) return HTTP 200 with ok: false. Only rate limiting returns HTTP 429.

POST /v1/client/activate

Request

{
  "productId": "mediahub",
  "licenseKey": "CL1.xxxxx.CL-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE",
  "instanceId": "sha256_hex...",
  "application": {
    "domainHash": "sha256-of-normalized-domain",
    "environment": "production"
  },
  "sdk": {
    "name": "@corelicense/node",
    "version": "0.0.0",
    "runtime": "node",
    "runtimeVersion": "v22.0.0",
    "appVersion": "1.0.0"
  }
}

application is only sent during activate. The SDK sends domainHash (hash of normalized domain) and environment from NODE_ENV — plain domain is never sent.

Response (success)

{
  "ok": true,
  "status": "active",
  "policyToken": "eyJhbGciOiJFZERTQSJ9...",
  "policyVersion": "12",
  "serverTime": "2026-06-07T12:00:00Z",
  "nextCheckAfter": 21600,
  "apiUrl": "https://api.corelicense.net"
}

Response (error)

{
  "ok": false,
  "status": "revoked",
  "code": "LICENSE_REVOKED",
  "message": "License has been revoked"
}

message may include an admin-provided reason (suspend/revoke/block). Example for blocked instance: status: "blocked", code: "INSTANCE_BLOCKED".

POST /v1/client/check

Request

{
  "productId": "mediahub",
  "licenseKey": "CL1.xxxxx.CL-AAAAA-BBBBB-CCCCC-DDDDD-EEEEE",
  "instanceId": "sha256_hex...",
  "currentPolicyHash": "sha256...",
  "sdk": {
    "name": "@corelicense/node",
    "version": "0.0.0",
    "runtime": "node",
    "runtimeVersion": "v22.0.0",
    "appVersion": "1.0.0"
  }
}

Response (success)

{
  "ok": true,
  "status": "active",
  "policyToken": "eyJhbGciOiJFZERTQSJ9...",
  "policyVersion": "12",
  "serverTime": "2026-06-07T12:00:00Z",
  "nextCheckAfter": 21600,
  "apiUrl": "https://api.corelicense.net"
}

Response (error)

{
  "ok": false,
  "status": "revoked",
  "code": "LICENSE_REVOKED",
  "message": "License has been revoked"
}

POST /v1/client/status

Request body is the same as check, but the server does not sign a policy token and does not record new instances. A successful response only includes ok, status, apiUrl, serverTime, nextCheckAfter, and policyVersion.

License status values

StatusMeaning
activeLicense is valid with full entitlements
suspendedTemporarily suspended by admin
revokedPermanently revoked
expiredPast expiration date
invalidInvalid key, not yet started, or max instances exceeded
blockedInstance blocked (may include block reason)

Error codes

Code (server)SDK mappingDescription
PRODUCT_NOT_FOUNDPRODUCT_NOT_FOUNDProduct ID/slug does not exist
INVALID_PRODUCTPRODUCT_NOT_FOUNDLegacy alias
LICENSE_NOT_FOUNDLICENSE_NOT_FOUNDLicense key does not exist
LICENSE_INVALID / INVALID_LICENSELICENSE_INVALIDLicense does not belong to specified product
LICENSE_REVOKEDLICENSE_REVOKEDLicense has been revoked
LICENSE_SUSPENDEDLICENSE_SUSPENDEDLicense has been suspended
LICENSE_EXPIREDLICENSE_EXPIREDLicense has expired
LICENSE_NOT_STARTEDLICENSE_NOT_STARTEDLicense effective date not yet reached
POLICY_SIGN_FAILEDPOLICY_SIGNATURE_ERRORServer failed to sign policy token
MAX_INSTANCESMAX_INSTANCESMax instances exceeded (status invalid)
INSTANCE_BLOCKEDINSTANCE_BLOCKEDInstance blocked by admin (status blocked)
RATE_LIMITED (HTTP 429)RATE_LIMITEDToo many requests (retryable)

Public keys

The SDK verifies policy tokens using Ed25519 public keys. During boot(), the SDK calls the product keys endpoint (slug or UUID) and caches keys to public.keys.json:

GET /v1/public/keys/{kid}
GET /v1/public/products/{productId}/keys

Response:

{
  "keys": [
    { "kid": "key_dev_01", "alg": "Ed25519", "publicKey": "..." }
  ]
}

HTTP errors (public keys)

HTTPcodeDescription
404PRODUCT_NOT_FOUNDProduct does not exist
404PUBLIC_KEYS_NOT_PUBLISHEDProduct exists but signing key not published
404PUBLIC_KEY_NOT_FOUNDGET /v1/public/keys/{kid} — kid not found
429RATE_LIMITEDToo many public API requests

The API domain for the SDK is embedded in the license key (format CL1.<base64url>.CL-...) when admin creates/rotates keys. Server configures via SDK_PUBLIC_URL. Check/activate responses also return apiUrl for SDK caching with legacy keys.

GET /v1/public/sdk-config

# Response
{ "apiUrl": "https://api.corelicense.net" }

When admin rotates signing keys, the SDK automatically refetches keys and retries verification once. Pass publicKey in config to pin a key — pinned mode disables auto-fetch.

Minimal data policy

The SDK only sends:

  • productId, licenseKey, instanceId
  • SDK version, app version (if available)
  • Policy token hash (check/refresh)
  • Application domain hash + environment (activate)

The SDK does not send: database contents, user data, request bodies, internal logs, secrets, or business data.

Health check

GET /v1/public/health

Related docs