Product binding
Every license belongs to a product: the product's Reference Id is stamped into the license when it activates. Declaring your application's product with one call makes that binding enforceable — a customer cannot run product A on a key they bought for product B — and two read APIs let you inspect the stamped product and the license metadata yourself.
Declare your product
Before validating, tell the client which product this application is, using the product's Reference Id from the console's Products page:
C++
#include "softactivate/sws_licensing.h"
auto client = sws::licensing::client::create(endpointUrl);
client->set_metadata("trusted_domain", trustedDomain);
client->set_vendor_id(vendorId);
client->set_license_key(customerLicenseKey);
// bind this application to YOUR product's Reference Id (console -> Products):
// a key for a different product is refused at activation, and a stored license
// for a different product fails validation with SWS_LICENSE_STATUS_BAD_PRODUCT
client->set_product_id("PRO_EDITION");
auto params = client->create_license_validation_params();
auto result = client->validate_license(params);
if (result->is_license_valid())
{
auto license = result->get_updated_license();
// the product the license is bound to, stamped by the server
const char* product = license->get_license_scope_value("sku_id");
// template metadata authored in the console rides the license too;
// query any value by dot-separated path (integers index arrays)
const char* edition = license->get_license_metadata_value("edition");
}
C#
using SWS.Licensing;
using var client = new LicensingClient(endpointUrl)
{
TrustedDomain = trustedDomain,
VendorId = vendorId,
LicenseKey = customerLicenseKey
};
// bind this application to YOUR product's Reference Id (console -> Products):
// a key for a different product is refused at activation, and a stored license
// for a different product fails validation with LicenseStatus.BadProduct
client.SetProductId("PRO_EDITION");
LicenseValidationResult result = await client.ValidateLicenseAsync(null);
if (result.IsValid && result.UpdatedLicense != null)
{
// the product the license is bound to, stamped by the server
string? product = result.UpdatedLicense.GetScopeValue("sku_id");
// template metadata authored in the console rides the license too;
// query any value by dot-separated path (integers index arrays)
string? edition = result.UpdatedLicense.GetMetadataValue("edition");
}
VB.NET
Imports SWS.Licensing
Using client As New LicensingClient(endpointUrl) With {
.TrustedDomain = trustedDomain,
.VendorId = vendorId,
.LicenseKey = customerLicenseKey
}
' bind this application to YOUR product's Reference Id (console -> Products):
' a key for a different product is refused at activation, and a stored license
' for a different product fails validation with LicenseStatus.BadProduct
client.SetProductId("PRO_EDITION")
Dim result As LicenseValidationResult = Await client.ValidateLicenseAsync(Nothing)
If result.IsValid AndAlso result.UpdatedLicense IsNot Nothing Then
' the product the license is bound to, stamped by the server
Dim product As String = result.UpdatedLicense.GetScopeValue("sku_id")
' template metadata authored in the console rides the license too;
' query any value by dot-separated path (integers index arrays)
Dim edition As String = result.UpdatedLicense.GetMetadataValue("edition")
End If
End Using
With the declaration in place:
- At activation, a key sold for a different product is refused by the service (before it consumes a seat), so the wrong key never even activates.
- At every validation, a stored license for a different product fails with
BAD_PRODUCT— see the statuses reference for the recommended handling.
Skip the call and nothing changes from earlier SDK versions: any license of your account validates in any of your applications. That is fine while you sell one product; declare the moment you sell two. A null or empty product id is rejected — not calling is the only unbound state.
Licenses issued before product binding existed carry no stamped product. They pass the declaration check tolerantly, so shipping the declaration does not strand existing customers; if you want to be stricter, read the stamped value yourself (below).
Read the product and the metadata
A validated license exposes two JSON attributes the issuing service stamped into it:
- The scope — the service-authored product binding:
{"sku_id": "PRO_EDITION"}(or{}when the license is not bound to a product). Read it withget_license_scope()or query one value withget_license_scope_value("sku_id")(.NET:License.Scope,License.GetScopeValue("sku_id")). - The metadata — the free-form JSON you author on the license template in the
console (edition, feature flags, anything up to 4 KB). It is embedded at every
activation and refreshed at every renewal. Read it with
get_license_metadata()or query values withget_license_metadata_value("edition")(.NET:License.Metadata,License.GetMetadataValue("edition")).
Path queries are dot-separated: object members by name, array elements by a
non-negative integer segment ("features.0"). A string value is returned as its
exact content, other values as their JSON text, and any miss answers null — never an
error.
Where the values come from
The Reference Id is fixed when you create the product; orders, licenses, and receipts all carry it, so the id your app declares is the same one your customer sees. The metadata comes from the license template (or the product's embedded license terms) and can be edited later — the next activation or renewal delivers the updated values, while offline-activated licenses keep the metadata current at their issue time.