The CLV1 protocol is now publicRead the spec
Browse the library

Documentation libraryIntegration

App variables

Variables are name and value pairs your software receives after a successful sign-in. Use them to ship configuration, feature flags, announcements or per-customer settings without releasing a new build.

6 min read

What variables are for

Common uses:

  • A message shown at startup, such as a maintenance window or a promotion.
  • Settings you may want to change after release: the address of your own server (https://files.example.com), retry counts, warning thresholds.
  • A limit for one customer, such as a higher max_projects for an enterprise license.
  • Valuable data you only want to hand to a valid session instead of baking it into the binary.

Variables belong to one app. Values are always strings; store structured data as JSON and parse it in your software.

Three scopes

ScopeApplies toYou provide
All usersEvery session of the appNothing
One licenseEvery session running on that licenseA license ID (lic_…)
One userOnly sessions signed in to that accountAn account ID (usr_…)

A session has an account when it was created by register(), login() or redeem(). Sessions created by activate(key) or trial() carry no account, so they never receive One user variables.

Precedence

When the same name exists in several scopes, the server merges them into one map and the most specific value wins: user, then license, then global. Take three variables all named max_projects:

ScopeValue
All users3
One license (license A)10
One user (account B)50
  • A machine activated with license A's key, no account: gets 10.
  • Account B signed in, whatever license it uses: gets 50.
  • Every other session: gets 3.

Finding license and user IDs

  • License ID: in Licensing & releases › Licenses, open the ⋯ menu at the end of the row and choose Copy ID, or choose View details and read the ID under the title. Your software also has it as grant.license.id.
  • User ID: the End users › Users table does not show IDs. Take it from the endUserId field of the user.created webhook payload, or from the Seller API's user list.

Adding a variable in the dashboard

  1. Open the app and go to Integration › Variables.
  2. Click Add variable.
  3. Fill in Key: 1 to 64 characters, starting with a letter or an underscore, then only ASCII letters, digits, _, . and -. Keys are case-sensitive.
  4. Fill in Value: up to 16,384 characters. The dashboard does not accept an empty value.
  5. Pick a Scope. For anything other than All users, a License or user ID field appears and is required.
  6. Turn on Secret if the value should be encrypted (see below).
  7. Click Save. Variable saved confirms it.

The table lists variables by scope, then by key, with the columns Key, Value (secret ones show Hidden), Scope (with the first 12 characters of the ID) and Created, which in fact shows the last save.

Editing and deleting

Click a row to edit it. The Key is locked; to rename, delete the variable and add a new one. Each combination of scope, ID and key holds exactly one variable, so saving the same combination again overwrites the value.

To delete, click the trash icon (Delete) at the end of the row. The variable is gone at once, with no confirmation and no undo, and Variable deleted appears.

Every workspace member can view variables, but only an Admin or the Owner can add, edit or delete them. Each save and delete is written to the audit log with the key, scope and secret flag, never the value.

Secret variables

With Secret on, the value is encrypted with AES-256-GCM before it is stored and bound to its own app, scope, ID and key, so ciphertext copied into another row will not decrypt. After saving:

  • The dashboard only shows Hidden and the Seller API returns value: null. There is no way to read the value back.
  • The edit dialog opens with an empty Value field. To save any change, you must type the whole value again. Turn Secret off while re-entering it and the variable becomes a plain one.

Only valid sessions get variables

The server hands out variables in exactly two places: in the signed answer of a successful sign-in (activate, trial, register, login, redeem), and through variables(). A variables() call needs a live session and runs the same checks as a heartbeat:

Error codeWhen
session_requiredThe software has not signed in
session_expiredThe session ran out or was ended
session_device_mismatchThe session ID was sent from another machine
device_revoked, device_blockedThe machine was removed from the license or blocked
user_bannedThe account is banned
license_revoked, license_suspended, license_expiredThe license can no longer be used (after the plan's grace period)

The server applies this rule to every app, with nothing to turn on. It is part of Server-Side Enforcement in Standard Protection. See The two protection modes.

What follows from this:

  • Variables are not part of the offline token. While your software runs offline, it gets no fresh variables.
  • During maintenance, open sessions still read variables; only new sign-ins are refused. When the app is paused, every request, variables() included, gets app_paused.

Reading variables in your software

With the JavaScript SDK, variables arrive with the sign-in result, and variables() fetches the latest values without signing in again:

import { ClavisClient, ClavisError } from "@clavisauh/sdk";

const grant = await clavis.activate(key);
const notice = grant.variables["notice"]; // undefined when not set
const limits = JSON.parse(grant.variables["limits"] ?? "{}");

// Later, when you need the latest values
try {
  const variables = await clavis.variables();
  applyConfig(variables);
} catch (err) {
  if (err instanceof ClavisError && err.code === "session_expired") {
    await signInAgain();
  } else {
    throw err;
  }
}
  • variables is an object of names to strings, already merged by precedence. A missing name is undefined, so always keep a default.
  • variables() resolves to { variables }, not to the map itself.
  • Heartbeats do not return variables. A change in the dashboard reaches your software at the next sign-in or the next variables() call; nothing is pushed.
  • The answer is signed like every other answer, so the SDK refuses values altered in transit.
  • Do not write secret values to disk or to your software's logs.

Managing variables with the Seller API

To automate, for example to set a per-license limit the moment a customer pays, use the Seller API with a key that has variables:read or variables:write:

  • GET /apps/{appId}/variables: lists variables; secret values come back as null.
  • PUT /apps/{appId}/variables: creates or overwrites by scope, ID and key.
  • DELETE /variables/{id}: deletes one variable.
{ "key": "max_projects", "value": "10", "scope": "license", "scopeRef": "lic_…", "isSecret": false }

Rules for keys, lengths and scopeRef match the dashboard. Details are in the Seller API reference.

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