ExisOne Client SDK

Embed our cross-platform .NET library to generate hardware IDs, activate and validate licenses, and send support tickets.

Package: ExisOne.Client · Version: 0.6.0 · TFMs: net8.0, net9.0

Install

dotnet add package ExisOne.Client --version 0.6.0
📦 Demo Project: See the full working example at github.com/exisllc/ExisOne.Client.Console

Initialize

using ExisOne.Client;

var client = new ExisOneClient(new ExisOneClientOptions {
	BaseUrl = "https://your-api-host", // must be https
	AccessToken = "exo_at_<public>_<secret>", // create in Access Tokens UI
	OfflinePublicKey = null // optional: set for offline license validation
});

// Optional: change base URL later
client.WithBaseUrl("https://another-host");

// Library version
var sdkVersion = client.GetVersion();

BaseUrl is not hard-coded; you may also set environment variable EXISONE_BASEURL.

For offline license validation, set OfflinePublicKey to your tenant's RSA public key (PEM format). Obtain this from the Crypto Keys page.

Capabilities

Quickstart

// 1) Hardware ID (store locally)
var hwid = client.GenerateHardwareId();

// 2) Activation with version (user enters key/email)
var result = await client.ActivateAsync(activationKey, userEmail, hwid, "MyProduct", version: "1.0.0");
if (!result.Success) {
    if (result.ErrorCode == "version_outdated")
        Console.WriteLine($"Please upgrade to version {result.MinimumRequiredVersion}");
    else
        Console.WriteLine(result.ErrorMessage);
}

// 3) Validate on app start with version check (requires 'verify' permission)
var (isValid, status, exp, features, serverVer, minVer) = 
    await client.ValidateAsync(hwid, "MyProduct", activationKey, version: "1.0.0");

if (status == "version_outdated")
    Console.WriteLine($"Update required: minimum version is {minVer}");
else if (serverVer != null && serverVer != "1.0.0")
    Console.WriteLine($"Update available: v{serverVer}");

// 4) Optional: Deactivate from the same hardware
bool deactivated = await client.DeactivateAsync(activationKey, hwid, "MyProduct");

// 5) (Publisher tooling) Generate a new key for a product
var responseJson = await client.GenerateActivationKeyAsync("MyProduct", "user@example.com", planId: 1);

// 6) Submit support ticket from client
await client.SendSupportTicketAsync("MyProduct", userEmail, "Subject", "Message body");

Offline License Validation

For customers without internet access, generate offline activation codes from the License Keys page by providing their Hardware ID. These codes are RSA-signed and validated locally.

Setup

// Configure with your tenant's RSA public key for offline validation
var client = new ExisOneClient(new ExisOneClientOptions {
	BaseUrl = "https://your-api-host",
	OfflinePublicKey = @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----"
});

Smart Validation (Recommended)

Auto-detects online vs offline keys based on format. Falls back to offline validation if server is unreachable.

var hwid = client.GenerateHardwareId();

// Works with both online keys (XXXX-XXXX-XXXX-XXXX) and offline codes
var result = await client.ValidateSmartAsync(licenseKeyOrOfflineCode, hwid, "MyProduct");

if (result.IsValid) {
	Console.WriteLine($"Licensed until: {result.ExpirationDate}");
	Console.WriteLine($"Offline mode: {result.WasOffline}");
	Console.WriteLine($"Features: {string.Join(",", result.Features ?? Array.Empty<int>())}");
} else {
	Console.WriteLine($"Invalid: {result.ErrorMessage}");
}

Direct Offline Validation (No Network)

// Validate completely offline - no server calls
var offlineResult = client.ValidateOffline(offlineCode, hwid);

if (offlineResult.IsValid) {
	Console.WriteLine($"Product: {offlineResult.ProductName}");
	Console.WriteLine($"Expires: {offlineResult.ExpirationDate}");
} else if (offlineResult.IsExpired) {
	Console.WriteLine("License expired");
} else if (offlineResult.HardwareMismatch) {
	Console.WriteLine("Wrong machine - license bound to different hardware");
} else {
	Console.WriteLine($"Invalid: {offlineResult.ErrorMessage}");
}

Deactivation with Opportunistic Sync

// Tries to notify server, but succeeds locally even if offline
var result = await client.DeactivateSmartAsync(keyOrCode, hwid, "MyProduct");
Console.WriteLine($"Success: {result.Success}, Server notified: {result.ServerNotified}");

Version Enforcement New in 0.5.0

Force users to upgrade their software by enabling version enforcement on your product. When enabled, activation and validation will fail if the client version is below the minimum required version.

How It Works

  1. Set Minimum Required Version on your product (e.g., "2.0.0")
  2. Enable Enforce Version Check in the Product Management page
  3. Client apps send their version during activation/validation
  4. If client version < minimum, server returns version_outdated status
  5. Server always returns serverVersion so clients know when updates are available

Activation with Version

var result = await client.ActivateAsync(key, email, hwid, "MyProduct", version: "1.5.0");

if (!result.Success) {
    if (result.ErrorCode == "version_outdated") {
        Console.WriteLine($"Your version 1.5.0 is outdated.");
        Console.WriteLine($"Minimum required: {result.MinimumRequiredVersion}");
        Console.WriteLine($"Latest available: {result.ServerVersion}");
    } else {
        Console.WriteLine($"Activation failed: {result.ErrorMessage}");
    }
} else {
    Console.WriteLine($"Activated! Server version: {result.ServerVersion}");
}

Validation with Version Check

var (isValid, status, exp, features, serverVer, minVer) = 
    await client.ValidateAsync(hwid, "MyProduct", activationKey, version: "1.5.0");

if (status == "version_outdated") {
    // Force user to upgrade
    Console.WriteLine($"Please upgrade to version {minVer} or later");
} else if (isValid) {
    // Optionally notify about available updates
    if (serverVer != "1.5.0")
        Console.WriteLine($"Update available: v{serverVer}");
}

Smart Validation with Version

var result = await client.ValidateSmartAsync(keyOrCode, hwid, "MyProduct");

// ServerVersion and MinimumRequiredVersion are included in the result
if (result.ServerVersion != null)
    Console.WriteLine($"Server version: {result.ServerVersion}");
if (result.MinimumRequiredVersion != null)
    Console.WriteLine($"Minimum required: {result.MinimumRequiredVersion}");

Offline Workflow

  1. Customer runs your app and sees their Hardware ID (from GenerateHardwareId())
  2. Customer contacts you with their Hardware ID (email/phone/support ticket)
  3. You generate an Offline Activation Code in the License Keys page
  4. Customer enters the code into your app
  5. Your app validates locally using ValidateOffline() or ValidateSmartAsync()
Security: Offline codes are RSA-SHA256 signed and embed: product ID, hardware ID, expiration date, email, and feature flags. They cannot be forged or transferred to other machines.

Permissions

MethodRequired PermissionNotes
ValidateAsyncverifyServer enforces in /api/license/validate
GenerateActivationKeyAsyncgeneratePublisher operations
DeactivateAsyncNoneAllowed only when server-side hardware matches bound license
SendSupportTicketAsyncemailSends email via tenant/global SMTP
ActivateAsyncNoneCurrent API does not require a specific scope

API Reference

// Version
string GetVersion();

// Hardware
string GenerateHardwareId();

// Activation (Updated in 0.5.0 - now returns ActivationResult)
Task<ActivationResult> ActivateAsync(string activationKey, string email, string hardwareId, string? version = null);
Task<ActivationResult> ActivateAsync(string activationKey, string email, string hardwareId, string productName, string? version = null);

// Validation (Online) - Updated in 0.5.0 to include serverVersion and minimumRequiredVersion
Task<bool> ValidateAsync(string activationKey, string hardwareId);
Task<(bool isValid, string status, DateTime? expirationDate, int[] features, string? serverVersion, string? minimumRequiredVersion)> ValidateAsync(string hardwareId, string productName, string? activationKey = null, string? version = null);

// Validation (Offline)
OfflineValidationResult ValidateOffline(string offlineCode, string hardwareId);
Task<SmartValidationResult> ValidateSmartAsync(string keyOrCode, string hardwareId, string? productName = null);

// Deactivation
Task<bool> DeactivateAsync(string activationKey, string hardwareId, string productName);
Task<DeactivationResult> DeactivateSmartAsync(string keyOrCode, string hardwareId, string productName);

// Generation (publisher)
Task<string> GenerateActivationKeyAsync(string productName, string email, int? planId = null, int? validityDays = null);

// Support
Task SendSupportTicketAsync(string productName, string email, string subject, string message);

// Configuration
IExisOneClient WithBaseUrl(string baseUrl);

// Result Types
class ActivationResult { // New in 0.5.0
    bool Success; 
    string? ErrorCode;      // e.g., "version_outdated"
    string? ErrorMessage; 
    string? ServerVersion;  // Current product version on server
    string? MinimumRequiredVersion; // Minimum required when enforcement is on
    string? LicenseData;    // Raw license data on success
}
class OfflineValidationResult { bool IsValid; string? ErrorMessage; string? ProductName; DateTime? ExpirationDate; int[]? Features; bool IsExpired; bool HardwareMismatch; }
class SmartValidationResult { // Updated in 0.5.0
    bool IsValid; string Status; DateTime? ExpirationDate; int[]? Features; 
    bool WasOffline; string? ErrorMessage; string? ProductName;
    string? ServerVersion;  // New in 0.5.0
    string? MinimumRequiredVersion; // New in 0.5.0
}
class DeactivationResult { bool Success; bool ServerNotified; string? ErrorMessage; }

Common Errors

Demo & Examples

A complete working demo console application is available on GitHub: ExisOne.Client.Console

The demo includes interactive examples of all SDK features:

git clone https://github.com/exisllc/ExisOne.Client.Console.git
cd ExisOne.Client.Console
dotnet run --project ExisOne.Client.Console

Changelog