Activation and validation
The whole integration is a single call, validate_license / ValidateLicenseAsync,
which behaves differently depending on what you hand it. This page covers when to
call it and what to do with the result.
Day 0: activation
Call validate with no stored license. The SDK registers this device with the
service; this first call must be online. The service returns a license, which you
retrieve with get_updated_license() / result.UpdatedLicense. Persist it
atomically, and consider keeping a backup copy: a lost license file costs your
customer a re-activation, which holds a seat until the old one is deactivated.
A few first-activation failures deserve distinct handling in your UI: all seats
already in use on a standard license (point the customer at deactivating the old
machine), SEAT_LIMIT on a floating license
(the error carries a retry hint), PAYMENT_REQUIRED on a lapsed
subscription, and plain network failure, where the
fix is to retry later; nothing else is wrong.
Every start: local validation
Load the stored license, set it on the validation parameters, and call validate.
While the license is current this touches no network and returns VALID in
milliseconds. Two rules keep the rest of the lifecycle automatic:
- Always save the updated license when the SDK issues one. That is how renewals reach disk.
- Keep validating even after a failure. Renewal is self-healing: the moment the customer pays, or connectivity returns, the next validation call fixes it.
Renewal is silent
When a license lapses, the next validation call contacts the service once and returns a license reflecting whatever the customer has paid for by then. A subscription renewal needs no code on your part (subscriptions guide).
Clock skew and manipulation
Validation relies on the local clock. Winding the clock back buys a limited amount of
time at most, because periodic renewal bounds the damage automatically, so you do not
need your own clock heuristics unless your threat model calls for them. Setting the clock
forward and then correcting it can produce NOT_YET_VALID; when that happens, tell
the customer to fix their clock.
Verify the product
If you sell more than one product, declare which one this application is before
validating: client->set_product_id("PRO_EDITION") (.NET
client.SetProductId("PRO_EDITION")), using the product's Reference Id from the
console's Products page. Two things then hold without further code: a key sold for a
different product is refused at activation, before it consumes a seat, and a stored
license for a different product fails validation with BAD_PRODUCT. Skip the call
and any license of your account validates in any of your apps — fine while you sell
one product, not once you sell two. The full contract, including reading the
license's product and metadata back out, is the
product binding guide.
Statuses that deserve their own handling
A few statuses deserve handling distinct from a generic error:
EXPIRED: apply your grace policy, and keep validating.PAYMENT_REQUIRED: your dunning moment. Show the subscription state and a link to renewal, not a generic error.BAD_CONTEXT: the license is bound to a different machine. Offer the move flow.BAD_PRODUCT: the key belongs to a different product of yours. Tell the customer which product it matches (it is on their receipt) and where to get the right one.REVOKED: terminal. Direct the customer to support.BAD_SIGNATURE: a damaged license file. Delete it and re-activate.
The full table with recommended actions is the statuses reference.
Threading
Create one licensing client per thread, or serialize access to a shared one. The client object is not thread-safe.