A comprehensive demo console application showcasing all features of the ExisOne.Client NuGet package for software licensing and activation.
ExisOne is a cloud-based software licensing platform that helps developers:
- π Protect software with hardware-locked licenses
- π° Monetize applications with license keys
- π Track activations with real-time analytics
- π Support offline customers with RSA-signed activation codes
- π³ Integrate with Stripe & PayPal for automatic license delivery
- .NET 8.0 SDK or later
- An ExisOne account (free tier available)
# Clone this repository
git clone https://github.com/ExisLLC/ExisOne.Client.Console.git
# Navigate to the project
cd ExisOne.Client.Console
# Run the demo
dotnet run --project ExisOne.Client.ConsoleBefore running, update the configuration in Program.cs:
// Your ExisOne API base URL
private const string BaseUrl = "https://www.exisone.com";
// Your access token from the ExisOne dashboard
private const string AccessToken = "exo_at_YOUR_PUBLIC_KEY_YOUR_SECRET_KEY";
// Your product name
private const string ProductName = "MyProduct";This demo showcases all ExisOne.Client SDK capabilities:
| Feature | Description |
|---|---|
| Hardware ID Generation | Generate unique device fingerprints |
| License Activation | Activate licenses with version checking |
| Simple Validation | Quick boolean license check |
| Rich Validation | Get status, expiration, features, server version |
| Smart Validation | Auto-detect online/offline with fallback |
| Offline Validation | Validate RSA-signed codes without network |
| Deactivation | Release licenses from hardware |
| Smart Deactivation | Opportunistic server sync |
| Support Tickets | Submit tickets from within your app |
| Key Generation | Programmatically create license keys |
dotnet add package ExisOne.Client --version 0.7.0using ExisOne.Client;
var client = new ExisOneClient(new ExisOneClientOptions
{
BaseUrl = "https://www.exisone.com",
AccessToken = "exo_at_<public>_<secret>",
OfflinePublicKey = null // Set for offline license support
});// Generate once and persist locally
var hardwareId = client.GenerateHardwareId();// Activate with version check (v0.5.0+)
var result = await client.ActivateAsync(
activationKey,
email,
hardwareId,
"MyProduct",
version: "1.0.0"
);
if (!result.Success)
{
if (result.ErrorCode == "version_outdated")
Console.WriteLine($"Please upgrade to {result.MinimumRequiredVersion}");
else
Console.WriteLine(result.ErrorMessage);
}// Rich validation with version info (v0.5.0+)
var (isValid, status, expiration, features, serverVersion, minVersion) =
await client.ValidateAsync(hardwareId, "MyProduct", activationKey, version: "1.0.0");
if (status == "version_outdated")
{
Console.WriteLine($"Upgrade required: minimum version is {minVersion}");
}
else if (isValid)
{
Console.WriteLine($"Licensed until: {expiration}");
// Check for updates
if (serverVersion != "1.0.0")
Console.WriteLine($"Update available: v{serverVersion}");
}// Auto-detects online vs offline keys
var result = await client.ValidateSmartAsync(keyOrOfflineCode, hardwareId, "MyProduct");
Console.WriteLine($"Valid: {result.IsValid}");
Console.WriteLine($"Mode: {(result.WasOffline ? "Offline" : "Online")}");
Console.WriteLine($"Server Version: {result.ServerVersion}");// Standard deactivation
await client.DeactivateAsync(activationKey, hardwareId, "MyProduct");
// Smart deactivation (works offline)
var result = await client.DeactivateSmartAsync(keyOrCode, hardwareId, "MyProduct");
Console.WriteLine($"Server notified: {result.ServerNotified}");For air-gapped environments, ExisOne supports RSA-signed offline activation codes:
var client = new ExisOneClient(new ExisOneClientOptions
{
BaseUrl = "https://www.exisone.com",
OfflinePublicKey = @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----"
});- Customer runs your app and sees their Hardware ID
- Customer contacts you with their Hardware ID
- You generate an Offline Activation Code in the ExisOne dashboard
- Customer enters the code into your app
- App validates locally using
ValidateOffline()orValidateSmartAsync()
// Direct offline validation (no network)
var result = client.ValidateOffline(offlineCode, hardwareId);
if (result.IsValid)
Console.WriteLine($"Valid until: {result.ExpirationDate}");
else if (result.IsExpired)
Console.WriteLine("License expired");
else if (result.HardwareMismatch)
Console.WriteLine("Wrong machine");Force users to upgrade their software before using new features:
- Set Minimum Required Version in ExisOne dashboard β Product Management
- Enable Enforce Version Check
- Pass version during activation/validation
- Handle
version_outdatedstatus
var (isValid, status, _, _, serverVer, minVer) =
await client.ValidateAsync(hwid, "MyProduct", key, version: "1.0.0");
if (status == "version_outdated")
{
// Block usage until upgrade
MessageBox.Show($"Please upgrade to version {minVer}");
return;
}
// Optionally notify about updates
if (serverVer != null && serverVer != "1.0.0")
{
ShowNotification($"Update available: v{serverVer}");
}Starting in v0.6.0, the API always returns an expiration date during validation, even for trial users or invalid licenses. This enables consistent UI messaging about when trials expire.
| Scenario | Expiration Date |
|---|---|
| First visit (new hardware ID + valid product) | NOW + TrialDays |
| Returning visit (existing device record) | Stored expiration from device record |
| Invalid/blank license key | Device's creation date + TrialDays |
| Activated license | License expiration date |
| Product not found | Current date |
// First time user with no activation key (trial mode)
var (isValid, status, expirationDate, features, _, _) =
await client.ValidateAsync(hardwareId, "MyProduct", activationKey: null);
// status = "trial" (if within trial period) or "expired" (if trial ended)
// expirationDate = CreatedAt + TrialDays (always set, never null)
if (status == "trial")
{
var daysLeft = (expirationDate.Value - DateTime.UtcNow).Days;
Console.WriteLine($"Trial mode: {daysLeft} days remaining");
}
else if (status == "expired")
{
Console.WriteLine($"Trial expired on {expirationDate:d}. Please purchase a license.");
}- First contact creates a record: When a hardware ID first contacts the API with a valid product, a device record is created with
CreatedAt = NOWandExpirationDate = NOW + TrialDays - Consistent dates: The same hardware ID will always get the same expiration date (based on first contact), preventing trial resets
- Override capability: Administrators can manually adjust expiration dates via the License Activity page
- Deactivation clears records: When a license is deactivated, the device record's activation key and expiration are cleared, allowing trial mode to resume if applicable
- SDK Documentation
- API Reference
- AI Integration Prompt
- MCP Server for Claude - Manage licenses with AI
- Stripe Integration
- PayPal Integration
| Method | Permission Required |
|---|---|
ValidateAsync |
verify |
GenerateActivationKeyAsync |
generate |
SendSupportTicketAsync |
email |
ActivateAsync |
None |
DeactivateAsync |
None |
GenerateHardwareId |
None |
- MCP Server Support: New AI-powered license management via Claude Desktop MCP integration
- Documentation Updates: Improved setup instructions and troubleshooting guides
- Consistent Expiration Dates:
expirationDateis now always returned during validation, even for invalid/trial licenses - Trial Expiration Calculation: For first-time visits with a valid product, expiration is calculated as
CreatedAt + TrialDays - Device Tracking: Device records are now created on first contact for analytics and trial management
- Deactivation Fix: Device records are properly cleared on license deactivation
- Version Enforcement: Pass client version during activation/validation
- ActivationResult: Structured return type with error codes
- Server Version Info: All responses include
serverVersionandminimumRequiredVersion - version_outdated Status: New status when client is below minimum version
- Offline license validation with RSA-signed codes
- Smart validation (auto-detect online/offline)
- Opportunistic deactivation sync
- License deactivation
- IP and country tracking
ExisOne is developed by Exis, LLC, providing software licensing solutions for developers worldwide.
- Website: https://www.exisone.com
- Email: exisllc@gmail.com
- Phone: +1 (423) 714-7047
This demo project is licensed under the MIT License. See LICENSE for details.