License Validation API Integration Guide

Technical reference for external client applications integrating with the JCP-VISION licensing platform. This page mirrors the required activation, validation, cache, outage, and rebind behavior.

Reason code driven logic Idempotent activation Offline policy safeguards Environment update propagation

Base URL and Endpoints

Set your base URL to the deployed License Validation API.

  • POST /client/activate
  • POST /client/validate
  • POST /client/fingerprint/generate
  • GET /health

Fingerprint and Request Contract

Use the same payload for activate and validate.

Canonical fingerprint algorithm
import hashlib

def generate_machine_fingerprint(mac: str, hostname: str, machine_id: str) -> str:
		payload = f"{mac}{hostname}{machine_id}"
		return hashlib.sha256(payload.encode("utf-8")).hexdigest()
Shared JSON body
{
	"app_name": "scanstock",
	"license_key": "LICS-JCV-1234-ABCD",
	"machine_fingerprint": "7a2b7d3d5c0f6d9e4cc7bdb7f45d2f0dc7f9b1197a68fd4d8db03337df2ab4c0",
	"hostname": "workstation-01",
	"app_version": "1.4.0"
}

Activation response behavior

Activation success returns HTTP 200 with status=success and activation_id. Activation business failures return HTTP 400 for invalid/expired/revoked/inactive licenses, blocked machines, or max instance limits.

Validation response behavior

Validation business outcomes return HTTP 200 always. Gate your logic with is_valid and reason_code. status is success when valid and error when invalid.

Runtime Decision Logic

Apply this sequence exactly in the client runtime.

First run on machine

  1. Generate fingerprint using local system values.
  2. Activate using POST /client/activate.
  3. Validate immediately after successful activation.
  4. Cache policy if is_valid=true.
  5. Deny execution if validation returns invalid.

Startup and periodic checks

  1. Call validate directly on each check cycle.
  2. Update cache when valid.
  3. Enforce deny path when invalid.
  4. Offline fallback only on transport-level failures.

Reason codes to handle

Valid and executable

  • validation_ok
  • license_active
  • license_expired_in_grace

License-level deny

  • license_expired
  • license_not_found
  • license_revoked
  • license_inactive
  • license_invalid_expiry

Machine-level

  • machine_not_activated
  • machine_blocked

Caching, Offline, and Recovery

Confirmed outage logic and safe rebind behavior.

Minimum cache fields

Persist accepted_machine_fingerprint, checked_at, policy_updated_at, license_state, machine_state, reason_code, full license_snapshot, and full environment_updates after successful validation.

Offline execution conditions

Allow offline execution only when validate failed on transport, /health is unreachable, allow_offline is true, cached fingerprint matches, and last known state is executable (active or grace).

Grace behavior

When is_valid=true and reason_code=license_expired_in_grace, allow app execution, show user warning, and continue periodic validation.

Rebind behavior

For machine_not_activated, perform one controlled activate retry, then validate once more. If still invalid, deny execution. Do not loop activation indefinitely.

Runtime pseudocode
startup_or_periodic_check():
	fp = generate_machine_fingerprint(local_mac, local_hostname, local_machine_id)

	try:
		result = POST /client/validate with fp
	except network_error:
		if GET /health succeeds:
			deny("validation transport failed while service is reachable")
		else:
			return offline_decision(fp, cached_state)

	if result.is_valid:
		cache(result, fp)
		if result.license_state == "grace":
			show_warning(result.reason)
		allow()
		return

	if result.reason_code == "machine_not_activated":
		act = POST /client/activate with fp
		if act succeeds:
			retry = POST /client/validate with fp
			if retry.is_valid:
				cache(retry, fp)
				allow()
				return

	deny(result.reason_code + ": " + result.reason)

Developer Acceptance Checklist

Integration is complete when all checks pass.

  • First run activates then validates.
  • Normal startup uses validate only.
  • Grace state allows app with warning.
  • Blocked machine always denies execution.
  • Not-activated machine triggers one rebind attempt.
  • Transport failure with reachable /health does not use offline mode.
  • Confirmed outage uses cached allow_offline plus fingerprint match.
  • Cached environment_updates are persisted and applied.
  • reason_code drives all decision branches.
  • No infinite activation retry loops.