Licence keys for a desktop or command-line app
A paid desktop or command-line app needs three things from its licensing: to know that a key is genuine, to count the machines it runs on, and to keep working when the network does not. This page puts the pieces together for an app you ship; each step links to the page with the details.
What the app stores
Section titled “What the app stores”Keep these together, in the app’s own settings or data directory, per user:
| Item | Where it comes from | Why |
|---|---|---|
| The licence key | The customer enters or pastes it | It authenticates every call and carries the licence |
| The machine id | A random UUID the app generates once per install | Sealcord counts a seat per machine id |
| The latest verdict | The token of the last activation or status answer |
Lets the app run offline until the verdict’s end |
| The public key | Built into the app, from the product’s page in the Console | Checks the key and the verdict with no network |
Never store or send a hardware serial or anything personal as the machine id. No admin API key ships in the app: the licence key is enough for every call the app makes.
From key entry to a running app
Section titled “From key entry to a running app”- The customer enters a key. Check it offline first: its signature, its product and its expiry. A key that fails is refused at once, with no call.
- Activate this machine. Call
POST /v1/activationswith the key, the machine id, a label the customer will recognise (the computer’s name), the system and the app’s version. Check the answer’stokenas a verdict and store it. - On later launches, check the stored verdict offline. While it is valid, the app runs with no call.
- Refresh it at most once a day, or when it has expired: call
POST /v1/activations/statusand store the new token. - When the customer signs out or moves to another computer, call
POST /v1/activations/deactivateto free the seat.
A network error, a timeout or a 5xx never means the licence is gone. Keep the last good result and
try again later.
What the app shows
Section titled “What the app shows”| What the API answers | What the customer sees |
|---|---|
An active verdict |
Nothing: the app runs |
revoked with reason not_activated |
Nothing: the app activates once and carries on |
revoked with reason revoked, or licence_revoked |
The licence no longer holds, and how to reach you |
revoked with reason expired, or licence_expired |
The licence has ended, and where to renew |
seat_limit_reached |
Every seat is in use (details.limit), and how to free one |
licence_key_invalid or licence_wrong_product |
The key is not for this app; check it was copied whole |
No answer, 429 or 5xx |
Nothing: the app keeps its last good result and retries later |
Unless you turn it off, customers can free their own machines in the Console under My licences, so the seat message can point there, next to the app’s own “Sign out” button (Free a seat).
Command-line apps
Section titled “Command-line apps”The same calls work from a terminal tool. A few things differ:
- Entering the key. Offer a command such as
acme-notes activate ACN1-…, and read the key from an environment variable as well, so scripts and build machines can set it without a prompt. - The label. Use the host name, so the customer can tell machines apart when freeing a seat.
- Short-lived machines. A container or a build runner that starts fresh each time makes a new
machine id, and so takes a new seat on each run. Give such machines a stable id from their
configuration, or issue that customer a licence limited by
machineswith room for them (seats or machines), and deactivate when a machine is retired. - Exit codes. Keep running on a network error, as a desktop app does; fail with a clear message only when the API answers that the licence no longer holds.
Selling it
Section titled “Selling it”Issue one licence per paid order from your checkout’s backend with the Admin API (Issue from your backend), or connect your own Polar or Stripe account and let its webhooks issue, revoke and expire licences for you.
Questions
Section titled “Questions”Can the app run before the customer buys?
Section titled “Can the app run before the customer buys?”Yes. Issue a trial: an active licence with an end date, which your app treats like any other (Trials).
What happens if Sealcord is unreachable on launch day?
Section titled “What happens if Sealcord is unreachable on launch day?”The offline check of the key needs no network, and a stored verdict keeps the app running until its end. For a new customer whose first activation gets no answer, the app decides: running on the offline check while it retries is the safe default (Apps that run offline).
Does every launch call the API?
Section titled “Does every launch call the API?”No. The app checks the stored verdict offline on each launch, and asks the API at most once a day.