# Client API Reference CoreLicense Client API reference — activate, check, refresh, and status endpoints for license activation and policy token flows. Canonical HTML URL: https://corelicense.net/docs/client-api Plain-text URL: https://corelicense.net/docs/client-api.txt ---# 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 | Method | Path | Description | Used by SDK? | |--------|------|-------------|--------------| | POST | `/v1/client/activate` | First-time license activation, create/record instance, return policy token | Yes — first boot | | POST | `/v1/client/check` | Periodic license validation, return new policy token if active | Yes — background sync | | POST | `/v1/client/refresh` | Alias of `check` on the server | Yes — `license.refresh()` | | POST | `/v1/client/status` | Fast status read, **does not** return policy token | No — 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 ```json { "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) ```json { "ok": true, "status": "active", "policyToken": "eyJhbGciOiJFZERTQSJ9...", "policyVersion": "12", "serverTime": "2026-06-07T12:00:00Z", "nextCheckAfter": 21600, "apiUrl": "https://api.corelicense.net" } ``` ### Response (error) ```json { "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 ```json { "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) ```json { "ok": true, "status": "active", "policyToken": "eyJhbGciOiJFZERTQSJ9...", "policyVersion": "12", "serverTime": "2026-06-07T12:00:00Z", "nextCheckAfter": 21600, "apiUrl": "https://api.corelicense.net" } ``` ### Response (error) ```json { "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 | Status | Meaning | |--------|---------| | `active` | License is valid with full entitlements | | `suspended` | Temporarily suspended by admin | | `revoked` | Permanently revoked | | `expired` | Past expiration date | | `invalid` | Invalid key, not yet started, or max instances exceeded | | `blocked` | Instance blocked (may include block reason) | ## Error codes | Code (server) | SDK mapping | Description | |---------------|-------------|-------------| | `PRODUCT_NOT_FOUND` | `PRODUCT_NOT_FOUND` | Product ID/slug does not exist | | `INVALID_PRODUCT` | `PRODUCT_NOT_FOUND` | Legacy alias | | `LICENSE_NOT_FOUND` | `LICENSE_NOT_FOUND` | License key does not exist | | `LICENSE_INVALID` / `INVALID_LICENSE` | `LICENSE_INVALID` | License does not belong to specified product | | `LICENSE_REVOKED` | `LICENSE_REVOKED` | License has been revoked | | `LICENSE_SUSPENDED` | `LICENSE_SUSPENDED` | License has been suspended | | `LICENSE_EXPIRED` | `LICENSE_EXPIRED` | License has expired | | `LICENSE_NOT_STARTED` | `LICENSE_NOT_STARTED` | License effective date not yet reached | | `POLICY_SIGN_FAILED` | `POLICY_SIGNATURE_ERROR` | Server failed to sign policy token | | `MAX_INSTANCES` | `MAX_INSTANCES` | Max instances exceeded (status `invalid`) | | `INSTANCE_BLOCKED` | `INSTANCE_BLOCKED` | Instance blocked by admin (status `blocked`) | | `RATE_LIMITED` (HTTP 429) | `RATE_LIMITED` | Too 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: ```json { "keys": [ { "kid": "key_dev_01", "alg": "Ed25519", "publicKey": "..." } ] } ``` ### HTTP errors (public keys) | HTTP | code | Description | |------|------|-------------| | 404 | `PRODUCT_NOT_FOUND` | Product does not exist | | 404 | `PUBLIC_KEYS_NOT_PUBLISHED` | Product exists but signing key not published | | 404 | `PUBLIC_KEY_NOT_FOUND` | `GET /v1/public/keys/{kid}` — kid not found | | 429 | `RATE_LIMITED` | Too many public API requests | The API domain for the SDK is embedded in the license key (format `CL1..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 - [Node.js SDK Quick Start](/docs/nodejs-sdk/quick-start) - [Node.js SDK API Reference](/docs/nodejs-sdk/api-reference) - [Server Architecture](/docs/server-architecture)