---
slug: "/docs/v1/start/quickstart"
title: "Quickstart: your first Sandbox license"
description: "Create a verified workspace, issue a test license and run one complete signed activation example on a single page."
section: "Start here"
order: 4
audience: "all"
level: "beginner"
last_updated: "2026-10-21"
version: "v1"
related: []
draft: false
layout: "quickstart"
anchor_aliases: ["csharp-setup", "curl-setup", "what-you-will-learn","prerequisites","create-the-sandbox-records","verify-your-account","create-your-product","create-your-plan-and-feature","issue-a-test-license","download-your-sdk","configure-your-environment","choose-a-runtime","common-mistakes","security-notes","troubleshooting","related"]
---
## What you will build
A small [Sandbox](/docs/v1/start/glossary#sandbox) program that activates a key and checks the server's signed decision before reporting access. Plan for about ten minutes after email and runtime setup; installation and email [delivery](/docs/v1/start/glossary#delivery) can take longer.

## What you need
An inbox you control, an internet connection and one of the runtimes listed below. No account or licensing knowledge is assumed. Use a user-owned example folder and private state directory.

## 1. Create your Sandbox workspace
1. Open [registration](/register) and select Free for Sandbox evaluation.
2. Enter an email you control and verify the code or single-use link from the message.
3. Complete your name, workspace name, password and terms acknowledgement.
4. In the workspace header, select **Sandbox**.
A workspace is the private area that contains your [products](/docs/v1/start/glossary#product) and [licenses](/docs/v1/start/glossary#license). Sandbox means test records, separate from [Live](/docs/v1/start/glossary#live). A paid platform selection can require checkout; it is not a [customer](/docs/v1/start/glossary#customer) license purchase.

## 2. Create your product and plan
1. Open **Products → New product** (or **Create product** on an empty page).
2. Enter **Product name** Quickstart App, **Slug** quickstart-app, **Platform** Desktop, and **License prefix** QUICK. Choose **Create product**.
3. Saving opens the first-plan form in **Plans & [features](/docs/v1/start/glossary#feature)**. If it is closed, choose **New plan**.
4. Select Quickstart App. Enter **Plan name** Quickstart plan and **Slug** quickstart-plan.
5. Choose **Currency** EUR, **Price** 19.50, **Billing type** Monthly, **Duration days** 30, **Max [devices](/docs/v1/start/glossary#device)** 2 and **Feature keys** pdf_export.
6. Choose **Create plan**. Return to Products and copy the product's public **App ID**.
A product represents your software. Its App ID is the application identifier; there is no separate Application record to create. A plan describes access for that product. These plan prices are reference metadata, not a payment or recurring charge.
:::warning Use the demonstrated test values
The sample price and name are test data. Python SDK 1.1.5 fixes the earlier Python canonicalization defects; legacy JavaScript/C# limitations remain documented. Test your real payloads and never weaken verification. A production assessment is required at Stage 14.
:::

## 3. Issue your test key
1. Open **Licenses** while the header still says **Sandbox**.
2. Choose **New license** or **Create first license**.
3. Select Quickstart App / Quickstart plan in **Product and plan**.
4. Enter your test **Customer email** and a simple **Customer name**, such as Quickstart Tester. Company and External customer ID are optional.
5. Choose **Create license**. Copy **Your new [license key](/docs/v1/start/glossary#license-key)**, shown once, into your private test configuration.
The dashboard creates or reuses the customer for this email. A new license has no activated devices yet.
:::security Credential basics
The [public App ID](/docs/v1/start/glossary#public-app-id) identifies the software. A customer license key activates one purchase. The SDK receives a private [activation token](/docs/v1/start/glossary#activation-token) and stores an installation identity. A [developer API key](/docs/v1/start/glossary#developer-api-key) can manage licenses: create one under **API keys** only for a trusted sales backend, never for this client. If you supply an email during [activation](/docs/v1/start/glossary#activation), it must match the license.
:::

## 4. Install your chosen SDK
:::tabs
:::tab python Python
### Python setup
Use Python 3.11–3.14 and a working HTTPS certificate store. The official package is published on [PyPI](https://pypi.org/project/kdpika-licensing/) with [GitHub source](https://github.com/kdpika/kdpika-licensing). Create a virtual environment, then install:
```bash
python3 -m venv .venv
.venv/bin/python -m pip install "kdpika-licensing==1.1.5"
.venv/bin/python -m pip check
```
On native Windows use py -m venv .venv, then .venv\\Scripts\\python.exe for both pip commands and the example. Import kdpika_licensing. Use the same virtualenv interpreter for pip and your example.
:::
:::tab javascript JavaScript / Node.js
### JavaScript setup
Requires Node.js 18+ source compatibility, npm and an authenticated backend, CLI or Electron main process. Do not run this SDK in a web page. Download [JavaScript SDK 1.0.0](/docs/downloads/kdpika-javascript-sdk-1.0.0.zip) and extract into kdpika-javascript-sdk, then run:
```bash
npm install --ignore-scripts --save-exact ./kdpika-javascript-sdk
```
Save your example with the .mjs extension. The installed module is @kdpika/license-sdk. Choose a maintained Node runtime for production and run the compatibility tests there.
:::
:::tab php PHP
### PHP setup
Requires PHP 8.2+, cURL, Sodium and Composer. The package's older ^8.1 declaration is not its actual minimum. Download [PHP SDK 1.0.0](/docs/downloads/kdpika-php-sdk-1.0.0.zip) and extract into kdpika-php-sdk:
```bash
composer install --working-dir ./kdpika-php-sdk --no-interaction --no-plugins --no-scripts --prefer-dist
```
The example loads that folder's vendor/autoload.php. It runs on your trusted backend or CLI, not in browser JavaScript. KDP_SDK_DIR can point to a different extracted package folder.
:::
:::tab csharp C# / .NET
### C# setup
Requires the .NET SDK and the unchanged SDK's net7.0 source target. Download [C# SDK 1.0.0](/docs/downloads/kdpika-csharp-sdk-1.0.0.zip) and extract into kdpika-csharp-sdk. Save the project below beside that folder:
```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable>
    <EnableDefaultCompileItems>false</EnableDefaultCompileItems></PropertyGroup>
  <ItemGroup><Compile Include="Program.cs"/>
    <ProjectReference Include="kdpika-csharp-sdk/KDPika.LicenseSdk/KDPika.LicenseSdk.csproj"/>
  </ItemGroup>
</Project>
```
```bash
dotnet restore Guide.csproj
```
The demonstrated net7 target is an existing compatibility constraint, not a recommendation to deploy an end-of-support runtime. Production needs a maintained-runtime compatibility assessment; see the disclosed SDK limitations.
:::
:::tab curl cURL / other language
### cURL setup
This is a transport exercise, not an SDK or signature verifier. Use this route for a REST-only language, then follow its real client and verification project before granting access.
```bash
umask 077
curl --fail --silent --show-error -o response-private.json \
  -H "X-App-ID: $KDP_APP_ID" -H "X-Environment: SANDBOX" \
  -H "Content-Type: application/json" \
  --data '{"license_key":"YOUR_SANDBOX_LICENSE_KEY","device_id":"guide-install-CHANGE-ONCE"}' \
  https://licensing.kdpika.com/api/v1/licenses/activate
```
Replace the key privately. Keep the device_id stable for this installation. The file contains a private activation token: do not publish or log it. HTTP success alone does not authorize your app. Read [activation](/docs/v1/api/activate), then choose your [REST client](/docs/v1/sdks/overview) and [signature verification](/docs/v1/security/verify-ed25519).
:::
:::


## 5. Configure your terminal
Set these variables in your terminal. App ID is public; your customer's key and state directory are private. Do not use a developer API key in this client.
:::tabs
:::tab bash macOS / Linux · Bash
```bash
export KDP_APP_ID="YOUR_PUBLIC_APP_ID"
export KDP_CUSTOMER_EMAIL="YOUR_TEST_CUSTOMER_EMAIL"
export KDP_STATE_DIR="$HOME/.kdpika-guide-sandbox"
read -r -s -p "Sandbox license key: " KDP_LICENSE_KEY
printf '\n'
export KDP_LICENSE_KEY
```
For a different device test, use a new private state directory, not a different email.
:::
:::tab powershell Windows · PowerShell
```powershell
$env:KDP_APP_ID = "YOUR_PUBLIC_APP_ID"
$env:KDP_CUSTOMER_EMAIL = "YOUR_TEST_CUSTOMER_EMAIL"
$env:KDP_STATE_DIR = Join-Path $env:LOCALAPPDATA "KDPikaGuideSandbox"
$secureKey = Read-Host "Sandbox license key" -AsSecureString
$env:KDP_LICENSE_KEY = [System.Net.NetworkCredential]::new("", $secureKey).Password
```
The variable is plaintext in this process; do not print it or leave it in a shared terminal. Close the terminal after testing. Windows Python uses .venv\\Scripts\\python.exe; Node/PHP/.NET commands are the same if their runtimes are installed.
:::
:::
Expected result: the variables are available only to this terminal and its child processes. Use a user-owned directory, outside a web root or shared repository. The sample uses Sandbox explicitly.

## 6. Save and run one complete file
:::tabs
:::tab python Python
Save **license-check.py** in your example folder.
```python
"""Sandbox learning example; use private state, never a developer API key."""
import os
import sys
from pathlib import Path
from kdpika_licensing import LicenseClient

try:
    action = sys.argv[1] if len(sys.argv) == 2 else "activate"
    if action not in ("activate", "validate", "check", "deactivate"):
        raise ValueError("Choose an action")
    client = LicenseClient(
        product=os.environ["KDP_APP_ID"], environment="SANDBOX",
        base_url=os.getenv("KDP_BASE_URL", "https://licensing.kdpika.com"),
        storage_dir=Path(os.environ["KDP_STATE_DIR"]), prefer_keyring=False)
    result = client.activate(os.environ["KDP_LICENSE_KEY"],
        os.getenv("KDP_CUSTOMER_EMAIL") or None) if action == "activate" else getattr(client, action)()
    released = action == "deactivate" and result.valid
    allowed = action != "deactivate" and result.valid and result.signature_valid
    print("Device released." if released else "Sandbox license verified." if allowed
          else "License could not be confirmed. Check your key or contact support.")
    sys.exit(0 if released or allowed else 1)
except Exception:
    print("License check unavailable. Check configuration and connection.", file=sys.stderr)
    sys.exit(2)
```
Run:
```bash
.venv/bin/python license-check.py activate
```
:::
:::tab javascript JavaScript / Node.js
Save **license-check.mjs** in your example folder.
```javascript
// Node.js / Electron main process, not browser JavaScript.
import { LicenseClient } from '@kdpika/license-sdk';
try {
  const action = process.argv[2] || 'activate';
  if (!['activate', 'validate', 'check', 'deactivate'].includes(action)) throw Error();
  const client = new LicenseClient({
    product: process.env.KDP_APP_ID, environment: 'SANDBOX',
    baseUrl: process.env.KDP_BASE_URL || 'https://licensing.kdpika.com',
    storageDir: process.env.KDP_STATE_DIR
  });
  if (!process.env.KDP_STATE_DIR) throw Error();
  const result = action === 'activate'
    ? await client.activate(process.env.KDP_LICENSE_KEY, process.env.KDP_CUSTOMER_EMAIL || null)
    : await client[action]();
  const released = action === 'deactivate' && result.valid;
  const allowed = action !== 'deactivate' && result.valid && result.signatureValid;
  console.log(released ? 'Device released.' : allowed ? 'Sandbox license verified.'
    : 'License could not be confirmed. Check your key or contact support.');
  process.exitCode = released || allowed ? 0 : 1;
} catch {
  console.error('License check unavailable. Check configuration and connection.');
  process.exitCode = 2;
}
```
Run:
```bash
node license-check.mjs activate
```
:::
:::tab php PHP
Save **license-check.php** in your example folder.
```php
<?php
declare(strict_types=1);
use KDPika\LicenseSdk\LicenseClient;
try {
    require (getenv('KDP_SDK_DIR') ?: './kdpika-php-sdk').'/vendor/autoload.php';
    $action = $argv[1] ?? 'activate';
    if (!in_array($action, ['activate','validate','check','deactivate'], true)
        || !getenv('KDP_APP_ID') || !getenv('KDP_STATE_DIR')) throw new RuntimeException();
    $client = new LicenseClient(product:getenv('KDP_APP_ID'), environment:'SANDBOX',
        baseUrl:getenv('KDP_BASE_URL') ?: 'https://licensing.kdpika.com',
        storageDir:getenv('KDP_STATE_DIR'));
    $result = $action === 'activate'
        ? $client->activate(getenv('KDP_LICENSE_KEY') ?: '', getenv('KDP_CUSTOMER_EMAIL') ?: null)
        : $client->$action();
    $released = $action === 'deactivate' && $result->valid;
    $allowed = $action !== 'deactivate' && $result->valid && $result->signatureValid;
    echo $released ? 'Device released.' : ($allowed ? 'Sandbox license verified.'
        : 'License could not be confirmed. Check your key or contact support.'), PHP_EOL;
    exit($released || $allowed ? 0 : 1);
} catch (Throwable) {
    fwrite(STDERR, 'License check unavailable. Check configuration and connection.'.PHP_EOL);
    exit(2);
}
```
Run:
```bash
php license-check.php activate
```
:::
:::tab csharp C# / .NET
Save **Program.cs** in your example folder.
```csharp
using KDPika.LicenseSdk;
static string Required(string name) => Environment.GetEnvironmentVariable(name)
    is string value && value.Length > 0 ? value : throw new ArgumentException();
try {
    var action = args.Length == 0 ? "activate" : args[0];
    if (!new[]{"activate","validate","check","deactivate"}.Contains(action)) throw new ArgumentException();
    var client = new LicenseClient(Required("KDP_APP_ID"), "SANDBOX",
        Environment.GetEnvironmentVariable("KDP_BASE_URL") ?? "https://licensing.kdpika.com",
        storageDir:Required("KDP_STATE_DIR"));
    var result = action switch {
        "activate" => await client.ActivateAsync(Required("KDP_LICENSE_KEY"),
            Environment.GetEnvironmentVariable("KDP_CUSTOMER_EMAIL")),
        "validate" => await client.ValidateAsync(), "check" => await client.CheckAsync(),
        _ => await client.DeactivateAsync()
    };
    var released = action == "deactivate" && result.Valid;
    var allowed = action != "deactivate" && result.Valid && result.SignatureValid;
    Console.WriteLine(released ? "Device released." : allowed ? "Sandbox license verified."
        : "License could not be confirmed. Check your key or contact support.");
    Environment.ExitCode = released || allowed ? 0 : 1;
} catch {
    Console.Error.WriteLine("License check unavailable. Check configuration and connection.");
    Environment.ExitCode = 2;
}
```
Run:
```bash
dotnet run --project Guide.csproj -- activate
```
:::
:::


## Expected result
The chosen [official SDK](/docs/v1/start/glossary#official-sdk) sample prints exactly:
```text
Sandbox license verified.
```
It exits with code 0 and the dashboard's Devices count shows one active installation. No real charge is made. A cURL-only success is a transport result, not a verified access grant.

## If it does not work
| What you see | What to do |
|---|---|
| INVALID_LICENSE | Check the full key, public App ID, Sandbox selection and matching customer email. |
| MAX_DEVICES_REACHED | Deactivate an old installation or inspect Devices. Do not make random identities. |
| SIGNATURE_INVALID / cached signature error | Keep access locked. Check the runtime, clock and SDK compatibility notes; do not disable verification. |
| NOT_ACTIVATED | Run activate in the same private state directory first. |
| NETWORK_ERROR / no connection | Reconnect. A first activation needs internet; no cached response means no offline grant. |
| LICENSE_EXPIRED / LICENSE_REVOKED / LICENSE_SUSPENDED | Explain the state to the customer and provide support; never silently convert it to valid. |

## Next steps
- [Continue with the Complete Integration Guide](/docs/v1/integration-guide).
- [Validate on every launch](/docs/v1/integration-guide/validate).
- [Review the launch gate](/docs/v1/integration-guide/go-live).

## Previous detailed reference
The [earlier quickstart reference](/docs/v1/help/previous-quickstart) retains all earlier explanations and example files. It is supplementary, not required for this page.
