Browse the library
Documentation libraryLicensing
Creating and managing licenses
A license is the key your customer types into your software to unlock it. This guide covers the whole life of a key: creating keys in batches, reading their status, finding them again, inspecting one in detail, and handling the everyday cases such as extending, suspending, revoking or freeing a machine.
9 min read
Before you start
- Everything happens in Licensing & releases › Licenses for each app.
- Every workspace role (Owner, Admin, Member) can create and manage licenses. If the app has Team MFA Enforcement turned on, turn on 2FA or add a passkey first in Account & organization › Account security.
- Each key is made from a license plan (policy). The plan sets the default term, the number of machines, entitlements, grace days and the move rules. You define plans in Licensing & releases › Sign-in & license plans; see License plans and entitlements.
Creating keys in batches
- Open Licensing & releases › Licenses and click Create keys.
- Fill in the Create license keys panel (fields below).
- Click Generate N keys.
- Copy or download the keys, then click I've saved them.
| Field | What it does |
|---|---|
| How many | From 1 up to your plan's "Keys per batch". The input stops at that limit. |
| Plan | The license plan for the whole batch. Only shown when the app has two or more plans; it starts on the default plan. |
| Duration | 1 day, 7 days, 30 days, 90 days, 1 year or Lifetime. Leave it alone to use the plan's term. The term is counted from first activation, not from creation. |
| Devices per key | Empty means the plan's value (shown as the placeholder). Capped at your plan's "Devices per key". |
| Prefix | 1 to 12 uppercase letters or digits, for example PRO. Other characters are dropped as you type. |
| Note | Optional, up to 500 characters, applied to every key in the batch. A customer name or order number makes keys easy to find later. |
A key is 25 characters in five groups, drawn from a 32-symbol alphabet without 0, O, 1 and I, so customers cannot mistype look-alikes. With a prefix it reads PRO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX. Key checks ignore letter case and stray spaces.
Keys are shown once
From then on the table shows a preview such as PRO-7KQ2M-…-X4WZB: enough to recognise a key, useless for guessing it.
The buyer's email
The dashboard's create panel has no email field. You attach an email to a single license from its detail panel (see below), or pass email when you create keys through the Seller API. The Seller API takes the same fields as the dashboard (count, policyId, durationSeconds, maxDevices, prefix, note) plus email and metadata:
curl -X POST https://api.example.com/seller/v1/apps/app_…/licenses \
-H "Authorization: Bearer $CLAVIS_API_KEY" \
-H "content-type: application/json" \
-d '{"count": 1, "durationSeconds": 2592000, "note": "Order 1042", "email": "buyer@example.com"}'
The answer is { "data": { "keys": [...], "ids": [...], "warnings": [] } }. See Selling with the Seller API and the Seller API reference.
Daily key limits per plan
| Limit | Basic | Premium |
|---|---|---|
| Keys per batch | 50 | 1,000 |
| Keys per day | 100 | 10,000 |
| Devices per key | 3 | 100 |
| Soft limits | No | Yes |
These are the defaults. The figures that apply to your workspace are always in Account & organization › Plan & usage, and the "Keys today" meter at the bottom of the sidebar shows how many you have used today.
- The daily limit is shared by the whole workspace: every app and every way of creating keys (dashboard, Seller API), counted per UTC day.
- On Basic, a batch that would go over the daily limit is refused with
plan_limit. A batch that fails gives its quota back. - On Premium (soft limits), the batch is still created and the API adds the warning
daily_key_limit_exceeded.
More in Basic, Premium and limits and on the pricing page.
Test keys (TEST)
To try your software before you sell it, turn on Test keys (TEST) in the create panel:
- They are outside the daily key limit; up to 10 at a time.
- They end exactly 24 hours after they are made, activated or not, with no grace days.
- They carry a TEST label in the license list; resellers cannot make them.
- Once you turn on App settings › Publicly released, no new TEST keys can be made (
app_released) and those already made stop working: the software getstest_key_disabled(see Error codes).
Through the Seller API, send "test": true; durationSeconds and expiresAt are then ignored.
License statuses
| Status | Meaning | Your customer's software |
|---|---|---|
| Unused | Created, never activated. The term has not started. | Can activate it |
| Active | Activated. It expires at activation time plus the term. | Works |
| Expiring | A dashboard filter: active keys with 7 days or less left. | Works, state is expiring within the plan's notice window |
| Grace | Past the expiry date but still inside the plan's grace days. | Still works, state is grace |
| Expired | Past the expiry date. | Refused (license_expired) once grace is over |
| Suspended | Paused by you, can be reinstated. | Refused (license_suspended) |
| Revoked | Stopped for good. | Refused (license_revoked) |
The effective status
The database stores five statuses: unused, active, expired, suspended and revoked. A background job runs every hour and:
- Sends exactly one
license.expiringnotice per term, when the key enters the plan's "Notice before the end (days)" window (7 by default). If the license has an email, the buyer gets a reminder, with a link to the customer portal when the portal is on. - Marks the key expired once the expiry date plus "Grace after the end (days)" has passed, and emits
license.expired.
The dashboard does not wait for that job: an active key past its expiry date is shown as Expired right away. That is the effective status. The software side also works out expiry plus grace on every request, independently of the job.
How the software sees it
import { ClavisClient, ClavisError } from "@clavisauh/sdk";
const clavis = new ClavisClient({ baseUrl: "https://api.example.com", appId: "app_…", appPublicKey, device });
try {
const grant = await clavis.activate(key);
if (grant.license.state === "expiring") showNotice(`Your license ends on ${grant.license.expiresAt}`);
if (grant.license.state === "grace") showNotice("Your license has ended; you are in the grace period");
} catch (err) {
if (err instanceof ClavisError) showError(err.code); // device_limit, license_expired, invalid_credentials…
}
With the CLV1.1 protocol (the JS SDK's default), a machine that holds no seat on the key gets the same invalid_credentials code for every unusable key. The specific reason, such as license_expired or license_suspended, is only returned to a machine already bound to that key.
Filters and search
- Filter buttons: All, Unused, Active, Expiring, Expired, Suspended, Revoked. Expiring is always the next 7 days, whatever the plan's notice window.
- The search box matches four things: a full key (compared through its hash, the only way to find a key by its real value), part of a note (case-insensitive), part of a preview, such as the last group, and a buyer mark such as
0123456789.abcdeffound in a leaked file (Forensic Watermarking). - Filters and the search term live in the URL (
?status=expiring&q=...), so you can bookmark a view or send it to a teammate. - The list shows newest first, 50 rows at a time; click Load older for more.
The license detail panel
Click a row, or open the ⋯ menu and choose View details. The header shows the preview, the status, the ID and a risk meter when Protect 24/7 has scored the key. Below it:
- Activated, Expires ("On first use" before activation, "Lifetime" with no end), Devices (machines holding a seat over the maximum).
- Buyer: the license email.
- Features: the feature flags the plan grants, shown only when there are some.
- Device transfers: the plan's move rule, for example "The buyer used 1/2 in 30 days, 24 h apart".
- Devices: each machine with its platform, app version, last IP and last seen time, or a label: Removed, Blocked, Asleep, seat released. A chip icon means the device key lives in secure hardware.
- Linked users, Move history (the latest 20) and Recent SDK calls (the latest 10 events).
On each machine, the X button (Free this seat) removes it and logs "Removed by the seller" in the history; it never uses up the buyer's moves. A blocked machine has an Unblock button. When a new machine was refused because the key is full, the panel shows "Machine … was refused, compared with this one" with the hardware components that match or not; Let that machine in frees the old machine's seat so the new one can activate on its next try. See Devices, HWID and transfers.
Actions
Open the ⋯ menu at the end of a row:
| Action | What it does |
|---|---|
| Extend | Adds +7 days, +30 days, +90 days or +1 year. |
| Suspend | Only for Unused or Active keys. Every running session ends at once. The success message has an Undo button. |
| Reinstate | Back to Active if the key was ever activated, otherwise back to Unused. |
| Reset devices | Removes every machine and ends every session; status and expiry stay. Not written to the move history. |
| Revoke | Stops the key for good and ends every session. A machine that stays offline can keep running until its offline token ends. Cannot be reinstated. Asks for confirmation. |
| Delete | The key stops working now and leaves the list; the record stays in the audit log. Asks for confirmation. |
How Extend behaves:
- Unused key with a term: the time is added to the term; the clock still starts at activation.
- Key with an expiry date: the time is added to that date. If it has already passed (grace included), the time counts from now. An Expired key goes back to Active.
- Lifetime keys do not change. A suspended key gets the extra time but stays suspended; its term keeps running while suspended.
- The expiring notice is reset, so the buyer is reminded again near the end of the new term.
To add or change the email, click the Buyer row in the detail panel ("Add an email to open the customer portal" when there is none), type the address and click Save. Save an empty field to remove it. The email opens the customer portal and receives expiry reminders. In the dashboard a note can only be set at creation; to change it later, use the Seller API note action.
Bulk actions
Tick the boxes at the start of each row (the header box selects every row on screen). The N selected bar offers Suspend, Reinstate, Extend, Reset devices and Revoke. Delete is only in each row's menu. One call handles up to 1,000 licenses; keys in the wrong state (reinstating a key that is not suspended, say) are skipped.