---
title: "Python desktop: your first licensed app"
description: "Run a real Python window, activate a test license and protect CSV export before adding advanced rules."
slug: "/docs/v1/tutorials/python/desktop"
section: "Practical tutorials"
order: 1002
audience: "all"
level: "beginner"
last_updated: "2026-10-26"
version: "v1"
related: ["/docs/v1/tutorials/python", "/docs/v1/sdks/python", "/docs/v1/sdks/capabilities"]
draft: false
layout: "guide"
---

## What you will make

A small window with a license key, customer email, Check now, Export CSV and Release this machine. The app saves a real CSV only after a verified license includes `csv_export`. No developer management key is embedded.

[Download the project](/docs/downloads/kdpika-python-projects-1.0.0.zip) and extract it to a normal private folder. Keep its files together. Follow [the shared setup](/docs/v1/tutorials/python#make-your-first-test-license) first: a tutorial product, a normal plan with `csv_export`, a customer and a Sandbox license.

## 1. Install Python and the SDK

Use Python 3.11–3.14. In the extracted project folder:

```bash
python -m venv .venv
```

Activate the environment with the command for your terminal:

| Terminal | Command |
| --- | --- |
| Windows PowerShell | `.venv\Scripts\Activate.ps1` |
| macOS / Linux shell | `source .venv/bin/activate` |

Then install the SDK. Flask is not needed for this desktop app.

```bash
python -m pip install "kdpika-licensing==2.0.1"
python -m tkinter
```

The second command should open a small Tk test window; close it. If Tkinter is missing, use a Python installation with Tk support. Debian/Ubuntu distributions commonly provide `python3-tk`; package names vary on other Linux distributions. This is a local app prerequisite, not a server/hosting requirement.

## 2. Set your public App ID

Replace `app_YOUR_PUBLIC_ID` with the real ID copied from Products. It is public configuration, not a management secret.

macOS/Linux:

```bash
export KDP_APP_ID="app_YOUR_PUBLIC_ID"
export KDP_ENVIRONMENT="SANDBOX"
python desktop.py
```

Windows PowerShell:

```powershell
$env:KDP_APP_ID="app_YOUR_PUBLIC_ID"
$env:KDP_ENVIRONMENT="SANDBOX"
python desktop.py
```

Dashboard example: Grant product access

Open: Licenses → Create license

Example data only; use your own real dashboard.

Product and plan: Orbit Notes / Personal test

Customer name: Jane Example

Customer email: jane@example.com — Use an email you control in your actual test.

Company: Leave blank (optional)

External customer ID: Leave blank (optional)

Buttons: Create license

The dashboard creates or reuses the customer for the entered email. You do not need a separate Add customer step.

## 3. Activate and export

1. Paste the Sandbox license key into the window and enter the email attached to it.
2. Choose **Activate**. The SDK collects the installation/device information, calls KDPika and verifies its signed answer.
3. Type a message and choose **Export CSV**. Open the path shown by the app. The CSV contains your message.
4. Close and reopen the app. Its private saved state remembers this installation; you do not need to enter the key on every start.

The files live under your user profile in `.kdpika-python-tutorial`, separated by product and environment. Do not place this folder in the application installer, a shared network folder or a public web directory. On Windows configure private user ACLs; POSIX mode bits alone do not set them. Optional OS keyring support is described in [Python storage guidance](/docs/v1/sdks/python).

## 4. Understand the important code

`common.py` is the small shared part used by both starter apps. The important order is: obtain a result, require valid + verified access, require the feature, then do the work.

{{sample:python-projects/common.py}}

The example intentionally refuses metered exports. A feature check does not spend a credit; accepting it alone would accidentally provide unpaid work. After understanding this first app, run [the credit-based export](/docs/v1/tutorials/python/credits).

## 5. Add device rules and offline access

Set the device allowance in the software plan or one license's **Limits & overrides**. It is enforced by the licensing server; there is no `max_devices=...` constant to trust inside the client.

Set normal validation/offline windows in **Products → Validation & offline policy**. `check()` uses a valid saved result when permitted; `validate()` explicitly checks online. An expired license cannot be made usable by increasing the offline window.

If your KDPika subscription includes advanced matching, choose strict/flexible matching in that product policy. The SDK handles the fingerprint; never use a sample's made-up component hash or a random ID as a hardware fallback. Hardware matching is software-based, not hardware attestation.

## 6. Try shared seats

1. Create a separate floating software plan. Set a positive shared-seat allowance and timing values; keep the device allowance large enough for your intended installations. Your KDPika plan must include floating licensing.
2. Issue a Sandbox license and activate it in this app. The SDK automatically checks out a seat and starts background heartbeats after successful activation/check.
3. Keep every protected operation behind a fresh `client.check()` / feature guard, as the CSV action already does. A background worker alone is not permission to continue forever.
4. Closing this window calls `client.close()`, releasing a shared seat. It does **not** deactivate the installed machine. **Release this machine** uses `deactivate()` and requires activation again.

When using explicit SDK seat operations in your own UI:

```python
from kdpika_licensing import AllSeatsInUseError

try:
    result = client.checkout()
    if not result.valid or not result.signature_valid:
        show_access_unavailable()
    elif result.feature("csv_export"):
        enable_export_button()
except AllSeatsInUseError:
    show_message("All seats are in use. Close the app on another machine and try again.")
# These UI callback names belong to your own application.
# Still check access again immediately before protected work.
```

After a crash there may be no checkin; the signed lease times out. Offline access stops at the shared-seat deadline even if normal offline grace is longer. Long work should check its authorization at sensible boundaries, not only before the entire job starts.

## 7. Give a customer extra access

Open their license → **Limits & overrides**. For example, grant 3 devices while its plan normally allows 2. The customer's next eligible check uses the signed effective settings. **Reset to plan default** removes the exception.

To change everyone, edit the plan and choose the appropriate new-only or new-and-existing scope. The preview shows the affected licenses; individual overrides are preserved. Lowering a limit does not silently kick out already-active devices/seats: it blocks additional ones until usage is below the limit.

## 8. Add customer self-service

Open [Customer portal](/portal-settings), configure your name/logo/support contact and copy its sign-in link. When testing, use its Sandbox link. You can put that link in your app's Help menu using Python's `webbrowser.open(portal_url)`. Copy the real sign-in URL from the dashboard; do not construct a token URL or put a license key into a query string.

The hosted portal handles verified email links, license-key sign-in, allowed device/session removal, balances and history. Hide KDPika branding only if your platform plan includes it. No custom-domain host or payment flow is created by this example.

## Full window code

SDK calls run in one worker so a slow network does not freeze the window. Tk widgets are updated on the main thread. No GUI screenshots/target-OS packaging certification are claimed by the downloadable example.

{{sample:python-projects/desktop.py}}

## If something does not work

| What happens | What to check |
| --- | --- |
| Key is refused | Same product, Sandbox environment and matching customer email |
| CSV export is unavailable | A verified current license includes `csv_export`; use existing-license scope if you changed a plan |
| Device cannot match | Check the real machine and publisher policy; do not fabricate a fingerprint |
| No shared seat | Close another running app or end a genuinely stuck session in the portal/dashboard |
| Access stops offline | The signed offline/lease deadline may have passed; reconnect |
| This license uses credits | Use [the credit example](/docs/v1/tutorials/python/credits) instead of a free feature-only export |

[Python feature map](/docs/v1/tutorials/python) · [Web app](/docs/v1/tutorials/python/web) · [SDK reference](/docs/v1/sdks/python).
