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_projectsfor 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
| Scope | Applies to | You provide |
|---|---|---|
| All users | Every session of the app | Nothing |
| One license | Every session running on that license | A license ID (lic_…) |
| One user | Only sessions signed in to that account | An 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:
| Scope | Value |
|---|---|
| All users | 3 |
| 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
endUserIdfield of theuser.createdwebhook payload, or from the Seller API's user list.
Adding a variable in the dashboard
- Open the app and go to Integration › Variables.
- Click Add variable.
- Fill in Key: 1 to 64 characters, starting with a letter or an underscore, then only ASCII letters, digits,
_,.and-. Keys are case-sensitive. - Fill in Value: up to 16,384 characters. The dashboard does not accept an empty value.
- Pick a Scope. For anything other than All users, a License or user ID field appears and is required.
- Turn on Secret if the value should be encrypted (see below).
- 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 code | When |
|---|---|
session_required | The software has not signed in |
session_expired | The session ran out or was ended |
session_device_mismatch | The session ID was sent from another machine |
device_revoked, device_blocked | The machine was removed from the license or blocked |
user_banned | The account is banned |
license_revoked, license_suspended, license_expired | The 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, getsapp_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;
}
}
variablesis an object of names to strings, already merged by precedence. A missing name isundefined, 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 asnull.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.