The CLV1 protocol is now publicRead the spec
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

  1. Open Licensing & releases › Licenses and click Create keys.
  2. Fill in the Create license keys panel (fields below).
  3. Click Generate N keys.
  4. Copy or download the keys, then click I've saved them.
FieldWhat it does
How manyFrom 1 up to your plan's "Keys per batch". The input stops at that limit.
PlanThe license plan for the whole batch. Only shown when the app has two or more plans; it starts on the default plan.
Duration1 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 keyEmpty means the plan's value (shown as the placeholder). Capped at your plan's "Devices per key".
Prefix1 to 12 uppercase letters or digits, for example PRO. Other characters are dropped as you type.
NoteOptional, 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

LimitBasicPremium
Keys per batch501,000
Keys per day10010,000
Devices per key3100
Soft limitsNoYes

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 gets test_key_disabled (see Error codes).

Through the Seller API, send "test": true; durationSeconds and expiresAt are then ignored.

License statuses

StatusMeaningYour customer's software
UnusedCreated, never activated. The term has not started.Can activate it
ActiveActivated. It expires at activation time plus the term.Works
ExpiringA dashboard filter: active keys with 7 days or less left.Works, state is expiring within the plan's notice window
GracePast the expiry date but still inside the plan's grace days.Still works, state is grace
ExpiredPast the expiry date.Refused (license_expired) once grace is over
SuspendedPaused by you, can be reinstated.Refused (license_suspended)
RevokedStopped 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.expiring notice 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.

  • 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.abcdef found 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:

ActionWhat it does
ExtendAdds +7 days, +30 days, +90 days or +1 year.
SuspendOnly for Unused or Active keys. Every running session ends at once. The success message has an Undo button.
ReinstateBack to Active if the key was ever activated, otherwise back to Unused.
Reset devicesRemoves every machine and ends every session; status and expiry stay. Not written to the move history.
RevokeStops 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.
DeleteThe 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.

How can we help?

Frequently asked

What is ClavisAUH?

A licensing and authentication service for desktop software: license keys, device binding, end-user sign-in and signed responses. All run from one dashboard

Why can't a cracker fake your server?

The SDK only trusts answers signed with your app's private key. That key never leaves our servers, so a fake server can't sign and gets rejected

What ends up in my binary?

Your app id and public key. Nothing secret, because each device makes its own key pair on first run

Does it work offline?

Yes. Every plan gets a signed offline token for 7 days, and your app checks it locally. Premium lets you choose how long. A revoked license can keep working offline for that time, unless Online-Only Mode is on

Which languages are supported?

JavaScript and TypeScript today. C++, C# and Python are next, on the same open protocol

Is Basic really free?

Yes. No time limit, no card. Move to Premium when you need more

Get in touch