Seat policies
Every offline product has a seat policy, set by the merchant per product in the Waveform admin: lease (the default,
and every existing product) or deactivate. It governs how a seat is freed, not how it is granted. The token format,
the verifier, and every endpoint are identical under both. Only the number signed into the token’s exp and the set of
budgeted release paths differ.
lease (default) |
deactivate |
|
|---|---|---|
Token lease (exp) |
30 days by default, renewed on every check-in | The product’s longLeaseDays: 90 to 3650, default 365, renewed on every check-in |
| How a seat is freed | The lease lapses, or the user or support releases it | The user deactivates: in the app on that machine (free, unlimited), or remotely (budgeted, default 2 per rolling 365 days; 409 support-limit once spent) |
| When revocation reaches an offline machine | Within lease plus grace (up to 44 days on defaults) | Only when the machine reconnects; otherwise not until longLeaseDays plus grace after its last check-in |
| Your client | The code in Offline integration, as written | The same code with the offline deadline disabled |
What the client does differently under deactivate
Section titled “What the client does differently under deactivate”Nothing else changes.
- Do not impose your own offline deadline. The server’s long lease governs how long a machine can run without contact.
Pass
maxOfflineDays: 0toevaluateOffline(zero disables the deadline; any positive value is applied) and keep everything else, including signature, product and machine binding, and the sealed clock, exactly as before. With the JUCE kit, callcfg.useDeactivateSeatPolicy()after your otherConfigfields: it disables the kit’s own deadline, keeps the online re-check on, and switches the Manage devices copy. - Keep acting on definitive verdicts. A heartbeat answered
revoked,expired,not-activated, orblockedstill clears the stored token immediately. Underdeactivatethat reconnect is the only way a revocation reaches a machine, so do not skip heartbeats because the lease is long. - Give the user a way to deactivate from inside the app.
POST /v1/deactivate { key, fingerprint }releases this machine (free).{ key, deviceId }releases another (budgeted underdeactivate). Confirm before either, wipe the local token and key after a successful self-release, and on409 support-limitshow the server’sused,limit, andwindowDaysrather than a bare error code.
Each half is safe alone
Section titled “Each half is safe alone”A client built for deactivate that receives a 30-day token behaves exactly as under lease: the token’s own exp binds
first. A client built with the default deadline that receives a long token still asks for a check-in when its deadline
passes, and locks only if it cannot get one. The client can only be stricter than the server, never looser, so the server
never needs to know how a build was configured.
Heartbeat cadence is not an offline lever
Section titled “Heartbeat cadence is not an offline lever”The token’s hb claim (the product’s heartbeat interval) governs how often the client attempts a check-in, and attempts
are how a revocation eventually lands. Raising it slows revocation without adding a single day of offline tolerance. The
lease is the only offline dial. Under deactivate, leave the cadence alone.
What the merchant accepts
Section titled “What the merchant accepts”The bounded loss this model accepts: up to maxDevices + 2 + extraActivations machines per key, each for at most
longLeaseDays plus grace from its last check-in. The distinct-machine budget (the number of different machines a key may
ever activate in a rolling window) is enforced either way and answers 409 activation-limit; freeing a seat does not clear it.
Related endpoints and errors
Section titled “Related endpoints and errors”POST /v1/deactivateand409 support-limitPOST /v1/activateand409 activation-limit- Errors