Licensing by version
Every key covers a range of major versions, with every minor and patch release in them. A key that covers version 2 runs 2.0, 2.4.1 and every other 2.x release.
At a glance
- A major version
- Any whole number from 1 to 99,999
- Upgrade protection
- 1 to 120 months, 12 for a new model
- A month of protection
- 30 days, counted from the sale
- Your product's current version
- 1 until you set it
What a key covers
The license model a key is sold under sets which versions it covers, under Versions in the model dialog:
| Versions | Covers | Default for |
|---|---|---|
| Version sold | Only the major version the sale is for. A key sold for 2.4 runs every 2.x release. | A new perpetual model |
| Sold and later | The major version the sale is for, and every later one. | |
| Every version | Every major version, older and newer. | A new subscription or trial model |
| Specific versions | A range you set, such as 3.x alone or 2.x and later. |
A key sold under no model gets the default for its type. Editing a model does not change keys already sold under it.
The version a sale is for
Version sold and Sold and later depend on which version was bought. KeyGrant takes it from the sale:
- Issued by hand or from your server: the Version sold field on the
Issue license form, or
versionon POST /v1/licenses. - Sold through Stripe:
versionin the checkout's metadata. A Payment Link's metadata is copied onto every checkout it starts, so set it on the link. A value that is not a valid version is ignored, so a typo never blocks a paid sale.
Write the version in any common form: 2.4.1, v2, 2, or with a
suffix such as 2.4.1-beta+5. Only the major version counts, and 0.x counts as 1.
When a sale names no version, the key's first activation sets it, from the app's major. So if
a customer buys a key and installs a later build before activating, the key runs that later build. A
license file never activates: its key takes the product's current version when its file is made.
Tell the SDK each build's major
Set major in every build, as a whole number from 1 to 99,999:
const license = new License({ product: "my-product", apiBaseUrl: "https://api.keygrant.dev", publicJwks: PUBLIC_KEYS, major: 2, // this build is 2.x});
const status = await license.status();if (status.state === "invalid") { // The key stops below this build: offer the upgrade. if (status.reason === "upgrade") showUpgradeOffer(); // The key starts above this build (a 3.x key on a 2.x app): update the app. if (status.reason === "outdated") askToUpdateTheApp();} - A 0.x build sets 0 or 1: the SDK reads 0 as 1, as KeyGrant reads a 0.x version.
- It defaults to 1, so a build that leaves it out sets a Version sold key sold without a version to 1.x, and a Sold and later key to 1.x and later.
-
When a key does not cover the build,
status()returnsinvalid, online and offline: reasonupgradewhen the build is newer than the key covers, andoutdatedwhen it is older. - The lease stays valid for every build it covers, so going back to a covered build works offline.
Upgrade protection
A model on Version sold, or on a Specific versions range with a highest version, can add Upgrade protection, from 1 to 120 months:
A new model starts with it on at 12 months. A month is 30 days (12 months is 360 days), counted from the sale, not from the first activation.
While it lasts, the key covers every version from its lowest up. Each check-in records the major the app is running, up to your product's current version at that moment.
When it ends, the key keeps the newest major recorded this way. A customer who updated to 2.0 during protection keeps 2.x; one who never updated keeps the version they bought.
A major run offline during protection still counts: the SDK reports it at its next check-in, even after protection has ended.
Your product's current version
Every product has a current version, the newest major you have released. A check-in never records a major newer than the current version, regardless of what the app reports, so raise it as soon as you release a new major. It is 1 until you set it. You can change it in three ways:
- By hand: Current version on the product's Settings tab. This sets the exact value, and can also lower it.
- From your release script: POST /v1/products/:slug/releases with a live secret key. It only moves the current version up.
- By selling: a live sale that names a newer version moves it up. Test sales do not.
The SDK cannot set the current version, because it runs on your customers' machines, where anyone could change what it sends.
Change a key's versions
- When you issue it: Set versions for this key on the Issue license form.
- Later: open the key and, in the Signed lease card on its Overview tab, choose Change beside Versions (for instance, to take a 1.x key to 2.x and later).
- From your server: POST /v1/licenses/:key/versions.
The app picks up the change at its next check-in.
Sell an upgrade
To sell an upgrade for a key the customer already holds:
- Turn on Sell as an upgrade on a perpetual license model. The switch appears only while a Stripe or Paddle account is linked.
- Give it the versions it sells, such as 2.x and later.
- Pick its Stripe or Paddle price.
-
On Version sold or Sold and later, also put
versionin the Payment Link's metadata. Without it, the checkout creates a new key, whose version is set by its first activation.
A paid checkout for the model then adds those versions to the customer's existing key instead of creating
a second one. Open the Payment Link from the app with upgradeUrl, so the checkout names the
key:
import { shell } from "electron";
const link = "https://buy.stripe.com/your-upgrade-link";const url = await license.upgradeUrl(link);if (url) shell.openExternal(url); How KeyGrant finds the key
Every sale that changes a key the customer holds (a version, edition, device or uses upgrade, and a timed model bought again) finds that key in this order:
- The key the checkout names.
upgradeUrlsets the checkout'sclient_reference_idto this device's activation ID, so the license key never appears in the URL. If you build your own checkout links,client_reference_idcan also be the license key itself. KeyGrant uses that key when it qualifies. - The one key at the buyer's email. When the ID is missing, or names no key that qualifies, KeyGrant looks for a key under the buyer's email address and uses it only when exactly one key qualifies.
What qualifies depends on the sale. Every key must be of the product, in the checkout's mode (live or test):
| Sale | A key that qualifies |
|---|---|
| Version upgrade | A perpetual key, currently valid, with its version already set. |
| Edition upgrade | A perpetual or timed key, currently valid, holding the set the upgrade replaces (see Sell an edition upgrade). |
| Device upgrade | A perpetual or timed key, currently valid, that the devices would not take past 1,000 (see Sell more devices). |
| Top-up of uses | A perpetual, timed or subscription key, currently valid, that has every meter the sale adds to (see Sell more uses). |
| Timed model | Bought again: a key of the model bought. A named key that cannot take time holds the sale (see Buying again extends the key). |
KeyGrant never guesses. When no key qualifies, a version upgrade creates a new key, a timed model's purchase is a first one and creates a key, and the other sales are held. When more than one qualifies at the email, a version upgrade creates a new key and the other sales are held (see Held sales).
When a version upgrade creates a new key
- No key qualifies, or the key already covers everything the upgrade sells.
-
The model is on Version sold or Sold and later and the Payment Link has
no
versionin its metadata.
When a version sale is held
One sale upgrades one key, and that key needs a seat for each unit the sale bought. A version upgrade is held instead when:
- The buyer's email address holds keys of a sale of several keys of that product, and the checkout named no key that can take it. KeyGrant never picks one of them, and each of those keys' history says so.
- A checkout of several at once names or finds a key with fewer seats.
- It found a key, but KeyGrant cannot tell its line in the checkout: it was bought with other items, or Stripe would not give the checkout's lines.
No key is upgraded and none is created. A held sale waits for you to attach it to a key or refund it: see Held sales.
- Attach it to the key it was for: the key takes the versions it sold, with its whole window of upgrades counted from the day you attach it.
- A held sale can go only to a key with enough seats. For a team that bought one key per person, refund the sale and have each person buy the upgrade for their own key.
Refunds
A full refund (while Refunds revoke keys is on in Settings > Payments) or a lost dispute takes the added versions back, unless the key's versions have changed since the upgrade (see Refunds and disputes).