---
title: "Build licensed apps with Python"
description: "Start with a desktop or web app, then add device rules, shared seats, credits and customer self-service."
slug: "/docs/v1/tutorials/python"
section: "Practical tutorials"
order: 1001
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"
anchor_aliases: ["choose-your-first-project", "first-understand-these-three-things", "make-your-first-test-license", "every-feature-in-plain-words", "what-the-numbers-on-a-pricing-card-mean", "a-simple-test-for-each-feature", "what-you-should-not-have-to-write"]
---

You will build **Orbit Notes**, a small app with a CSV export button. First give one test customer access. Then choose only the extra rules your app needs.

**Read → copy the example choices into your real dashboard → save → try the app.** The dashboard fragments below are HTML/CSS illustrations with example data. They do not change your account. Open the real page with the link in each step.

## Before you start: three names, one simple difference

- **Product:** your app. Orbit Notes has one public App ID.
- **Software plan:** what one customer gets. Our first plan allows 2 computers and CSV export.
- **Your KDPika plan:** what your developer workspace may use. Advanced features depend on the capabilities enabled for it.

Do not enable every advanced option at once. Finish steps 1–4 first. You need Python 3.11–3.14, a KDPika account and an email you control. No card details or developer management key go into the example app.

## 1. Switch to Sandbox

**Why:** you can try licenses without mixing them with real sales.

1. Open [your workspace](/dashboard).
2. At the top, open **Environment** and select **Sandbox**.
3. Keep this choice while issuing all tutorial licenses.

Dashboard example: Work in test mode

Open: Dashboard header → Environment

Example data only; use your own real dashboard.

Environment: Sandbox

This changes the dashboard view. The application example also explicitly uses SANDBOX; it does not read your browser choice.

**Done when:** the real header says Sandbox. Your Python example must also use `KDP_ENVIRONMENT=SANDBOX`; the dashboard cannot change a running app's environment for you.

## 2. Create the app you will license

**Why:** the App ID tells the SDK which software it belongs to.

1. Open [Products](/products) and choose **New product**. On an empty page, use **Create first product**.
2. Type **Orbit Notes**. Use the example slug and prefix below, and choose **Desktop**. For the separate [web example](/docs/v1/tutorials/python/web), choose **Web** instead.
3. Choose **Create product**. Copy its actual **App ID** from the resulting product card.

Dashboard example: What are you licensing?

Open: Products → New product

Example data only; use your own real dashboard.

Product name: Orbit Notes

Slug: orbit-notes

Description: A small app that exports a CSV file.

Platform: Desktop

License prefix: ORBT — 2–8 letters or numbers.

Buttons: Create product

Use a separate test product. The name and App ID shown here are examples, not real account data.

Dashboard example: Orbit Notes

Open: Products → Your product

Example data only; use your own real dashboard.

App ID: app_YOUR_PUBLIC_ID

App ID is public. Never paste a management API key into a desktop app or browser.

**Done when:** you have a real `app_...` ID copied from your account. The placeholder shown here is not a usable App ID. An App ID is public; a management API key is private and is not needed for this project.

## 3. Make a simple customer plan

**Why:** this tells KDPika what your customer is allowed to use.

1. Open [Plans & features](/plans) → **New plan**. Saving the new product may already open this form.
2. Choose Orbit Notes, name the plan **Personal test**, and enter its slug.
3. Use **Max devices = 2**, **Duration days = 30**, and **Feature keys = csv_export**.
4. Leave **Apps running at once** empty and **Usage credits (pay per use)** unchecked.
5. Choose **Create plan**.

Dashboard example: Define access and entitlements

Open: Plans & features → New plan

Example data only; use your own real dashboard.

Product: Orbit Notes

Plan name: Personal test

Slug: personal-test

Price: 0 — Reference metadata for this tutorial; creating a plan does not collect payment.

Currency: EUR

Billing type: One-time

Duration days: 30 — This teaching license lasts 30 days from its start.

Max devices: 2

Feature keys: csv_export

Apps running at once: Leave blank — Blank means a normal license, not a floating one.

Usage credits (pay per use):  — Leave off for your first feature-based export.

Buttons: Create plan

The 2-device allowance belongs to each customer license. It is different from the workspace device allowance.

**What these choices mean:** one customer's key works on at most two active computers, lasts 30 days, and includes CSV export. The plan price is reference data, not a payment being collected. Creating this record does not subscribe or charge anyone.

**Done when:** the plan appears under Orbit Notes. You can add stricter rules later; don't start with a credit/floating plan before the first standard export works.

## 4. Give one test customer a key

**Why:** your app needs a real customer license to activate.

1. Open [Licenses](/licenses), check that you are still in Sandbox, then choose **Create license**.
2. Choose **Orbit Notes / Personal test**. Enter an email you control and an example customer name.
3. Leave Company and External customer ID empty if not needed. Choose **Create license**.
4. Privately copy the newly created key. The dashboard creates or reuses the customer for that email automatically.

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.

Dashboard example: Your new license key

Open: Licenses → After creating a license

Example data only; use your own real dashboard.

License key: TEST-••••-••••-DEMO

Buttons: Copy license key

The masked key shown here is illustrative. Copy your actual new key privately and use its matching customer email. Directory keys stay masked until authorized reveal/copy.

**Done when:** you have three things: the public App ID, a Sandbox license key and its matching customer email. Do not put a key in a URL, screenshot shared with others or public source repository.

## 5. Run your first app and export a file

[Download the Python projects](/docs/downloads/kdpika-python-projects-1.0.0.zip) and extract the ZIP. The project README lists setup commands for Windows/macOS/Linux. Choose your first app:

- [Desktop window: install, activate and export step by step](/docs/v1/tutorials/python/desktop).
- [Local web app: start a Python server and download a protected file](/docs/v1/tutorials/python/web).

You do not need to package a macOS/Windows installer to learn. Run the `.py` example first. Put your **actual public App ID** in its environment setting, run the file and enter the license and matching email when asked.

The standard example protects its real CSV export like this:

```python
result = client.check()
if not result.valid or not result.signature_valid:
    stop_export()
if result.feature("csv_export"):
    create_the_csv_file()
# stop_export/create_the_csv_file are names in your own app.
# The downloadable example implements the actual file operation.
```

**Done when:** you open the exported CSV and see your message. A successful request alone is not access; the SDK verifies the signed result and the feature before the work.

## 6. Allow a customer to use the app offline

**Use this when:** a normal desktop app should keep working during a short connection outage. Skip it if every protected action needs your server.

1. Open [Products](/products), find Orbit Notes, and expand **Validation & device policy**.
2. For this test set **Validate every (hours) = 24** and **Offline grace (hours) = 168**.
3. Choose **Save policy**. In the app, choose **Check now** to fetch the latest policy online.
4. After one successful online check, disconnect and try a normal CSV export within the allowed window.

Dashboard example: Validation & device policy

Open: Products → Your product → Validation & device policy

Example data only; use your own real dashboard.

Validate every (hours): 24

Offline grace (hours): 168

Device matching: Flexible — allow some hardware changes

Matching components (flexible mode): 2

Buttons: Save policy

Example policy: normal access is checked daily, with up to 7 days offline within signed deadlines, never beyond license expiry. Floating leases and credits have stricter rules.

**What changes in Python:** keep using `client.check()`. It decides whether an authentic saved result is still usable. `client.validate()` forces an online check. You do not write your own timer or extend the signed deadline.

**Remember:** 168 hours is 7 days, never beyond license expiry. Offline revocation is learned after reconnecting. Floating seats end at their shorter lease deadline; usage credits have no offline fallback.

## 7. Choose how strictly to match a computer

**Use this when:** you want to make copying local activation files to a different machine less useful, or choose how much hardware change to tolerate.

1. In the same **Validation & device policy** section, choose **Strict** or **Flexible** under **Device matching**.
2. To try Strict, choose the exact option below, then **Save policy**.
3. To allow some non-critical component changes, choose Flexible and set **Matching components (flexible mode)**, for example 2.
4. Choose **Check now** while online and test on your actual target machine.

Dashboard example: Validation & device policy

Open: Products → Your product → Validation & device policy

Example data only; use your own real dashboard.

Device matching: Strict — all available components must match

Matching components (flexible mode): 2 — Used when you choose Flexible; it does not relax Strict mode.

Buttons: Save policy

Strict is less tolerant of hardware changes. Flexible still protects motherboard/CPU anchors when available. Virtual-machine detection alone never rejects access.

**What changes in Python:** nothing new is collected by your code. The SDK derives product-salted hardware hashes automatically and verifies the signed binding. Basic binding is always required; advanced policy settings require an eligible KDPika capability.

Flexible matching still protects saved motherboard/CPU anchors when available. It is not permission to move the same token anywhere. A VM hint alone does not reject access. If hardware is unavailable or mismatched, keep access locked; never replace it with a random fallback fingerprint.

## 8. Let many computers share fewer running seats

**Use this when:** a team may install the app on 10 computers but only 2 people should run it at once.

1. Create a **separate** software plan, such as Team seats, for Orbit Notes. Keep `csv_export` enabled.
2. Set **Max devices = 10**. In **Shared seats (optional)**, set **Apps running at once = 2**.
3. Use **Check interval = 180** and **Release an unreachable app after = 600** seconds, then create the plan.
4. Issue a new Sandbox license for it and try it on separate real installations.

Dashboard example: Shared seats (optional)

Open: Plans & features → New plan → Shared seats (optional)

Example data only; use your own real dashboard.

Max devices: 10 — Up to 10 registered machines in this example.

Apps running at once: 2

Check interval (seconds): 180

Release an unreachable app after (seconds): 600

Feature keys: csv_export

Usage credits (pay per use): 

Buttons: Create plan

Ten machines may be registered, but only two may run the app at once. The timeout must be at least twice the check interval. Values are tutorial choices, not your account limits.

**What changes in Python:** the current SDK automatically checks out a seat after a successful activation/check and renews it in the background. Keep checking before protected work. Call `client.close()` when your app closes; the downloadable desktop example already does that.

**Done when:** the first two running installations may use their permitted feature and a further one cannot obtain a new seat. Close one app and retry. A crash releases its seat after the timeout, not instantly. A closed app remains an activated machine until it is deactivated.

[See the short explicit checkout/close example](/docs/v1/tutorials/python/desktop#6-try-shared-seats). If the capability is unavailable in your workspace plan, this configuration is disabled; do not try to bypass it in code.

## 9. Charge one credit for each export

**Use this when:** your customer's access is based on completed jobs, not unlimited feature use. Credits are usage units, not money.

1. Create another software plan named **Export credits**. Enable **Usage credits (pay per use)**. Leave shared seats empty while learning.
2. Issue its new Sandbox license. New credit licenses start at zero.
3. On that license's **Manage** menu, choose **Usage credits**. Select **Add credits**, enter 3 and save.
4. Run the [durable credit export example](/docs/v1/tutorials/python/credits) with one stable job name.

Dashboard example: Define access and entitlements

Open: Plans & features → New plan

Example data only; use your own real dashboard.

Plan name: Export credits

Max devices: 2

Apps running at once: Leave blank — Learn credit charging without mixing in shared seats.

Usage credits (pay per use): 

Feature keys: Leave blank — The credit job is authorized by its signed consume receipt.

Buttons: Create plan

New metered licenses start with zero credits. Issue a new Sandbox license for this plan, then add units on that license.

Dashboard example: Manage usage credits

Open: Licenses → Manage → Usage credits

Example data only; use your own real dashboard.

Action: Add credits

Credits: 3

Note (optional): Initial tutorial allowance

Available credits: 0 → 3

Buttons: Save credit transaction

Usage units, not money. This does not charge a card or refund a payment.

**What changes in Python:** `client.consume(1, idempotency_key=the_same_job_key)` must confirm the signed charge **before** that job runs. The downloaded `credit_export.py` keeps the job key in SQLite and writes a real CSV.

Run the identical completed job again: the same output is returned without another debit. After a lost reply, retry the same job/key and message. A new key for the same uncertain job could charge twice. The final successful charge may leave zero and still authorizes that paid operation.

Dashboard example: Latest 50 transactions

Open: Licenses → Manage → Usage credits

Example data only; use your own real dashboard.

Grant: +3 → balance 3

Consume: −1 → balance 2

Reopening the same completed job does not create another debit. Your app must preserve the original job key.

**Done when:** the balance changes from 3 to 2 for the first job, then stays at 2 when that same completed job is reopened. The SDK deduplicates the charge; your application also needs to deduplicate its actual work. The example implements that for its CSV, not arbitrary external payments or emails.

## 10. Give one customer an extra device or feature

**Use this when:** the plan normally allows 2 devices, but Jane should get 3 without changing everyone else.

1. Open [Licenses](/licenses), find Jane's license, and use **Manage → Limits & overrides**.
2. At **Devices**, choose **Set for this license** and enter 3.
3. Add an optional reason, then choose **Save overrides**.
4. To remove the personal exception later, use **Reset to plan default** for that field and save.

Dashboard example: This license’s limits

Open: Licenses → Manage → Limits & overrides

Example data only; use your own real dashboard.

Devices: Plan now: 2
Current: 2 · Override: None

Devices action: Set for this license

Devices: 3

Reason (optional): Extra machine for Jane’s laptop

Buttons: Reset to plan default, Save overrides

Only this license changes. Reset removes this field’s personal override and returns it to the current plan setting.

The same editor can adjust total activations, shared seats, expiry and extra features when allowed. Max devices counts active installations; total activations counts first activations/reactivations over the license lifetime. Deactivation does not reset the cumulative total.

**What changes in Python:** no customer-specific `if customer == ...` code. The SDK uses the server's effective signed limits/features. Reconnect and choose Check now for a new result; old valid offline caches cannot learn the change instantly.

## 11. Change the default for a whole plan

**Use this when:** a whole offer changes, such as raising the normal device allowance from 2 to 3.

1. Open [Plans & features](/plans) and choose **Edit** on the right plan.
2. Set **Devices = 3**. Choose **New licenses only** to leave current customers alone, or the existing-license option below to update current grants too.
3. Choose **Review changes**, inspect the affected-license count and warnings, then save the reviewed change. Individual overrides are kept.

Dashboard example: Who receives these changes?

Open: Plans & features → Your plan → Edit

Example data only; use your own real dashboard.

Devices: 3

New licenses only: 

New and existing licenses on this plan:  — Applies across Live and Test. Individual overrides are kept.

Reason (optional): Increase allowance for current customers

Buttons: Review changes

Review the affected-license preview and any risk warning before the final save. Existing usage above a lowered limit is preserved; additional devices/seats are blocked.

To change a feature on the Edit screen, edit **Plan features** as JSON, such as `{"csv_export": true}`. This is different from the comma-separated **Feature keys** used on the New plan form. Setting false disables an entitlement; your app and plan must use the exact same feature key.

Dashboard example: Features

Open: Plans & features → Your plan → Edit

Example data only; use your own real dashboard.

Plan features: {"csv_export": true}

New and existing licenses on this plan: 

Buttons: Review changes

New plan creation takes comma-separated Feature keys. The Edit screen uses feature JSON. Use false to disable a flag; do not rename the key without updating your app.

**Important:** plan changes can apply across Live and Test because the plan is shared. Use a separate tutorial product. Lowering a limit preserves existing active connections but blocks additional ones while usage is above the new limit. Prices/payment schedules are not rewritten by editing access settings.

## 12. Let customers help themselves

**Use this when:** customers should review expiry, see their machines or free a genuinely stuck shared seat without asking you every time.

1. Open [Customer portal](/portal-settings).
2. Copy **Customer sign-in page (Live)**. For this Sandbox test, append `?environment=SANDBOX` to the copied URL.
3. Open it and use the customer's email or license key. The actual portal verifies access; do not build your own token URL.
4. Put that publisher sign-in link in your app's Help menu. It is a normal sign-in URL, not a reusable secret login token.

Dashboard example: Share your portal

Open: Customer portal → Share your portal

Example data only; use your own real dashboard.

Customer sign-in page (Live): Copy the URL shown in your real account

Test environment: Append ?environment=SANDBOX

Buttons: Copy link

This is a publisher sign-in page, not a secret session link. Never add a license key to its query string.

**What changes in Python:** use `webbrowser.open(portal_url)` with the real public sign-in URL from your account. No separate portal SDK is required. Email sign-in may show this customer's authorized licenses; key sign-in opens only its own license. The portal can show balances/history and active shared sessions. It does not sell plans, collect payments, issue invoices or refund money.

## 13. Make the portal match your brand

1. In the same page, enter **Display name**, your HTTPS **Logo URL**, **Accent color** and **Support email**.
2. If your platform plan includes it, select **Hide KDPika branding in the customer portal and self-service sign-in email**.
3. Set the allowed **Removals per customer, per product, in any 30 days**, then choose **Save portal settings**.

Dashboard example: Your brand and self-service limits

Open: Customer portal → Your brand and self-service limits

Example data only; use your own real dashboard.

Display name: Orbit Studio

Logo URL (optional): https://your-site.example/logo.png

Accent color: #7657d5

Support email (optional): help@your-site.example

Hide KDPika branding in the customer portal and self-service sign-in email: 

Orbit Notes: 3 — Removals per customer, per product, in any 30 days.

Buttons: Save portal settings

Hide branding only when your platform plan includes it. Machine removal and ending stuck sessions share this allowance. No custom-domain hosting is added.

**Why the removal limit matters:** deactivating machines and ending stuck sessions share one customer/product allowance. Set 0 if customers should contact you instead. The example value 3 allows limited self-service without unlimited cycling. Full white label does not add a custom-domain host or remove all signs of the actual platform domain.

## 14. Release an old computer safely

1. Open the license's **Limits & overrides** and find **Machines on this license**.
2. Choose **Deactivate** to release the machine's access, or **Remove** to release/hide it while preserving history.
3. Read and confirm the action. Use license suspension/revocation if the whole key should stop working.

Dashboard example: Machines on this license

Open: Licenses → Manage → Limits & overrides → Machines on this license

Example data only; use your own real dashboard.

Jane’s laptop: Active

Jane’s old computer: Inactive

Buttons: Deactivate, Remove

Deactivate releases access. Remove also hides the machine while retaining history. A valid key may activate again if limits permit; use license suspension/revocation to stop all license access.

**What the app sees:** new checks using revoked device tokens are refused and activation is needed again. A valid key might reactivate if limits permit; removing one machine is not a permanent hardware ban. An offline app learns about the server-side change when it reconnects or reaches its signed deadline.

## 15. Understand workspace limits without putting them in code

Open [Billing & usage](/billing). These example figures show a Business-style allowance; your administrator may configure different values.

Dashboard example: Billing & usage

Open: Billing & usage → Your plan and usage

Example data only; use your own real dashboard.

Products: 1 / 100

Active licenses: 1 / 100,000

API requests / month: 12 / 5M

Active devices: 1 / 250,000

Example Business-style allowance only; read your current account’s configured values. Never copy these numbers into Python constants.

- **Products:** apps you manage, each with an App ID. It is not the number of features in your Python app.
- **Active licenses:** customer grants in the workspace, not devices or running sessions.
- **API requests / month:** the workspace allowance. Usage warnings do not automatically block requests. Let `check()` use a valid cache; don't force online checks for every keystroke.
- **Active devices:** workspace planning allowance; the customer's own software-license device limit is enforced separately.
- **Live + Sandbox:** separate test/real license data. A Sandbox key does not become Live by changing a label.
- **Log retention:** how long eligible server logs are retained. It does not extend license expiry or offline access.
- **Support:** the service level of your platform account; dedicated support is not a promise of instant response.

Do not copy 100, 100,000, 5M, 250,000 or 365 into Python constants. Configure the actual license policy and use the SDK's verified result.

## 16. See who changed access

Open [Audit logs](/audit-logs) if your KDPika plan includes audit access. Look for the recorded plan/override/credit changes you just performed.

Dashboard example: Audit logs

Open: Audit logs

Example data only; use your own real dashboard.

Plan updated: Recorded

License override updated: Recorded

Credit grant: Recorded

Illustrative event summaries, not real customer records. Access and retention follow your platform plan; this is not an audit trail for every button inside your app.

This is the platform's history of eligible actions, subject to retention. It does not record every export or button in your own application. If you need that, add your own application event log without keys, tokens, emails or raw hardware identifiers.

## 17. Move to Live only after your tests work

1. Finish the standard export, offline/lease/credit cases you actually intend to ship, and test on your target Windows/macOS/Linux machine.
2. Use a platform plan that includes Live licensing. Switch your dashboard Environment to Live and issue real customer licenses there.
3. Change your packaged app's environment to `LIVE` with a deliberate release configuration. Keep the same product's real public App ID and a separate private Live state.
4. Test your real store/email/renewal process. Do not distribute tutorial keys or copy your local activation directory into an installer.

Dashboard example: Move from testing to real licenses

Open: Dashboard header → Environment

Example data only; use your own real dashboard.

Environment: Live

Use an eligible paid plan, issue new Live customer licenses and set KDP_ENVIRONMENT=LIVE in the app. Sandbox keys/state do not become Live automatically.

## Your next small step

[Open the desktop instructions](/docs/v1/tutorials/python/desktop) or [the local web instructions](/docs/v1/tutorials/python/web). The files already implement the standard protected export; you only supply your App ID and your own Sandbox license. Then add **one** advanced model from the steps above.

[Download project files](/docs/downloads/kdpika-python-projects-1.0.0.zip) · [All language learning paths](/docs/v1/tutorials) · [Python API reference](/docs/v1/sdks/python).
