Use the documented licensing operations over HTTPS and distinguish transport success, authenticated license state and management actions.
What you will learn
- Choose the base URL, credential and environment for each operation.
- Interpret response envelopes and request IDs.
- Avoid assuming pagination, browser CORS or universal retry guarantees.
Prerequisites
- Complete the Sandbox quickstart.
- Read authentication.
- Keep developer API keys on trusted servers only.
Base URL and version
The production API base is https://licensing.kdpika.com/api/v1. Endpoint paths shown as /licenses are relative to that base. SDK constructors use the site origin without /api/v1; do not append the version twice.
Use HTTPS with certificate and hostname verification. Do not disable TLS checks or silently follow a redirect to another origin carrying credentials. Loopback HTTP is used only by the isolated local test environment, not as a production integration setting.
The route prefix identifies API v1, independently of SDK package versions. Do not assume a requested /api/v2 path exists. Use the versioned contract and inspect documented compatibility limitations when updating a client.
Headers and credential boundaries
| Header | Use | Notes |
|---|---|---|
| Accept: application/json | Recommended on every API request | Responses use JSON; this is not content negotiation for HTML |
| Content-Type: application/json | Send for JSON bodies | Create reads decoded JSON directly; client actions read JSON when this media type is present |
| X-App-ID | Public activation | The public app_ identifier, not the internal product ID or slug |
| X-Environment | Activation | SANDBOX by default; uppercase-normalized SANDBOX or LIVE |
| Authorization: Bearer act_ credential | Validate/deactivate | Token determines installation/product/environment |
| X-Installation-ID | Validation | Transport-optional, but always send the same persisted 16–255 character ID; clients require its signed binding |
| Authorization: Bearer lmp_ credential | Management operations | Key fixes product, organization, environment and scopes |
| Idempotency-Key | Required on create only | Trimmed 8–190 characters; not a universal mutation retry mechanism |
| User-Agent | Recommended | Identify your application/version without secrets or customer data |
X-Environment does not override a developer key's environment or an activation token's binding. Public-key retrieval has no environment selector. The public App ID can be shared across environments; customer/license/key/webhook state is isolated according to the product contract.
Header names are case-insensitive. Bearer scheme matching is case-insensitive, but credential values must remain exact. Do not put credentials in query strings, URLs, documentation feedback or browser-delivered management code.
JSON request behavior
Send objects using the exact documented field names and types. Optional unknown fields are ignored; that does not create a metadata, bulk or feature-management API. Request parsing is permissive in the current backend: malformed/scalar JSON becomes an empty array of inputs rather than a dedicated INVALID_JSON licensing error. Required-input validation then determines the actual error, or an operation with no consumed body fields may still succeed.
The string validator trims and casts PHP values rather than enforcing strict JSON types. Do not depend on that coercion. Invalid optional descriptive lengths may become empty/null rather than reject the whole request. Detailed endpoint constraints describe these differences; the OpenAPI recommended-input schema is not a claim that every malformed alternate representation is rejected identically.
Create always reads JSON. Other input-consuming client/status/renew actions select JSON when Content-Type contains application/json; otherwise they read merged query/form fields with POST taking precedence. Prefer JSON consistently. There is no documented bulk request body or batch operation.
Response envelopes
| Response | Shape and interpretation |
|---|---|
| Successful key retrieval | success=true, key, request_id |
| Successful create | success=true, license with the new license_key, request_id; HTTP 201; a stored replay also returns this key |
| Successful activation | success=true, activation_token, signed license, request_id |
| Successful validation | success=true, valid=true, signed license, request_id |
| Successful deactivation | success=true, deactivated=true, request_id |
| Successful management read | success=true, unsigned license summary, request_id |
| Successful state/renew/reset action | success=true, documented result fields, request_id |
| Licensing error | success=false, error.code, error.message, request_id with the actual non-2xx HTTP status |
The success flag alone does not authorize a paid feature. Verify the signed license and enforce product/environment/installation/status/time/entitlement checks. A management summary is not an Ed25519 envelope. Deactivation success confirms release; it is not an active license payload.
The server encodes UTF-8 JSON without escaped slashes, preserves integral floats and emits Unicode directly with the documented separator exceptions. Preserve numeric tokens for signature verification; a normal JavaScript number round trip can change the signed bytes.
Request IDs and timestamps
Every routed licensing response has an X-Request-ID header. JSON success/error bodies carry request_id. Record the current header, HTTP status, method/path and UTC time for support; redact Authorization, license keys, activation tokens and customer payloads.
An idempotent create replay returns the original stored JSON body, including its original request_id, while the response header identifies the current request. Keep both values when reporting replay issues. A request ID is correlation metadata, not a credential or proof that a retry is safe.
Response builders emit ISO8601 UTC-labelled timestamps with an explicit offset. Nullable dates remain null, not empty strings. Signed server_time, next_validation_at and offline_grace_until have their own policy meaning. A known non-UTC database-session discrepancy can affect stored display timestamps such as created_at; do not treat that field as a freshness proof.
Lists, query parameters and sorting
The currently registered API v1 has 11 operations. There is no public license-list, customer CRUD, product CRUD, plan/feature CRUD, webhook-management or device-list endpoint. Manage those resources in the supported workspace UI.
No v1 operation implements pagination, cursor, page, limit, filtering or sorting query parameters. Do not invent ?page=, ?status= or a list response schema. Path license identifiers are opaque resource IDs scoped by the developer key; a mismatched/unknown ID returns the documented not-found response.
Browser CORS and server clients
The licensing API does not publish Access-Control-Allow-Origin or an OPTIONS preflight handler. Wrong methods/unknown /api/ routes return NOT_FOUND404, not a promised405. A cross-origin browser fetch with custom credentials/headers is therefore not a supported integration route. Use a trusted backend or the supported desktop/Node runtime; never solve CORS by shipping an lmp_ key to a browser.
The documentation's Sandbox console is same-origin and keeps credentials in transient memory. It does not add permissive CORS, proxy credentials or make the API a browser-safe management service.
Retry and idempotency boundary
Create requires an organization-scoped idempotency key retained24hours. Identical decoded-body hashes replay the stored response; changed content or member order can cause IDEMPOTENCY_CONFLICT409. Use a stable business-event identity and preserve the same request representation.
Current creation idempotency has recorded expired-row/concurrency defects. An expired key before cleanup or simultaneous creates can produce duplicate committed work and a500. Do not describe it as an exactly-once fulfillment guarantee. Reconcile uncertain creation results with your durable fulfillment records and support rather than blindly issuing a new key.
Other operations ignore Idempotency-Key. Activation rotates a token, renewal extends time again, deactivation repeats401 and repeated status/reset operations can emit audit/events again. A timeout after a mutation is an uncertain result, not proof nothing happened.
Rate-limit errors use429. Licensing errors currently omit Retry-After and success limiter headers; do not assume headers from the billing UI apply here. Successful limited calls expose X-RateLimit-Remaining and X-RateLimit-Reset. Use bounded backoff with jitter and operation-specific reconciliation, not immediate loops.
Expected result
Requests use the correct origin/version, credential and fields; failure handling preserves the actual HTTP/error/request-ID information without exposing secrets.
Common mistakes
- Using an internal product ID in X-App-ID, or a public App ID where create expects an internal ID/slug.
- Appending /api/v1 to an SDK origin twice.
- Assuming every mutation has safe replay or every wrong method returns405.
- Expecting unsupported list/query/CORS behavior.
Security notes
Send management credentials only from your backend. Do not authorize a license from a webhook or checkout-success URL. Preserve signed bytes and fail closed on verification problems. The console and examples do not change the actual server's authentication, plan gates or known defects.
Troubleshooting
| Symptom | Cause | Action |
|---|---|---|
| NOT_FOUND404 | Wrong path, method or version | Match the exact documented operation |
| INVALID_REQUEST422 | Missing/invalid consumed fields or creation policy rejection | Correct the request; do not retry unchanged input blindly |
| Browser preflight fails | No cross-origin CORS contract | Move the operation to your backend or supported client runtime |
| Replay body and header IDs differ | Stored response versus current request | Retain both IDs for correlation |
Next steps
Review signed responses and the working quickstart before sending real credentials.