Custom Attributes
Custom attributes let you attach your own data to products, licenses, users, organizations, resellers, activations, and trial activations. You define each attribute once, with its name, whether it is required, a default value, and who can see or change it. Cryptlex then validates every value against that definition, lets you filter lists by it, shows it in the portals you allow, and makes it readable in your app.
Typical uses are storing an order or CRM reference on a license, a region or tier on an organization, recording that a customer accepted your EULA, or collecting details about the machine an activation runs on.
Defining a custom attribute
Create custom attributes in the admin portal under Settings -> Custom Attributes, or through the Web API.
Custom attribute properties
| Property | Description |
|---|---|
| Name | The key you use to set, read, and filter the attribute in the Web API and in your app. It must be unique per resource type and cannot be changed after creation. |
| Display Name | The label shown to users in the customer and reseller portals. Unlike the name, you can change it at any time. |
| Resource Type | The resource the attribute belongs to: product, license, user, organization, reseller, activation, or trial-activation. It cannot be changed after creation. |
| Data Type | The type of the value. Currently only string is supported. |
| Products | For license, activation, and trial activation attributes, the products whose licenses or activations carry the attribute. See Linking products. |
| Required | Whether a value must be provided when the resource is created. See Required attributes and default values. |
| Default Value | The value stored when none is provided. |
| Application Access, User Access, Organization Access, Reseller Access | Who can read or change the value. See Access levels. |
| Archived | Retires the attribute without deleting its values. See Archiving and deleting custom attributes. |
Linking products
License, activation, and trial activation attributes apply only to the licenses and activations of the products you link to them. An attribute with no linked products applies to nothing, so a required attribute added for one product never affects the licenses of your other products.
You can link more products at any time. Unlinking a product is not allowed once that product has any licenses or trial activations, since those resources already carry values for the attribute.
Required attributes and default values
Required attributes and default values are applied when the owning resource is created:
- If a required attribute has no default value and the create request does not provide a value, the request fails.
- Every applicable attribute the request does not provide is stored with its default value, or an empty string if it has none. This way every resource carries its full set of attributes.
Updating a resource never fails because of a required attribute.
When you create an attribute, link more products to it, or unarchive it, Cryptlex also adds the default value to existing products, licenses, users, organizations, and resellers that don't have a value yet. Existing activations and trial activations receive the default value on their next server sync. Changing the default value later does not change values that are already stored.
Access levels
Each attribute has an access level for each audience, set once on the definition:
none: the audience cannot see the attribute.read: the audience can see the value.read-write: the audience can see and change the value.
| Access | Audience |
|---|---|
| Application Access | Your app, through LexActivator or the activation endpoints of the Web API. With read access the value is included in the signed activation response; with read-write access your app can also set it. |
| User Access | End users in the customer portal. |
| Organization Access | Organization admins in the customer portal. |
| Reseller Access | Reseller admins in the reseller portal. |
Admins in the admin portal, and Web API calls made with an access token that has the scopes for the owning resource, always have full access.
Not every level is allowed for every resource type. For example, product data is shared by all your customers, so only your app can read it, and activation attributes are always written by your app:
| Resource type | Application | User | Organization | Reseller |
|---|---|---|---|---|
| License | Up to read-write | Up to read-write | Up to read-write | Up to read-write |
| Product | Up to read | none | none | none |
| User | Up to read | Up to read-write | Up to read-write | Up to read-write |
| Organization | Up to read | none | Up to read-write | Up to read-write |
| Reseller | none | none | none | Up to read-write |
| Activation | Always read-write | Up to read | Up to read | Up to read |
| Trial activation | Always read-write | none | none | none |
Setting values
From the admin portal or the Web API
Set values on the resource itself when you create or edit a product, license, user, organization, or reseller in the admin portal, or through its customAttributes property in the Web API. Each name must match a custom attribute defined for that resource type (and, for licenses, linked to the license's product). Setting a value overwrites the previous one; to clear a value, set it to an empty string. Values of archived attributes cannot be changed.
From your app
Your app can set values on its license, activation, or trial activation, as long as the attribute grants read-write application access. Activation and trial activation attributes always do; license attributes do only if you allow it, which is useful for things like recording EULA acceptance against the license.
With LexActivator, call SetCustomAttribute() with the resource type, name, and value before activating, or at any time after. LexActivator sends the values with the next activation or server sync request:
SetCustomAttribute(LA_RESOURCE_TYPE_ACTIVATION, "device_env", "production");
SetCustomAttribute(LA_RESOURCE_TYPE_LICENSE, "eula_accepted", "true");
ActivateLicense();The resource type must be one of LA_RESOURCE_TYPE_LICENSE, LA_RESOURCE_TYPE_ACTIVATION, or LA_RESOURCE_TYPE_TRIAL_ACTIVATION. Setting a license attribute that does not grant your app read-write access fails with LA_E_CUSTOM_ATTRIBUTE_ACCESS_DENIED, either immediately or, if LexActivator does not yet know the attribute's access level, on the next server sync.
If you license via the Web API directly, send the values in the customAttributes array of the POST /v3/activations or PATCH /v3/activations/:id request, with a resourceType of license or activation for each entry:
"customAttributes": [
{ "name": "eula_accepted", "resourceType": "license", "value": "true" },
{ "name": "device_env", "resourceType": "activation", "value": "production" }
]Trial activation requests take plain name and value pairs, since there is no license to route the value to.
A few rules apply to values your app sets:
- Required activation and trial activation attributes must be provided when the activation is created, unless they have a default value; otherwise, the activation fails. Required license attributes are not enforced during activation, so adding one never breaks your deployed installations.
- If any value in the request is invalid, or your app does not have
read-writeaccess to it, the whole activation request fails. - When a license attribute value actually changes, Cryptlex fires the
license.updatedwebhook. Resending an unchanged value on every sync does not.
Reading values in your app
Values of attributes that grant your app read or read-write access are included in the signed activation response, and are refreshed on every server sync. Archived attributes are never included.
With LexActivator, read a single value with GetCustomAttribute(), or all values of a resource type with GetCustomAttributes(). Pass one of the following resource types:
| Resource type | Returns attributes of |
|---|---|
LA_RESOURCE_TYPE_PRODUCT | The product. Available for both license and trial activations. |
LA_RESOURCE_TYPE_LICENSE | The activated license. |
LA_RESOURCE_TYPE_USER | The user the license is assigned to. |
LA_RESOURCE_TYPE_ACTIVATION | The current activation. |
LA_RESOURCE_TYPE_TRIAL_ACTIVATION | The current trial activation. |
char region[1025];
if (GetCustomAttribute(LA_RESOURCE_TYPE_LICENSE, "region", region, 1025) == LA_OK)
{
printf("Region: %s", region);
}An attribute your app cannot read returns LA_E_CUSTOM_ATTRIBUTE_NOT_FOUND, as if it did not exist. A value your app set with SetCustomAttribute() is returned only after the server has accepted it on the next activation or server sync.
Archiving and deleting custom attributes
Once any resource of its type exists, a custom attribute holds stored values and cannot be deleted. Archive it instead. An archived attribute:
- Keeps its existing values, visible only to admins in the admin portal and the Web API.
- Is hidden from the customer and reseller portals and from your app.
- Rejects new values, and is no longer required or given a default on new resources.
- Cannot be edited until you unarchive it.
Unarchiving an attribute makes it active again and adds its default value to existing resources that don't have one yet.
License metadata for integrations
Besides custom attributes, each license can hold up to five metadata fields. They are meant for integrations that need to store their own reference on a license, such as the subscription ID from your payment gateway. The Stripe, Paddle, and FastSpring integrations use them this way.
License metadata:
- Can only be set through the Web API, using the
metadataproperty when you create or update a license. - Cannot be used to filter licenses.
- Is available on licenses only.
For any other data you want to store on a license, use a custom attribute.
Limits
You can define up to 20 custom attributes per resource type. Enterprise plans can have a custom limit. Each license can also hold up to five metadata fields.