Error Codes

HTTP status and license status

HTTP status tells you whether the request was processed. The isValid field tells you whether to allow licensed functionality. Check both. These rules describe public validate and activate requests; admin endpoints have separate authentication requirements.

StatusMeaningNext step
200License decision returned. Missing, revoked, expired, blocked, or exhausted licenses can return isValid: false.Require isValid === true before unlocking features.
400Invalid request or missing activation nonce.Check fields and key format. Fetch a nonce before activation.
422The supplied activation nonce is expired, invalid, or already used.Fetch a fresh nonce.
429An IP, tenant, or license abuse limit was reached.Honor Retry-After when present and use bounded backoff.
5xx / timeoutNo reliable license decision is available.Retry validation a limited number of times. Offline access requires a verified signed token.

Error bodies are not always license response objects: request errors may be text or problem details, and a proxy may return HTML. Do not assume every non-200 response is JSON.

Machine-readable rejection reasons

Use errorCode when supplied. Keep message for display, not string matching. Some rejection paths return no error code, so retain a generic invalid-license branch.

CodeMeaningAction
NotFoundNo active matching licenseCheck the key or contact the vendor.
ProductInactiveThe product is inactiveVendor checks product status.
ExpiredExpiry and any post-expiry grace have elapsedRenew the license.
VersionBlockedApp version is outside the configured rangeUse an entitled version or update the license.
IpBlocked / RegionBlockedNetwork restriction rejected the requestCheck allowlists and the customer's network.
VendorSubscriptionLapsedVendor subscription needs attentionVendor reviews Billing.
PoolExhaustedTenant activation allowance exhaustedReview usage and plan capacity.
SeatsExhaustedNo seats available on the keyRemove an unused device or increase the key's limit.

A bounded validation request

This JavaScript example makes at most three attempts and never grants access on connection failure. After a valid response, check your product and feature entitlements. For signed offline caching, use an official SDK.

JavaScript
async function validateLicense(licenseKey) {
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      const res = await fetch('https://api.permitcore.dev/api/v1/validate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ licenseKey, version: '2.3.1' }),
        signal: AbortSignal.timeout(10000)
      });
      if ((res.status === 429 || res.status >= 500) && attempt < 2) {
        const header = res.headers.get('Retry-After');
        const seconds = header === null ? NaN : Number(header);
        const delay = Number.isFinite(seconds) ? seconds * 1000
          : header ? Date.parse(header) - Date.now() : NaN;
        if (Number.isFinite(delay) && delay > 30000)
          return { isValid: false, unavailable: true, retryAfter: header };
        await sleep(Number.isFinite(delay) ? Math.max(0, delay) : 1000 * 2 ** attempt);
        continue;
      }
      if (!res.ok) return { isValid: false, httpStatus: res.status };
      const data = await res.json();
      if (typeof data?.isValid !== 'boolean')
        return { isValid: false, unavailable: true };
      return data;
    } catch {
      if (attempt < 2) { await sleep(1000 * 2 ** attempt); continue; }
    }
  }
  return { isValid: false, unavailable: true, message: 'License service unavailable.' };
}

Activation retries need extra care

A timeout does not prove activation failed: the server may have committed it before the connection was lost. Blindly retrying count-based activation can consume another seat. Persist successful installation state, use a stable device ID for node-locked licenses, and fetch a new nonce for each new request. Reconcile uncertain results instead of looping indefinitely.

Offline access is a separate decision

A network failure is not a valid license. Offline access requires a previously issued signed token, a trusted public key, a valid expiry, and matching license/device binding where applicable. A cached JSON success or local “activated” flag alone is insufficient. Do not replace an explicit online rejection with an older cached success.