Documentation · SoftActivate Licensing

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 with get_license_scope() or query one value with get_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 with get_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.