KDPika Licensing Sign inStart free
API v1
Show all languages
Browse documentation

Choose a page in the navigation drawer.

KDPika Licensing · API v1 Intermediate

Retrieve a license

Read a scoped unsigned license summary.

On this page
Updated 2026-10-17 UTCDownload Markdown

Read a scoped unsigned license summary.

What you will learn

  • Send this exact operation with its required credentials and fields.
  • Interpret success, refusal, side effects and retry risks.

Prerequisites

Method and path

GET /api/v1/licenses/{license}

When to use it

Read an unsigned management summary from your trusted backend for support or reconciliation. This is not an activation response, a list operation or a replacement for signed installation validation.

Authentication and environment

Use Authorization: Bearer YOUR_DEVELOPER_API_KEY with scope licenses:read. The key fixes the organization/product/environment; X-Environment cannot override it.

Request headers

HeaderRequiredDescriptionExample
AcceptrecommendedJSON responsesapplication/json
AuthorizationyesScoped backend-only developer keyBearer YOUR_DEVELOPER_API_KEY

Path and query parameters

ParameterTypeRequiredDescriptionExample
licensestringyesOpaque license identifier. Scoped lookup returns 404; no UUID/length contract.YOUR_LICENSE_ID

No pagination, filtering, sorting or list query parameters are implemented. Use JSON rather than relying on alternate form/query parsing.

Request body

No request body is consumed. Send this GET request without a body.

The schemas describe supported recommended input types. Current PHP parsing trims/casts strings and may ignore unknown fields; it is not strict JSON-schema validation. Invalid optional descriptive fields can become empty/null. Conventions describes malformed JSON and alternate parsing.

Success response

HTTP 200. The following is a real isolated Sandbox capture; secret values are replaced with explicit placeholders.

jsonjson
{
  "success": true,
  "license": {
    "id": "lic_578581bd80c2a672da30fc29",
    "license_key_mask": "TESTDOCS-••••-••••-HAHH",
    "status": "active",
    "environment": "sandbox",
    "starts_at": "2026-10-17T10:39:35+00:00",
    "expires_at": "2026-11-16T10:39:35+00:00",
    "created_at": "2026-10-17T12:39:35+00:00",
    "updated_at": "2026-10-17T12:39:35+00:00",
    "max_devices": 2,
    "active_devices": 0,
    "max_activations": 4,
    "activation_count": 0,
    "min_version": null,
    "max_version": null,
    "revocation_reason": null,
    "product": {
      "id": "app_a642cd24641ec976626b",
      "name": "Docs fixture product"
    },
    "plan": {
      "id": "plan_3bf54c099326639ee183661f",
      "name": "Fixture plan",
      "slug": "fixture-plan",
      "billing_type": "monthly",
      "price": 19.5,
      "currency": "EUR"
    },
    "customer": {
      "id": "cus_d7db5516f8377f89a869e293",
      "name": "Synthetic contract customer",
      "email": "contract-6b9f0843f1d3609f@example.invalid",
      "company": null,
      "country": null
    }
  },
  "request_id": "req_2e71dc11a311d6a5d34c50cb"
}

Response fields

FieldTypeRequiredConstraintsDescriptionExample
successbooleanyesconst=trueThe operation succeeded.true
licenseobjectyesSee descriptionUnsigned management summary; no full license key or activation token.Nested object/array shown in full JSON
license.idstringyesSee descriptionLicense identifier."lic_578581bd80c2a672da30fc29"
license.license_key_maskstringyesSee descriptionDisplay-only masked key."TESTDOCS-••••-••••-HAHH"
license.statusstringyesenum=["active", "suspended", "revoked", "expired", "pending", "cancelled"]Lowercase stored license status."active"
license.environmentstringyesenum="sandbox", "[live"]Bound environment."sandbox"
license.starts_atstring or nullyesformat="date-time"Configured start."2026-10-17T10:39:35+00:00"
license.expires_atstring or nullyesformat="date-time"Configured expiry."2026-11-16T10:39:35+00:00"
license.created_atstring or nullyesformat="date-time"Creation timestamp."2026-10-17T12:39:35+00:00"
license.updated_atstring or nullyesformat="date-time"Last stored update."2026-10-17T12:39:35+00:00"
license.max_devicesintegeryesSee descriptionConcurrent device limit.2
license.active_devicesintegeryesSee descriptionActive device count.0
license.max_activationsinteger or nullyesSee descriptionCumulative activation cap.4
license.activation_countintegeryesSee descriptionConsumed activations.0
license.min_versionstring or nullyesSee descriptionLower version bound.null
license.max_versionstring or nullyesSee descriptionUpper version bound.null
license.revocation_reasonstring or nullyesSee descriptionOptional reason.null
license.productobjectyesSee descriptionProduct summary.Nested object/array shown in full JSON
license.product.idstringyesSee descriptionPublic App ID."app_a642cd24641ec976626b"
license.product.namestringyesSee descriptionProduct name."Docs fixture product"
license.planobjectyesSee descriptionPlan summary.Nested object/array shown in full JSON
license.plan.idstringyesSee descriptionInternal product-plan identifier."plan_3bf54c099326639ee183661f"
license.plan.namestringyesSee descriptionPlan display name."Fixture plan"
license.plan.slugstringyesSee descriptionPlan slug."fixture-plan"
license.plan.billing_typestringyesSee descriptionLowercase product-plan billing type."monthly"
license.plan.pricenumberyesSee descriptionPHP float; preserve numeric tokens such as19.0 for signatures.19.5
license.plan.currencystringyesSee descriptionConfigured product-plan currency."EUR"
license.customerobjectyesSee descriptionCustomer information; personal data, not an authentication credential.Nested object/array shown in full JSON
license.customer.idstringyesSee descriptionCustomer public identifier."cus_d7db5516f8377f89a869e293"
license.customer.namestring or nullyesSee descriptionOptional customer name."Synthetic contract customer"
license.customer.emailstringyesSee descriptionCustomer email associated with this license."contract-6b9f0843f1d3609f@example.invalid"
license.customer.companystring or nullyesSee descriptionOptional company.null
license.customer.countrystring or nullyesSee descriptionOptional country.null
request_idstringyespattern="^req_[a-f0-9]{24}$"Request correlation ID. Replay bodies can retain an earlier ID."req_2e71dc11a311d6a5d34c50cb"

Response headers

X-Request-ID identifies the current request. Successful limited calls include X-RateLimit-Remaining and X-RateLimit-Reset, the latter in Unix seconds.

Errors omit those rate headers and Retry-After. See the complete rate-limit reference.

Error responses

CodeHTTPCauseFixRetryable?
LICENSE_NOT_FOUND404Management scoped license absentCorrect org/product/environment/IDNo blind retry; apply the correction
INTERNAL_ERROR500Uncaught exception at global boundarySafe retry only for idempotent operation; preserve request_idOnly safe reads after backoff; reconcile mutations
INVALID_API_KEY401Missing/invalid/revoked key or prohibited subscription/environmentReplace/configure keyNo blind retry; apply the correction
FORBIDDEN403Developer scope missing; checkout account role not OWNER/ADMINUse authorized scope/roleNo blind retry; apply the correction
RATE_LIMITED429Bucket exhausted; wait for window reset; no licensing Retry-After headerAfter window; apply jitterAfter the 60-second window, with jitter

An unregistered path or wrong method is handled by the global router as NOT_FOUND (HTTP 404), not405. Every licensing error has success=false, error.code, error.message and request_id. Errors keeps SDK-local and operational namespaces separate.

Side effects

Reads the scoped license/customer/product/plan summary and active-device count. Does not reveal the complete key, issue a token, mutate license state or emit an audit/webhook/email. Successful key authentication updates the developer key's last-used timestamp. Customer fields are personal data: restrict the response to authorized operators.

Authenticated requests can consume the organization monthly API usage counter once tenant context is established, including some failures. This usage counter is currently warning-only, not a monthly API rejection gate. Developer-key authentication can update last_used_at before a later failure. No request body or Authorization value is saved by the API metadata logger.

Idempotency and concurrency

GET is safe to retry with bounded backoff. No Idempotency-Key is consumed. A summary is a snapshot, not a caller version/ETag concurrency precondition; do not use it as a lock before another mutation.

Rate limits

Developer pre-authentication:240 per60 seconds per IP. Scoped reads:180 per60 seconds per developer-key license-read bucket.

Fixed windows begin with the first hit after expiry. Attempted calls consume every bucket they reach, even if later business validation fails.

Runnable examples

These are complete native HTTP transport programs, not full SDK clients or entitlement verifiers. JSON request bodies and credentials come from KDP_* environment variables. They print raw responses to a private terminal; do not send their stdout to application/CI logs. Use the P5 verification guides before granting access.

JavaScript (Node)

Save every file shown below. Install the exact runtime/dependencies from the complete example archive README. Set the endpoint request JSON in KDP_BODY_JSON and the documented KDP_* credential variables in your private process environment.

Run node request.mjs get-license. Expected: full JSON, exit 0 for success, exit 1 for an API rejection; exit 2 for setup/transport failure. The cURL sample checks HTTP status; parse JSON before use. Never authorize a feature from transport output alone.

request.mjsjavascript
// Purpose: inspect one Sandbox API operation; not an entitlement verifier.
// Requirements: Node18.20+ native fetch, trusted server/Electron main process.
// Run: node request.mjs public-key. Expected: JSON; exit0 success/1 rejection/2 transport or setup.
// Credentials/body use KDP_* environment variables. Output can contain new secrets.
// Use a private terminal, never application/CI logs. Verify signed state separately.
const operations = Object.freeze({
  "public-key": {
    "method": "GET",
    "path": "/public-key",
    "auth": "public"
  },
  "create-license": {
    "method": "POST",
    "path": "/licenses",
    "auth": "developer"
  },
  "activate": {
    "method": "POST",
    "path": "/licenses/activate",
    "auth": "publicApp"
  },
  "validate": {
    "method": "POST",
    "path": "/licenses/validate",
    "auth": "activation"
  },
  "deactivate": {
    "method": "POST",
    "path": "/licenses/deactivate",
    "auth": "activation"
  },
  "get-license": {
    "method": "GET",
    "path": "/licenses/{license}",
    "auth": "developer"
  },
  "revoke": {
    "method": "POST",
    "path": "/licenses/{license}/revoke",
    "auth": "developer"
  },
  "suspend": {
    "method": "POST",
    "path": "/licenses/{license}/suspend",
    "auth": "developer"
  },
  "restore": {
    "method": "POST",
    "path": "/licenses/{license}/restore",
    "auth": "developer"
  },
  "renew": {
    "method": "POST",
    "path": "/licenses/{license}/renew",
    "auth": "developer"
  },
  "reset-devices": {
    "method": "POST",
    "path": "/licenses/{license}/reset-devices",
    "auth": "developer"
  }
});
function required(name) {
  const value = process.env[name];
  if (!value || /[\r\n]/.test(value)) throw new Error(`Set a valid ${name}.`);
  return value;
}
async function main() {
  const name = process.argv[2];
  if (process.argv.length !== 3 || !Object.hasOwn(operations, name)) throw new Error('Choose a documented operation.');
  const operation = operations[name];
  const base = (process.env.KDP_BASE_URL || 'https://licensing.kdpika.com/api/v1').replace(/\/+$/, '');
  const url = new URL(base);
  const loopback = url.protocol === 'http:' && ['127.0.0.1', 'localhost'].includes(url.hostname) && process.env.KDP_ALLOW_LOOPBACK === '1';
  if ((url.protocol !== 'https:' && !loopback) || url.username || url.password || url.search || url.hash || url.pathname !== '/api/v1') throw new Error('Use an HTTPS API URL ending in /api/v1; loopback HTTP requires explicit test opt-in.');
  let path = operation.path;
  if (path.includes('{license}')) {
    const id = required('KDP_LICENSE_ID');
    if (!/^[A-Za-z0-9_-]+$/.test(id)) throw new Error('Use the simple license ID issued by this platform with this sample.');
    path = path.replace('{license}', id);
  }
  const headers = {'Accept': 'application/json', 'User-Agent': 'KDPikaDocsExample/1.0 Node'};
  if (operation.auth === 'developer') {
    const key = required('KDP_API_KEY');
    if (!key.startsWith('lmp_sandbox_')) throw new Error('Use a Sandbox developer key.');
    headers.Authorization = `Bearer ${key}`;
  } else if (operation.auth === 'activation') headers.Authorization = `Bearer ${required('KDP_ACTIVATION_TOKEN')}`;
  else if (operation.auth === 'publicApp') {
    headers['X-App-ID'] = required('KDP_APP_ID'); headers['X-Environment'] = 'SANDBOX';
  }
  if (name === 'create-license') headers['Idempotency-Key'] = required('KDP_IDEMPOTENCY_KEY');
  if (name === 'validate') headers['X-Installation-ID'] = required('KDP_INSTALLATION_ID');
  let body;
  if (operation.method === 'POST') {
    body = process.env.KDP_BODY_JSON || '{}';
    const value = JSON.parse(body);
    if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('KDP_BODY_JSON must be a JSON object.');
    headers['Content-Type'] = 'application/json';
  }
  const response = await fetch(base + path, {method: operation.method, headers, body, redirect: 'error', signal: AbortSignal.timeout(15000)});
  const raw = await response.text();
  const result = JSON.parse(raw);
  process.stdout.write(raw + '\n');
  if (!response.ok || result.success !== true) {
    process.stderr.write('The API rejected this request. Inspect error.code and request_id; reconcile mutations before retrying.\n');
    return 1;
  }
  return 0;
}
main().then(code => {process.exitCode = code;}).catch(() => {
  process.stderr.write('Request unavailable or configuration invalid. Check KDP_* settings/connectivity; do not blindly retry mutations.\n');
  process.exitCode = 2;
});

Expected result

A correctly authenticated eligible request returns the documented HTTP status and schema. Check the actual success/error result and request IDs; a successful transport or unsigned management response does not grant a feature.

Common mistakes

  • Mixing Public App IDs, internal product IDs and product-plan IDs.
  • Assuming a mutation timeout means no work committed, or assuming Idempotency-Key protects every action.
  • Logging raw responses that contain a newly issued license key or activation token.

Security notes

Keep lmp_ credentials out of shipped desktop/browser code. Verify signed payload bytes and product/environment/installation/status/time/feature policy. The examples do not fix the documented idempotency, cache canonicalization or current-key-only rotation limitations.

Troubleshooting

SymptomCauseAction
LICENSE_NOT_FOUND (HTTP 404)Management scoped license absentCorrect org/product/environment/ID
INTERNAL_ERROR (HTTP 500)Uncaught exception at global boundarySafe retry only for idempotent operation; preserve request_id
INVALID_API_KEY (HTTP 401)Missing/invalid/revoked key or prohibited subscription/environmentReplace/configure key
FORBIDDEN (HTTP 403)Developer scope missing; checkout account role not OWNER/ADMINUse authorized scope/role
RATE_LIMITED (HTTP 429)Bucket exhausted; wait for window reset; no licensing Retry-After headerAfter window; apply jitter

Next steps

Read idempotency and signed responses before integrating a purchase-to-license workflow.

Was this page helpful?

Choose an answer

Do not include personal data, license keys or credentials. Retained for up to 30 days. This is not a support inbox.

Search documentation

Arrow keys to move, Enter to open, Escape to close. Search stays in your browser.

Cookie settings