Choose a workflow that matches the current licensing contract instead of assuming a store feature is a licensing API feature.
What you will learn
- Configure supported trial, duration, installation and feature workflows.
- Separate reversible suspension from terminal revocation.
- Recognize unsupported configuration and migration operations before building automation.
Prerequisites
- Complete the Sandbox quickstart.
- Read authentication, lifecycle and idempotency.
- Keep your own verified order-to-license mapping and buyer authorization on your backend.
- Use the complete tested programs linked from each operation; do not embed a developer key in a buyer app.
Support matrix
| Recipe | Current support boundary |
|---|---|
| R01 Trial licenses | Duration is supported; one-trial-per-person enforcement is your backend's responsibility |
| R02 Perpetual and subscription licenses | Null or day-based expiration is supported; billing is your store's job |
| R03 Upgrades | No in-place v1 plan change; use an explicit replacement/handover when authorized |
| R04 Downgrades | No built-in scheduled license plan switch; schedule in your backend and handle replacement explicitly |
| R05 Plan changes and renewal | Renewal adds days; it does not change plan, features or seat limits |
| R06 Moving to a new device | Deactivate or reset releases active device slots, not lifetime activation count |
| R07 Multiple seats and teams | Per-license installation counts are supported; buyer team-user management is not |
| R08 Application version ranges | Engine fields exist but public configuration is absent and omitted version skips the server check |
| R09 Feature flags | Boolean plan feature creation and signed feature reads are supported; general numeric editing is not exposed |
| R10 Grace after expiration | Offline transport grace does not extend license expiration |
| R11 Bulk issuance | Use the tested CSV API tool, not an invented native importer |
| R12 Customer self-service | Customer portal shows licenses/devices and permits own-device release; it is not a store-billing portal |
| R13 Key email delivery | Deliver your saved key privately; automatic key email/recovery is not provided |
| R14 Chargebacks | Use an authenticated provider decision and explicit suspend/restore/revoke policy |
R01 — Time-limited trials
A product plan with billing type Trial uses trial_days when issuing a new license. A nonzero duration creates an expiration relative to issuance time, not first activation. Trial creation uses the ordinary license-create API and its rate, scope and allowance rules.
- Create a product plan with billing type Trial and a positive Trial days value.
- Choose deliberate device and feature limits for evaluation.
- Verify the requester using your own signup/order process.
- Issue a Sandbox license and save its ID, complete key and expiry securely.
- Activate it using the full quickstart.
- Test the application's expiration/refusal message before offering the trial publicly.
Expected result: a seven-day trial receives an expiration about seven days after issuance. Do not assume a future starts_at delays access: it is not enforced as a not-before gate. Do not leave Trial days empty and assume a default evaluation period. Multiple creates for the same email can issue several trials; the platform does not provide one-trial-per-person, hardware anti-abuse or disposable-email enforcement for your product. Record trial eligibility on your own backend and do not rely on the Free platform plan as the buyer's trial system.
R02 — Perpetual versus subscription access
Licensing duration and payment collection are separate. A plan label such as Monthly is not an automatic charge schedule. Creation derives expiry from trial_days for Trial plans or duration_days for other billing types. No duration means null expiration; selecting Lifetime with a populated Duration days does not automatically erase that duration.
- For perpetual access, create the intended product plan with no duration and verify expires_at is null.
- For a fixed paid period, configure the deliberate day count and verify the actual returned UTC expiration.
- Record the verified store payment and resulting license ID in your own ledger.
- For later paid periods, call renew only through a deduplicated, serialized workflow.
- Reconcile the provider's actual calendar period with the day-based license expiration.
Expected result: the perpetual fixture has no expiry and a 30-day fixture expires about 30 days after issuance. Thirty days is not every calendar month; 365 days is not every calendar year. The v1 renewal API cannot set an arbitrary exact timestamp. Do not silently round a store period or grant extra time without an explicit policy. A store subscription cancellation does not automatically mutate a buyer license.
R03 — Upgrade by authorized replacement
There is no public v1 endpoint or current license-dashboard control for changing an existing license's product plan. A safe supported alternative is a deliberate new license and handover, not a hidden database update or a call to renew disguised as an upgrade.
- Verify the buyer's paid upgrade in your backend and confirm the intended target product plan.
- Record one stable upgrade-operation ID before creating a replacement.
- Create the target-plan license and save its complete key in your secured outbox.
- Deliver the replacement key after authenticating the recipient.
- Let the customer activate the replacement in your application's handover flow.
- Reconcile the new license and explicitly revoke or suspend the old license according to your authorized policy.
- Keep the old/new mapping for support and duplicate-event handling.
Expected result: the replacement's signed features match its own target plan; the old license remains usable until you explicitly act on it. This is not an atomic migration API. Creating a replacement uses current plan defaults and starts its own duration; it does not automatically carry over expiry, installation history, discounts or payment proration. Revocation is terminal. Define and explain that handover before automating it, or handle the upgrade manually until your requirements fit the supported operations.
R04 — Downgrade at the intended boundary
The licensing API has no built-in scheduled plan-change resource. A store's scheduled downgrade or cancelled renewal must not be treated as an immediate instruction to revoke already-paid access.
- Save the verified requested downgrade and its effective paid-through date in your backend.
- Leave the current license untouched until your policy says the new access begins.
- At the effective boundary, verify the current store/order state again.
- Create and privately deliver a lower-plan replacement only when your policy authorizes it.
- Reconcile that handover and retire the old license explicitly.
- Check that the new signed payload no longer grants higher-plan-only features.
Expected result: no premium feature is inferred from a buyer's historical plan name or a stale cache. The tested lower-plan fixture does not include pdf_export. The date scheduler, credit/refund calculation and paid-period reconciliation belong to your backend/store, not to a new KDPika endpoint. If precise seamless in-place migration is required, it is not supplied by the current v1 contract.
R05 — Plan changes are not renewal
Renew adds an integer from 1 through 3650 days. It extends a future expiration from that expiration; otherwise it extends from now. It can return an expired license to active, but a suspended license stays suspended. A revoked or cancelled license cannot renew.
- Save each verified paid-period effect under a unique business-operation key.
- Check the current license summary and expected old expiration.
- Call renew once with the intended supported day count.
- Persist the returned expiration and compare it with your recorded expectation.
- If the response is lost, stop for reconciliation instead of adding the same days again.
Expected result: the fixture adds exactly one day while plan_id remains unchanged. Renewal does not modify the product plan, feature schema, customer, max_devices or max_activations. Restoring a payment-suspended license is a separate authorized operation, and previously revoked activation tokens may require reactivation. Do not promise a public edit-plan or schedule-plan-change endpoint.
R06 — Move a license to a new installation
- Generate a stable random installation ID on each installation.
- When the user can access the old installation, call deactivate there.
- Confirm the old activation token is no longer usable.
- Activate the key on the new installation with its own stable ID.
- If the old installation is unavailable, authenticate the buyer before a support device reset.
- Explain any lifetime activation limit before asking the user to try again.
Expected result: deactivation or reset frees active device slots. Reset revokes active tokens and deactivates devices but does not clear the lifetime activation_count. A license can therefore have zero active devices and still return ACTIVATION_LIMIT_REACHED. Do not create a fresh random device ID on each application launch, clone a token between computers or promise unlimited transfers when max_activations is finite.
R07 — Multiple seats without an invented team service
A product plan's max_devices is the maximum active installation count for a license. It is not automatically a count of human users, concurrent sessions or verified employees.
- Choose a supported positive Max devices value when creating the product plan.
- State clearly whether your commercial seat policy means installations or named people.
- For a shared installation-license arrangement, activate each permitted installation using a different stable ID.
- Test the next installation beyond the maximum and handle DEVICE_LIMIT_REACHED clearly.
- If you need named-user seats, authenticate and manage those memberships on your own backend or issue separately tracked licenses per authorized recipient.
Expected result: a two-device fixture permits two active installations and refuses a third. The platform does not verify that two installations are two distinct humans. The developer's platform-plan team_members allowance is not a buyer-team invitation or seat-allocation API. Do not publish a developer API key or let arbitrary buyers manage someone else's devices.
R08 — Version ranges are not a complete public workflow
The engine can inspect stored min_version/max_version during activation when app_version is present. The signed payload also contains those fields. However, current v1 creation does not set those bounds and the dashboard has no supported version-range editor. Do not modify the production database from an integration guide to fill the gap.
The isolated fixture test configures bounds only to verify existing engine behavior. It refuses 0.9.0 and 3.0.0 outside an example 1.0.0–2.0.0 range, and accepts 1.5.0. It also confirms a limitation: omitting app_version skips that server range check. Validation does not perform a new range check against a caller-supplied application version.
- Do not advertise a configurable secure version-range product using the unsupported public workflow.
- Always send your actual application version in supported clients.
- Where version-specific feature access is needed, define stable feature keys in supported product plans and enforce them in a verified valid client result.
- Keep valuable remote version-dependent actions authorized by your backend.
- Confirm an approved configuration/enforcement workflow before relying on stored range fields.
Expected result: the current limitation is explicit rather than represented as a security guarantee. A client-controlled version string is not proof of an untampered binary. This recipe supplies no fabricated set-version API, plan editor or automatic version entitlement migration.
R09 — Feature-flag licensing
- Choose stable lowercase feature keys when creating a product plan.
- Issue a Sandbox license with the intended features.
- Activate or validate it and verify the Ed25519 signature and identity/time policy.
- Require a valid result before reading a feature.
- Require an actual boolean true for a boolean feature and a validated integer for a count.
- Test missing, false and wrong-typed values with restrictive defaults.
Expected result: the Pro fixture's valid signed features include pdf_export=true; a lower fixture does not gain it. Use the full guarded examples, not a truthy plan-name comparison. The current dashboard creates plans with boolean feature keys; it does not expose a general numeric JSON feature editor or a public feature-management API. An existing signed cache remains a snapshot until refreshed/refused. The current Plans & features screen offers creation and state/archive actions, not a general edit-plan form.
R10 — Offline grace does not extend paid expiration
Offline grace is a transport-failure policy bounded by the signed deadline and license expiration. It is not grace days automatically added after subscription cancellation or a rejected payment.
- Configure the supported product validation and offline-grace policy deliberately.
- Test offline transport failure before the signed grace deadline while the license is still valid.
- Test the exact signed expiration boundary and refuse access there.
- Test a server rejection and confirm no offline permission is substituted.
- For an explicit commercial courtesy extension, authorize a separate deduplicated renewal rather than modifying client time checks.
Expected result: a signed grace deadline can be later than expires_at, yet expired access is still refused. Existing executed SDK tests deny the exact expiry boundary and permit only eligible transport fallback. See offline mode and complete SDK configuration. Renewing a past license extends from now, not from its old expiration; define the commercial effect before using it as a courtesy extension.
R11 — Bulk license issuance
- Verify the orders/recipients in your own source system.
- Build a private one-product CSV with stable order-line IDs.
- Run the complete manual issuance tool's dry-run.
- Apply it with a product/environment-scoped backend key and a private durable ledger.
- Export saved keys only for authorized delivery.
- Investigate held IN_FLIGHT, REVIEW or CONFLICT records before another write.
Expected result: a repeated completed order does not create another license even after the API's replay window. The actual tool test loses a real successful response and confirms zero extra requests on rerun. No native CSV-import API is claimed. Protect the CSV as customer data and never email a developer API key with it.
R12 — Customer self-service
- Verify the customer and select the correct workspace environment.
- Open Customers and find the intended recipient.
- Choose the displayed portal-link duration and, when appropriate, Email.
- Select Create link and deliver that private bearer URL only to its authorized recipient.
- Let the customer review licenses/devices and release their own installation.
- For a replacement link, create another and confirm the old link is no longer accepted.
Expected result: the portal displays key masks, not original full keys, and can release a device owned by that customer. It does not offer buyer store refunds, change a store subscription, recover a lost plaintext key or create an owner account in your workspace. The exposed duration choices are one, four and 24 hours; server-side creation bounds the duration to 15–1440 minutes. These are bearer links: do not put them in analytics, public screenshots or a shared forum.
R13 — License-key email delivery
Creation returns the complete key to the authenticated issuing backend once; the outbound license.created event contains only its mask. LicenseService does not automatically send a complete-key email. Customer portal emails contain a portal link, not key recovery.
- Save the successful creation result in your encrypted delivery outbox before marking the issuance complete.
- Bind the intended recipient to your verified order/customer mapping.
- Queue one delivery effect under a durable order-line/license delivery ID.
- Send using your reviewed backend email workflow, with no API credential in the email.
- On a resend request, authenticate the buyer and reuse that saved key rather than creating a new license.
- If the original key is lost, investigate and plan an explicit authorized replacement instead of emailing the mask.
Expected result: the issuance ledger/outbox separates license creation from email delivery. The tested private export tool makes the original key available without printing it to logs; email delivery itself is your backend workflow and is not falsely claimed as sent by that tool. Store keys encrypted with restricted access and a separate key backup. Do not make the platform's 24-hour sensitive replay response your permanent key vault.
R14 — Refunds, disputes and chargebacks
- Authenticate the actual provider event and match its account, environment and original order/license mapping.
- Retrieve current provider state when needed; do not act on a browser return or an unverified reason string.
- Decide whether the confirmed event requires no licensing action, temporary suspension or final revocation under your disclosed policy.
- Use suspend for a reversible investigation if appropriate.
- After a confirmed resolution, restore only when the current license state and expiry permit it.
- Use revoke only for an explicitly authorized terminal access decision.
- Persist the effect, outcome and original/current provider state in your durable ledger.
Expected result: a suspended fixture can restore and reactivate; its revoked token does not become usable again automatically. A revoked license cannot be restored by calling restore, even if a dispute later closes in your favor. A partial refund, pending refund or newly opened dispute is not automatically proof that all paid access must end. The provider confirms money movement; the licensing API only changes license access. Renewal cancellation does not by itself refund a payment or invalidate already-paid access.
Not supported as automatic v1 features
| Requirement | Honest alternative or boundary |
|---|---|
| In-place/scheduled product-plan migration | Authorized replacement with explicit access/expiry handover, or obtain an approved product feature before promising it |
| Exact calendar paid-through timestamp setter | Reconcile day-based supported renewal with your provider; do not silently invent a timestamp endpoint |
| One trial per real human | Your backend eligibility/identity policy; repeated same-email creation is allowed |
| Buyer-team invitations or named-user enforcement | Your own authenticated membership model or separately tracked individual licenses |
| Version-range setup through v1/dashboard | No supported public setup; blank version skips the existing engine check |
| General numeric feature editor | Validate typed reads, but confirm a supported configuration workflow before selling numeric limits |
| Expired license access via offline grace | Not allowed; explicitly authorize any commercial extension |
| Original-key recovery from mask or portal | Saved encrypted outbox, or an authorized replacement process |
| Automatic provider refund/cancel/charge from licensing API | Your store/payment backend owns payment operations |
Expected result
Your implementation uses the actual lifecycle and tested examples, keeps payment confirmation on your backend and documents any unsupported commercial promise before offering it to buyers.
Common mistakes
- Calling renew to change a plan or repeating it after a lost response.
- Calling a device count a verified human-seat count.
- Assuming a trial label enforces one free trial per person.
- Treating a signed notification or an SDK feature helper alone as permission to unlock access.
Security notes
Use validity and feature guards for every unlock. Public product IDs, plan names and a user-supplied version are not secrets or identity proofs. A user with full control of a local executable can patch local checks. Protect valuable online operations behind your own authenticated authorization, and handle refunds/access changes without exposing payment or developer credentials to the buyer application.
Troubleshooting
| Symptom | Actual boundary | Fix |
|---|---|---|
| Renewed license still suspended | Renewal does not restore suspension | Reconcile payment/state and perform a separate authorized restore |
| Reset license cannot activate | Lifetime activation count is exhausted | Review the supported transfer policy; reset is not a lifetime-counter reset |
| Changed store plan still shows old license plan | No automatic product-plan migration exists | Use your recorded handover workflow, not renew |
| An old feature remains visible | Client still has a prior signed snapshot | Validate online and inspect validity plus feature type |
| Portal cannot show the full key | Only masks are available there | Use your private saved key or authorized replacement workflow |
Next steps
Compare the four reference architectures, then implement the manual issuance/reconciliation flow before automating store events.