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 the public key

Retrieve the current public verification key.

On this page
Updated 2026-10-17 UTCDownload Markdown

Retrieve the current public verification key.

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/public-key

When to use it

Fetch the current Ed25519 verification key during a trusted setup or controlled key refresh. This endpoint is global, not specific to an organization or environment.

Authentication and environment

No credential or environment header is required.

Request headers

HeaderRequiredDescriptionExample
AcceptrecommendedJSON responsesapplication/json

Path and query parameters

No path parameters are accepted.

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,
  "key": {
    "algorithm": "Ed25519",
    "key_id": "ed25519_2b9bc7e214b1209f",
    "public_key": "3VNX3t2NIvzg37o1w-bjxJdJVFtgbfpR-_Q80u4U5tQ",
    "encoding": "base64url"
  },
  "request_id": "req_d9e278f6722a692a5dc3bbe8"
}

Response fields

FieldTypeRequiredConstraintsDescriptionExample
successbooleanyesconst=trueThe operation succeeded.true
keyobjectyesSee descriptionCurrent global verification key, not a historical key ring.Nested object/array shown in full JSON
key.algorithmstringyesconst="Ed25519"Fixed verification algorithm."Ed25519"
key.key_idstringyespattern="^ed25519_[a-f0-9]{16}$"ed25519_ plus first16 hex SHA256 characters of raw public key."ed25519_2b9bc7e214b1209f"
key.public_keystringyespattern="^[A-Za-z0-9_-]{43}$"Unpadded base64url of32 raw Ed25519 public-key bytes."3VNX3t2NIvzg37o1w-bjxJdJVFtgbfpR-_Q80u4U5tQ"
key.encodingstringyesconst="base64url"Raw-key encoding."base64url"
request_idstringyespattern="^req_[a-f0-9]{24}$"Request correlation ID. Replay bodies can retain an earlier ID."req_d9e278f6722a692a5dc3bbe8"

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?
RATE_LIMITED429Bucket exhausted; wait for window reset; no licensing Retry-After headerAfter window; apply jitterAfter the 60-second window, with jitter
INTERNAL_ERROR500Uncaught exception at global boundarySafe retry only for idempotent operation; preserve request_idOnly safe reads after backoff; reconcile mutations

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

No license, device, token, customer, audit event, webhook or email is created. The public-key IP limiter is consumed. The global API metadata log has no organization attribution.

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. Cache the successful key for the advertised3600 seconds, subject to your pinned-key refresh policy. A timeout never justifies disabling verification. The endpoint returns only the current key, not a historical key ring.

Rate limits

60 requests per60 seconds, keyed by the caller IP.

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.

C

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 ./request-c public-key. 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.cc
/* C11 entry point. Build with client_http.c include, yyjson0.13.0 and libcurl7.76+.
 * Run request public-key; expected JSON, exit0 success/1 rejection/2 unavailable. */
#include "client_http.c"
int main(int argc,char **argv){return kdpika_example_main(argc,argv);}
client_http.cc
/* Purpose: shared native libcurl transport for C11 and C++17 Sandbox examples.
 * Requires libcurl7.76+, yyjson0.13.0. Build commands are in the sample README.
 * Run request public-key. JSON stdout; exit0 success/1 rejection/2 setup/transport.
 * Credentials/body: KDP_* environment. Private terminal only; output may contain secrets.
 * Transport inspection only: verify signed state and policy before authorizing access.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <curl/curl.h>
#include "yyjson.h"
static const char *kdp_contract="{\"public-key\":{\"method\":\"GET\",\"path\":\"/public-key\",\"auth\":\"public\"},\"activate\":{\"method\":\"POST\",\"path\":\"/licenses/activate\",\"auth\":\"publicApp\"},\"validate\":{\"method\":\"POST\",\"path\":\"/licenses/validate\",\"auth\":\"activation\"},\"deactivate\":{\"method\":\"POST\",\"path\":\"/licenses/deactivate\",\"auth\":\"activation\"}}";
typedef struct {char *data;size_t size;} kdp_buffer;
static size_t kdp_receive(char *data,size_t size,size_t count,void *context){
    kdp_buffer *b=(kdp_buffer *)context;
    if(size && count>SIZE_MAX/size)return 0;
    size_t n=size*count;if(n>1048576-b->size)return 0;
    char *p=(char *)realloc(b->data,b->size+n+1);if(!p)return 0;
    b->data=p;memcpy(p+b->size,data,n);b->size+=n;p[b->size]=0;return n;
}
static const char *kdp_env(const char *name){const char *v=getenv(name);return v?v:"";}
static const char *kdp_required(const char *name){const char *v=kdp_env(name);return *v&&!strchr(v,'\r')&&!strchr(v,'\n')?v:NULL;}
static int kdp_header(struct curl_slist **headers,const char *name,const char *value){
    if(!value)return 0;size_t n=strlen(name)+strlen(value)+3;char *line=(char *)malloc(n);if(!line)return 0;
    snprintf(line,n,"%s: %s",name,value);struct curl_slist *next=curl_slist_append(*headers,line);free(line);
    if(!next)return 0;*headers=next;return 1;
}
static int kdpika_example_main(int argc,char **argv){
    int code=2;CURL *curl=NULL;CURLU *url=NULL;struct curl_slist *headers=NULL;
    yyjson_doc *ops=NULL,*bodydoc=NULL,*result=NULL;char *scheme=NULL,*host=NULL,*path=NULL,*part=NULL,*target=NULL;
    kdp_buffer buffer={NULL,0};const char *base=kdp_env("KDP_BASE_URL"),*body=kdp_env("KDP_BODY_JSON");
    const char *method=NULL,*route=NULL,*auth=NULL;yyjson_val *op=NULL;long status=0;
    if(argc!=2)goto cleanup;
    if(!*base)base="https://licensing.kdpika.com/api/v1";
    if(!*body)body="{}";
    ops=yyjson_read(kdp_contract,strlen(kdp_contract),0);if(!ops)goto cleanup;
    op=yyjson_obj_get(yyjson_doc_get_root(ops),argv[1]);if(!op)goto cleanup;
    method=yyjson_get_str(yyjson_obj_get(op,"method"));route=yyjson_get_str(yyjson_obj_get(op,"path"));auth=yyjson_get_str(yyjson_obj_get(op,"auth"));
    if(!method||!route||!auth||curl_global_init(CURL_GLOBAL_DEFAULT)!=0)goto cleanup;
    url=curl_url();if(!url||curl_url_set(url,CURLUPART_URL,base,0)!=CURLUE_OK)goto cleanup;
    if(curl_url_get(url,CURLUPART_SCHEME,&scheme,0)||curl_url_get(url,CURLUPART_HOST,&host,0)||curl_url_get(url,CURLUPART_PATH,&path,0))goto cleanup;
    if(strcmp(path,"/api/v1")!=0)goto cleanup;
    if(strcmp(scheme,"https")!=0 && !(strcmp(scheme,"http")==0 && (!strcmp(host,"localhost")||!strcmp(host,"127.0.0.1")) && !strcmp(kdp_env("KDP_ALLOW_LOOPBACK"),"1")))goto cleanup;
    {CURLUPart forbidden[]={CURLUPART_USER,CURLUPART_PASSWORD,CURLUPART_QUERY,CURLUPART_FRAGMENT};
    for(size_t i=0;i<sizeof(forbidden)/sizeof(forbidden[0]);i++){if(curl_url_get(url,forbidden[i],&part,0)==CURLUE_OK)goto cleanup;}}
    target=(char *)malloc(strlen(base)+strlen(route)+1);if(!target)goto cleanup;strcpy(target,base);strcat(target,route);
    if(!kdp_header(&headers,"Accept","application/json")||!kdp_header(&headers,"User-Agent","KDPikaDocsExample/1.0 Native"))goto cleanup;
    if(!strcmp(auth,"publicApp")&&(!kdp_header(&headers,"X-App-ID",kdp_required("KDP_APP_ID"))||!kdp_header(&headers,"X-Environment","SANDBOX")))goto cleanup;
    if(!strcmp(auth,"activation")){
        const char *token=kdp_required("KDP_ACTIVATION_TOKEN");if(!token)goto cleanup;
        char *bearer=(char *)malloc(strlen(token)+8);if(!bearer)goto cleanup;strcpy(bearer,"Bearer ");strcat(bearer,token);
        int ok=kdp_header(&headers,"Authorization",bearer);free(bearer);if(!ok)goto cleanup;
    }
    if(!strcmp(argv[1],"validate")&&!kdp_header(&headers,"X-Installation-ID",kdp_required("KDP_INSTALLATION_ID")))goto cleanup;
    curl=curl_easy_init();if(!curl)goto cleanup;
    curl_easy_setopt(curl,CURLOPT_URL,target);curl_easy_setopt(curl,CURLOPT_CUSTOMREQUEST,method);
    curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,0L);curl_easy_setopt(curl,CURLOPT_CONNECTTIMEOUT,5L);curl_easy_setopt(curl,CURLOPT_TIMEOUT,15L);
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1L);curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,2L);
    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,kdp_receive);curl_easy_setopt(curl,CURLOPT_WRITEDATA,&buffer);
    if(!strcmp(method,"POST")){
        bodydoc=yyjson_read(body,strlen(body),0);if(!bodydoc||!yyjson_is_obj(yyjson_doc_get_root(bodydoc)))goto cleanup;
        if(!kdp_header(&headers,"Content-Type","application/json"))goto cleanup;
        curl_easy_setopt(curl,CURLOPT_POSTFIELDS,body);
    }
    curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);if(curl_easy_perform(curl)!=CURLE_OK)goto cleanup;
    curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&status);
    result=yyjson_read(buffer.data,buffer.size,0);if(!result||!yyjson_is_obj(yyjson_doc_get_root(result)))goto cleanup;
    printf("%s\n",buffer.data);
    code=status>=200&&status<300&&yyjson_is_true(yyjson_obj_get(yyjson_doc_get_root(result),"success"))?0:1;
cleanup:
    if(code)fprintf(stderr,"Request rejected, unavailable or configuration invalid. Inspect JSON error.code/request_id when returned; do not blindly retry mutations.\n");
    if(curl)curl_easy_cleanup(curl);if(url)curl_url_cleanup(url);curl_slist_free_all(headers);
    curl_free(scheme);curl_free(host);curl_free(path);curl_free(part);free(target);free(buffer.data);
    yyjson_doc_free(ops);yyjson_doc_free(bodydoc);yyjson_doc_free(result);return code;
}

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
RATE_LIMITED (HTTP 429)Bucket exhausted; wait for window reset; no licensing Retry-After headerAfter window; apply jitter
INTERNAL_ERROR (HTTP 500)Uncaught exception at global boundarySafe retry only for idempotent operation; preserve request_id

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