---
title: "One license for desktop and web"
description: "Business lets you choose Desktop only, Web only or both per software plan. Starter and Pro include both automatically."
slug: "/docs/v1/licensing/application-access"
section: "Licensing"
order: 224
version: "v1"
last_updated: "2026-10-27"
audience: "all"
level: "beginner"
draft: false
layout: "guide"
related: ["/docs/v1/workspace/plans-and-features", "/docs/v1/sdks/overview", "/docs/v1/tutorials/php"]
---

## What this means

Your app can have a desktop version and a web version under **the same public App ID**. The same customer key can activate both when the license includes both. This works for monthly/annual subscriptions, one-time purchases and lifetime access; it does not create a new payment schedule.

| Your KDPika workspace plan | What you can offer |
| --- | --- |
| Starter / Pro | One key works for both desktop and web under the same App ID. You cannot restrict individual software plans to one app family. |
| Business | Choose Desktop only, Web only or Desktop + Web for each software plan. |
| Free | Basic testing in Sandbox only; no Live sales. Per-plan app restrictions are not included. |

Authorized internal unlimited/special access keeps the Business-equivalent capabilities granted by its platform settings. An expired workspace follows its effective access plan.

**One license means one shared allowance.** If a key allows 3 devices, a desktop installation and a PHP server normally use two of those slots. Floating seats and usage credits also remain shared. A browser tab is not a separate licensed machine in this server-side PHP example.

## 1. Choose where your software plan works

1. Open [Plans & features](/plans), then **New plan** or **Edit plan**.
2. In **Available applications**, choose **Desktop + Web · one key**, **Desktop only** or **Web only**.
3. Save. When editing, select **New licenses only** (default) or **New and existing licenses**, review the impact and confirm if required.
4. Issue the customer a license normally. No second App ID or duplicate customer key is required.

Business is enforced in server-side writes, not just by disabling a field. Reserved feature JSON `application_access` can contain only `both`, `desktop` or `web`. It cannot bypass the platform plan through an API, WooCommerce editor, or per-license feature override. Starter/Pro use `both` on effective signed responses, including after a downgrade; saved feature/limit data and credit balances are preserved.

Extra feature values on one license can override `application_access` on Business using the existing audited feature override editor. Reset features returns to that license plan's default. Product/environment/tenant checks still apply.

## 2. Configure each app once

This is **publisher configuration in your application code**, not a dropdown for your end customers to claim another app type.

| SDK current source | Desktop | Web backend |
| --- | --- | --- |
| Python 2.1.0 | `application_type="desktop"` | `application_type="web"` |
| PHP 2.1.0 | `applicationType: 'desktop'` | `applicationType: 'web'` |
| JavaScript 2.1.0, Node/Electron main | `applicationType: "desktop"` | `applicationType: "web"` |
| C# 2.1.0, .NET 10 | `applicationType: "desktop"` | `applicationType: "web"` |
| Rust 0.3.0 | `.application_type("desktop")` | `.application_type("web")` |

Defaults are **web for PHP** and **desktop for the other SDKs**. Always select explicitly in a web backend. The same App ID/key works in both apps; each has its own private installation state. Context is now included in the SDK storage namespace. Updating from earlier sources can create a new installation: release the old activation using the old app first or remove the old machine through your authorized dashboard/portal. Do not delete state as a substitute for deactivation.

These source versions are available as [SDK ZIP downloads](/docs/v1/sdks/overview#download-and-verify). They are **not yet published to PyPI, crates.io, Packagist, npm or NuGet**. Earlier registry packages do not expose this constructor setting; use the current source ZIP for restricted plans.

## 3. Install the source you need

Download and extract the SDK for your language. Compare SHA-256 against the [download table](/docs/v1/sdks/overview#download-and-verify). Keep normal supported runtime requirements.

- **Python:** create a supported Python 3.11–3.14 virtual environment, then run its Python with `-m pip install /path/to/extracted/python-sdk`. This installs the local 2.1.0 source, not the older PyPI version. Run the same environment Python for your app. See [beginner Python setup](/docs/v1/sdks/python#install-python-once) for OS-specific interpreter commands.
- **PHP:** in the extracted PHP SDK folder run `composer install --no-plugins --no-scripts`, then require its `vendor/autoload.php` from your app. Do not put credentials or activation state in the public web folder. The License Lab uses the same current source directly on its server.
- **JavaScript:** in your app run `npm install --ignore-scripts /path/to/extracted/javascript-sdk`. Use Node.js 22+ or Electron's main process; no browser fingerprint fallback.
- **Rust:** add `kdpika-licensing = { path = "/path/to/extracted/rust-sdk" }` to your app's Cargo dependencies. Use Rust 1.90+.
- **C#:** reference the extracted `KDPika.LicenseSdk/KDPika.LicenseSdk.csproj` from your application using `dotnet add reference /path/to/extracted/csharp-sdk/KDPika.LicenseSdk/KDPika.LicenseSdk.csproj`. Use .NET 10 LTS. Do not request version 2.1.0 from NuGet before it is published.

## 4. Use a verified result before protected work

```python
from pathlib import Path
from kdpika_licensing import LicenseClient

client = LicenseClient(
    product="YOUR_PUBLIC_APP_ID",
    environment="SANDBOX",
    application_type="web",
    storage_dir=Path("/private/your-web-license-state"),
)
result = client.check()
if not result.valid or not result.signature_valid:
    raise SystemExit("Access is unavailable. Check your license and app type.")
if result.feature("csv_export"):
    # Run your actual authorized export here.
    pass
```

The server checks `X-Application-Type` before consuming an activation/seat/credit. The context is bound to the activation and included with `application_access` in the Ed25519-signed payload. Validation, heartbeat, checkout and credit operations use that same bound context. A token from the desktop app cannot be relabelled as web. SDKs check the signed policy again for network and cached decisions.

| Error | What to do |
| --- | --- |
| APPLICATION_NOT_ALLOWED | This license does not include this app family. Use the matching license or change its plan with the required Business permission. |
| APPLICATION_TYPE_REQUIRED | A restricted license was used without context. Use the current source SDK and configure desktop or web. |
| APPLICATION_TYPE_MISMATCH | An activation token belongs to another app family. Keep separate installation state and activate the matching app. |
| INVALID_APPLICATION_TYPE | Configure exactly desktop or web. Do not send an OS name such as Windows as the application type. |

Returning a shared seat or deactivating an old installation remains possible when its access scope changes. A lower limit does not silently remove other active devices/seats. A changed restriction is learned at the next online check; offline clients must still obey the signed access deadline they already received.

## 5. Try the real demo

1. Open [License Lab publisher controls](/license-lab/publisher). Its dedicated sign-in stays inside the demo and uses your publisher account's password/MFA, with a separate session.
2. Choose **Prepare test licenses**. On Business it adds Desktop only, Web only and Desktop + Web licenses alongside the existing standard/seats/credits tests.
3. Use **App context = Web** and try the Desktop only test: activation is refused by the actual API.
4. Use **App context = Desktop** in the server SDK test runner and activate Desktop only: it succeeds. A Web only license is refused in that context.
5. Use the Desktop + Web test in each context. Both succeed with the same key, using separate installation identities and a shared allowance. No new payment is taken.
6. For the customer website, open [Demo sign-in](/license-lab/login). That site is always configured as web; customer requests cannot select desktop or unlock publisher management.

The publisher runner labelled Desktop still runs PHP on the hosting server. It verifies context enforcement; it is not proof of running on your laptop. Test a real desktop client separately before distributing it.

## Security boundary

An application type is a claim from trusted publisher code, **not remote attestation** that a program is a desktop binary or browser. Someone who can modify executable code can change the claim or bypass local application checks. Protect valuable online operations on your own authenticated backend too. Browser-only/WASM hardware fingerprint fallback is unsupported. Do not embed management API keys in a distributed app. Ed25519 signing, raw-serial hashing, tenant isolation, atomic seat reservations and idempotent credit operations remain in place.
