NEOK License

Developer documentation

Build with NEOK License

Use the versioned API to verify licenses, activate installations, deliver updates, and send telemetry. Examples use placeholders only.

Getting Started

1. Create access

Use an API key with only the scopes and Products required.

2. Send requests

Use Authorization: Bearer YOUR_API_KEY over HTTPS.

3. Handle results

Preserve the request ID and distinguish permanent errors from outages.

curl -X POST https://license.neok.ing/api/v1/licenses/verify \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"product":"YOUR_PRODUCT_UUID","license_key":"YOUR_LICENSE_KEY"}'

Installation contract

Installing a Licensed NEOK Application

These endpoints are the installation bootstrap API. Preflight and activation resolve the Product from the customer license key. The application may optionally send the Product UUID or slug as an assertion; it must match the Product attached to the license. After activation, use the returned installation token as a bearer credential. All endpoints are under https://license.neok.ing/api/v1, use JSON, and are rate limited by the installations limiter.

1. Bootstrap

The customer enters the license key. The application may assert its configured Product UUID or slug; it does not need the customer to copy one.

2. Persist credentials

Store the installation token and certificate securely after activation.

3. Validate

Call validate periodically and replace the locally stored certificate with the returned one.

POST /api/v1/installations/preflight

No API key or installation token is required. Send Content-Type: application/json. The request is read-only.

product · optional
string, Product UUID or slug asserted by the application. If omitted, NEOK License derives the Product from the license.
license_key · required
string, maximum 200 characters; 20–200 ASCII letters/numbers with optional separating hyphens.
installation_identifier · required
string, maximum 255 characters; stable installation identity.
domain · optional
string, maximum 255 characters. Product policy may require it.
server_fingerprint · optional
exactly 64 alphanumeric characters.
app_url · optional
valid URL, maximum 2048 characters.
version · optional
string, maximum 100 characters.
IP address
not a request field; the server records the request IP during activation.
curl -X POST https://license.neok.ing/api/v1/installations/preflight \
  -H 'Content-Type: application/json' \
  -d '{"license_key":"CUSTOMER_LICENSE_KEY","installation_identifier":"membership-installation-1","domain":"membership.example.com","server_fingerprint":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","app_url":"https://membership.example.com","version":"1.0.0"}'
{"success":true,"data":{"eligible":true,"product":{"uuid":"PRODUCT_UUID","name":"Example Product"},"license":{"uuid":"LICENSE_UUID","status":"active"}},"request_id":"REQUEST_UUID"}

Success is HTTP 200. Invalid license or installation policy failures are HTTP 422 validation responses. If an optional product assertion does not identify the license’s actual Product, the response is HTTP 422 with PRODUCT_MISMATCH. Throttling is HTTP 429.

POST /api/v1/installations/activate

No API key or installation token is required. The request fields are the preflight fields plus callback_url. Activation derives the Product from license_key unless the optional product assertion is supplied, then creates or reuses the installation identity, revokes its previous active installation token, and issues a new token and certificate.

callback_url · optional
valid HTTPS URL, maximum 2048 characters; required when Product policy requires a callback.
Other fields
product, license_key, installation_identifier, domain, server_fingerprint, app_url, and version use the preflight rules above.
curl -X POST https://license.neok.ing/api/v1/installations/activate \
  -H 'Content-Type: application/json' \
  -d '{"license_key":"CUSTOMER_LICENSE_KEY","installation_identifier":"membership-installation-1","domain":"membership.example.com","server_fingerprint":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","app_url":"https://membership.example.com","version":"1.0.0","callback_url":"https://membership.example.com/license-callback"}'
{"success":true,"data":{"installation":{"uuid":"INSTALLATION_UUID","status":"active"},"installation_token":"nk_install_...","certificate":"BASE64_SIGNED_CERTIFICATE","next_check_at":"2026-01-01T00:00:00.000000Z","grace_until":"2026-01-08T00:00:00.000000Z"},"request_id":"REQUEST_UUID"}

Success is HTTP 201. The response contains no separate license-expiration or product object. The certificate is an opaque base64 value; its signed payload contains certificate, Product, license, installation, validity, online-check, grace, and state-version data.

The Product defaults to an online validation due time 24 hours after activation or validation and an offline-grace timestamp 7 days after that due time. These values are returned as next_check_at and grace_until; use the returned timestamps rather than hard-coding them. Product policy may change these intervals. The certificate validity default is also 24 hours. The API does not authorize an offline request or return an offline success response when the server cannot be reached; any bounded outage behavior is the client application's policy.

POST /api/v1/installations/validate

Authenticate with the installation token returned by activation:

Authorization: Bearer nk_install_...
Content-Type: application/json

The request body is empty. A successful validation response is HTTP 200:

{"success":true,"data":{"valid":true,"code":"VALID","installation_uuid":"INSTALLATION_UUID","status":"active","certificate":"BASE64_SIGNED_CERTIFICATE","next_check_at":"2026-01-01T00:00:00.000000Z","grace_until":"2026-01-08T00:00:00.000000Z"},"request_id":"REQUEST_UUID"}

A valid token causes the certificate to be reissued; replace the stored certificate. An expired, suspended, revoked, blocked, unassigned, not-started, mismatched, or over-limit license returns HTTP 200 with valid:false, the corresponding code, and a resulting installation status. An invalid, expired, deactivated, or normally transferred token is rejected by middleware with HTTP 401 and INSTALLATION_TOKEN_INVALID.

POST /api/v1/installations/deactivate

Authenticate with Authorization: Bearer INSTALLATION_TOKEN. Send an empty JSON body. Success is HTTP 200:

{"success":true,"data":{"deactivated":true,"installation_uuid":"INSTALLATION_UUID"},"request_id":"REQUEST_UUID"}

The installation is marked deactivated and all active tokens are revoked immediately. A repeat request using that token is therefore HTTP 401, not an idempotent success response.

POST /api/v1/installations/transfer

Authenticate with the source installation token. Required fields are destination_installation_identifier (string, maximum 255, no control characters) and idempotency_key (string, maximum 128, only letters, numbers, dot, underscore, colon, and hyphen). Optional fields are domain (maximum 255), server_fingerprint (64 alphanumeric), app_url (URL, maximum 2048), version (maximum 100), callback_url (HTTPS URL), and reason (maximum 255). Product policy must allow transfer.

curl -X POST https://license.neok.ing/api/v1/installations/transfer \
  -H 'Authorization: Bearer nk_install_...' \
  -H 'Content-Type: application/json' \
  -d '{"destination_installation_identifier":"membership-installation-2","domain":"membership.example.com","app_url":"https://membership.example.com","version":"1.0.0","idempotency_key":"transfer-2026-01-01-1","reason":"Server replacement"}'
{"success":true,"data":{"transfer_uuid":"TRANSFER_UUID","installation_uuid":"DESTINATION_INSTALLATION_UUID","installation_token":"nk_install_...","certificate":"BASE64_SIGNED_CERTIFICATE","status":"active","next_check_at":"2026-01-01T00:00:00.000000Z","grace_until":"2026-01-08T00:00:00.000000Z","idempotent":false},"request_id":"REQUEST_UUID"}

Success is HTTP 200. The source installation is transferred and its token revoked; the response credentials belong to the destination. Repeating the same source and idempotency_key for the same destination returns the stored destination credentials with idempotent:true. The transferred source token is accepted only for that recovery retry. Reusing the key for another destination, a disallowed transfer, cooldown, invalid source, or invalid destination returns HTTP 422.

Credential handling

NEOK API key: a developer/server credential for API-key-protected endpoints; it is not used by these installation bootstrap endpoints. Customer license key: the customer secret used only for preflight and activation. Installation token: the credential Membership must persist and send as Authorization: Bearer for validate, deactivate, and transfer. Installation certificate: the signed credential returned by activation/validation for the client’s local licensing use; replace it when validation returns a new certificate. Never log or expose any of these secrets.

Entitlements: activation and validation responses include a deterministic feature map and state. Features are resolved from product defaults and license overrides; inactive or unknown keys are disabled. The signed certificate payload includes the same map, license/product/installation UUIDs, next_check_at, grace_until, and entitlement revision so clients can verify it offline with the product Ed25519 public key.

Authentication

The API has separate credential types. Do not substitute one for another.

CredentialUsed byHeader / useStorage and lifecycle
API keyDeveloper/server API clientsAuthorization: Bearer YOUR_API_KEY; required by API-key routes such as license verify.Secret; created in the Developer/API-key area; may expire, be disabled/revoked, and is stored hashed by NEOK License. Use minimum scopes.
License keyCustomer/application bootstrapJSON license_key on license verify and installation preflight/activate. It is not a bearer header for installation routes.Secret; issued for a license. It is normalized and checked server-side; never log it. Installation validate/deactivate/transfer do not require it.
Installation tokenAn activated application installationAuthorization: Bearer INSTALLATION_TOKEN on installation validate, deactivate, and transfer.Secret; persist securely after activation. It has no expiry set by the current activation flow, but is revoked on deactivation, transfer, replacement activation, or administrative action.
Installation certificateThe activated application’s local licensing implementationReturned in activation, validation, and transfer responses; it is not the HTTP authentication header.Signed value; persist securely if used for local/offline verification. Validation reissues it. Its signed payload contains valid_until, online-check, grace, and state-version values.
Contract keyContract API clientsAuthorization: Bearer YOUR_CONTRACT_KEY on /api/v1/contracts/*.Secret; created for a contract; can expire, be disabled, revoked, IP-restricted, or blocked by contract status. It is unrelated to installation bootstrap.

Installation routes do not use an API key: preflight and activate have no credential middleware, while validate, deactivate, and transfer use the installation-token middleware. The same HTTP Bearer syntax is distinguished by the route middleware and the stored credential hash.

Products & Product UUIDs

A Product UUID identifies the NEOK License Product whose licensing policy and licenses are being checked. An administrator can copy it from the Product’s Licensing detail page or from the developer portal’s assigned Products page. It is an identifier, not a secret, and may be configured in the licensed application. The installation endpoints also accept a Product slug. For bootstrap, product is optional because the server resolves the actual Product through the license; if supplied, it is an application-controlled assertion and must match.

The public docs show https://license.neok.ing/api/v1 as the current API base. Keep the base URL configurable in the third-party application.

License Verification

POST /api/v1/licenses/verify

Requires an API key with the licenses.verify scope and the Authorization: Bearer header. Send Accept: application/json and Content-Type: application/json.

FieldTypeRequiredDescription
productstringYesProduct UUID or slug.
license_keystringYesMaximum 500 characters at request validation; the effective license-key format is 20–200 ASCII letters/numbers with optional separating hyphens after normalization.
activation_uuidUUIDNoOptional activation UUID used to validate that activation’s identifier.
curl -X POST https://license.neok.ing/api/v1/licenses/verify \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"product":"YOUR_PRODUCT_UUID","license_key":"YOUR_LICENSE_KEY"}'
{"success":true,"data":{"valid":true,"code":"VALID","license":{"uuid":"LICENSE_UUID","product":"PRODUCT_UUID","status":"active","starts_at":"2026-01-01T00:00:00.000000Z","expires_at":null,"support_expires_at":null}},"request_id":"REQUEST_UUID"}

The endpoint returns HTTP 200 for both a valid and an invalid entitlement result. Invalid results use data.valid:false and a code such as LICENSE_NOT_FOUND, PRODUCT_MISMATCH, LICENSE_EXPIRED, LICENSE_REVOKED, LICENSE_SUSPENDED, LICENSE_BLOCKED, LICENSE_UNASSIGNED, LICENSE_NOT_STARTED, or ACTIVATION_LIMIT_REACHED. Missing/invalid API credentials return 401 API_UNAUTHORIZED; product access restrictions return 404; malformed input returns 422; throttling returns 429.

Installation Identity

The installation identity is the application-provided installation_identifier, trimmed and hashed with SHA-256 by NEOK License. It is scoped to a Product. It is not automatically derived from hostname, domain, URL, or IP. Use a stable identifier for the same installed application; a server-generated UUID is appropriate for a server application.

domain is an optional policy value normalized to a lowercase host-like value. app_url is stored as application metadata. server_fingerprint is an optional 64-character alphanumeric value. The request IP is observed by the server and is not a JSON field. If Product policy disallows IP or domain changes, reactivation of the same identifier after that change is rejected. A server move or identity change should use transfer when the Product permits it; transfer is atomic and creates a new destination installation.

Persisting Installation Credentials

ValueStorage decisionReason
product / Product UUIDOptional application configurationUseful as an application-controlled assertion during bootstrap; the server derives the actual Product from the license.
installation_uuidRequired to retainReturned by activation and used to identify the installation in local records.
installation_tokenRequired secretThe bearer credential required for later validation, deactivation, and transfer.
certificateRequired if local certificate licensing is usedReturned as the signed installation certificate; replace it after validation.
Certificate expiryOptional derived valueNo separate response field exists; the signed certificate payload contains valid_until.
Customer license keyBootstrap-only secret; erase after successful activation if not otherwise neededInstallation validate/deactivate/transfer authenticate with the installation token instead.
NEOK API keyDo not put in the customer/browser applicationIt is for API-key-protected developer/server endpoints, not installation bootstrap.

Integrating NEOK License Into a Server Application

  1. Optionally configure the Product UUID in NEOK Membership as an application-controlled assertion; the License server can derive it from the license.
  2. Ask the administrator/customer for the NEOK license key.
  3. Call preflight with the license key and stable installation identifier. Include the configured Product UUID or slug when asserting application identity. A successful preflight creates no installation state.
  4. Call activate with the same identity and optional domain, fingerprint, URL, version, and HTTPS callback URL.
  5. Persist the installation UUID, installation token, and certificate securely. Continue the installer only after HTTP 201.
  6. At startup and on the Product’s schedule, call validate with the installation bearer token. Store the returned certificate.
  7. On valid:true/VALID, continue. On a definitive license code, stop or request customer action according to the code. On timeout, 502, 503, or 504, apply the application’s bounded outage policy; this API does not return an offline validation response.
  8. Call deactivate when removing the installation. For a server move, call transfer with a new stable identifier and an idempotency key.
# Later validation
curl -X POST https://license.neok.ing/api/v1/installations/validate \
  -H 'Authorization: Bearer YOUR_INSTALLATION_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json'
# Deactivation
curl -X POST https://license.neok.ing/api/v1/installations/deactivate \
  -H 'Authorization: Bearer YOUR_INSTALLATION_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json'

Errors, Statuses & Retry Policy

Standard middleware errors use this exact shape:

{"success":false,"error":{"code":"INSTALLATION_TOKEN_INVALID","message":"A valid installation token is required."},"request_id":"REQUEST_UUID"}

Validation failures from Laravel use HTTP 422 with a message and an errors object keyed by field; the generated request ID is available in the X-Request-Id response header. Installation-token middleware uses HTTP 401 with INSTALLATION_TOKEN_INVALID. API-key middleware uses HTTP 401 with API_UNAUTHORIZED. The implementation may return framework 404 responses for unknown products/resources, 409 for activation-capacity conflicts in other API areas, 429 for throttling, and 500 for an unhandled server error. These five installation routes do not define a normal 403 response.

Do not retry: invalid credentials, invalid license, wrong Product, expired/revoked/suspended/blocked license, malformed input, domain/IP policy rejection, transfer conflict, or activation limit until the customer/admin changes the condition. Retry with bounded backoff: network timeout, 502, 503, 504, and 429 according to Retry-After when supplied. Preserve request_id for support.

API Scopes & Security Best Practices

EndpointCredentialScope
/api/v1/licenses/verifyAPI keylicenses.verify
/api/v1/installations/preflightNoneNone
/api/v1/installations/activateNoneNone
/api/v1/installations/validateInstallation tokenNone
/api/v1/installations/deactivateInstallation tokenNone
/api/v1/installations/transferSource installation tokenNone
  • Use HTTPS and keep the API base URL configurable.
  • Never expose API keys, license keys, installation tokens, or certificates in browser JavaScript, logs, support diagnostics, or exception reports.
  • Protect persisted credentials with the application’s server-side secret storage and restrict file/database permissions.
  • Use API keys only server-side with the minimum Product restrictions and scopes required; rotate or revoke them when appropriate.

API Versioning

All documented endpoints use /api/v1/. The current project policy treats additive response fields as compatible; changed meanings and breaking changes require a new API version. The machine-readable update specification is available to authorized License administrators from the protected AdminCP Developer API Documentation page.

API reference

Licensing

POST/api/v1/licenses/verify

Verify a license entitlement.

licenses.verify

POST/api/v1/licenses/activate

Activate an installation.

licenses.activate

POST/api/v1/licenses/heartbeat

Refresh an active installation.

licenses.heartbeat

POST/api/v1/licenses/deactivate

Deactivate an installation locally.

licenses.deactivate

API reference

Updates & Downloads

POST/api/v1/updates/check

Check for an entitled update.

updates.check

GET/POST/api/v1/downloads

List entitled packages.

downloads.list

POST/api/v1/downloads/{uuid}/authorize

Issue a temporary download authorization.

downloads.download

API reference

Signing

GET/api/v1/signing-keys

Read public Ed25519 signing keys for entitlement verification.

Public

GET/api/v1/products/{uuid}/signing-keys

Read public signing keys for a Product.

products.signing_keys.read

API reference

Installations

POST/api/v1/installations/preflight

Validate installation eligibility without creating state.

Product + license bootstrap

POST/api/v1/installations/activate

Create or reuse an installation and issue a token/certificate.

Product + license bootstrap

POST/api/v1/installations/validate

Refresh an installation certificate online.

Installation token

POST/api/v1/installations/deactivate

Deactivate an installation and revoke its token.

Installation token

POST/api/v1/installations/transfer

Atomically move an installation to a new identity and issue replacement credentials.

Source installation token

API reference

Telemetry

POST/api/v1/telemetry

Submit one telemetry event.

telemetry.write

POST/api/v1/telemetry/batch

Submit a telemetry batch.

telemetry.write

GET/api/v1/telemetry/events

Read permitted telemetry events.

telemetry.read

API reference

Contracts

POST/api/v1/contracts/verify

Verify a contract key.

Contract Key

GET/api/v1/contracts/info

Read contract information.

Contract Key

GET/api/v1/contracts/products

List products available to the contract.

Contract Key

GET/api/v1/contracts/generators

List delegated generators.

Contract Key

GET/POST/api/v1/contracts/licenses

Create and list contract-generated licenses.

Contract Key

GET/api/v1/contracts/licenses/{uuid}

Read a contract-generated license.

Contract Key

POST/api/v1/contracts/licenses/{uuid}/revoke

Revoke a contract-generated license.

Contract Key

Common errors

CodeHTTPMeaning
API_UNAUTHORIZED401The credential is missing, invalid, disabled, or expired.
LICENSE_EXPIRED403Stop licensed functionality or request renewal.
ACTIVATION_LIMIT_REACHED409Deactivate another installation before retrying.
RATE_LIMIT_EXCEEDED429Back off and retry later.
VALIDATION_ERROR422Correct the request fields before retrying.
LICENSE_SERVICE_CONFIGURATION_ERROR503Retry after the license service restores its signing infrastructure.
INTERNAL_ERROR500Treat as transient and use bounded retry behavior.