C# SDK
Official C# SDK for LicenseSeat. Add license validation to your .NET apps, Unity games, and Godot projects in minutes.
Building a Unity game? We have a dedicated Unity SDK with full IL2CPP, WebGL, iOS, and Android support. No DLLs - just install via Unity Package Manager.
Installation
NuGet (.NET, Godot)
dotnet add package LicenseSeat
Requirements: .NET Standard 2.0+ (.NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+)
Unity
Option 1: Git URL (Recommended)
- Open Window > Package Manager
- Click + > Add package from git URL...
- Paste:
https://github.com/licenseseat/licenseseat-csharp.git?path=src/LicenseSeat.Unity
Option 2: manifest.json
Add to Packages/manifest.json:
{
"dependencies": {
"com.licenseseat.sdk": "https://github.com/licenseseat/licenseseat-csharp.git?path=src/LicenseSeat.Unity"
}
}
Option 3: OpenUPM
openupm add com.licenseseat.sdk
Pin to a version:
https://github.com/licenseseat/licenseseat-csharp.git?path=src/LicenseSeat.Unity#v0.4.0
Quick Start
using LicenseSeat;
var client = new LicenseSeatClient(new LicenseSeatClientOptions
{
ApiKey = "pk_live_xxxxxxxx",
ProductSlug = "your-product" // Required
});
// Activate a license
var license = await client.ActivateAsync("XXXX-XXXX-XXXX-XXXX");
// Check entitlements
if (client.HasEntitlement("pro-features"))
{
// Enable pro features
}
Static API (Singleton)
For desktop apps where you want global access:
using LicenseSeat;
// Configure once at startup
LicenseSeat.LicenseSeat.Configure("pk_live_xxxxxxxx", "your-product", options =>
{
options.AutoValidateInterval = TimeSpan.FromHours(1);
});
// Use anywhere in your app
await LicenseSeat.LicenseSeat.Activate("LICENSE-KEY");
if (LicenseSeat.LicenseSeat.HasEntitlement("premium"))
{
// Premium features
}
var status = LicenseSeat.LicenseSeat.GetStatus();
var license = LicenseSeat.LicenseSeat.GetCurrentLicense();
// Cleanup on exit
LicenseSeat.LicenseSeat.Shutdown();
Configuration
Basic Configuration
var client = new LicenseSeatClient(new LicenseSeatClientOptions
{
ApiKey = "pk_live_xxxxxxxx",
ProductSlug = "your-product"
});
Advanced Configuration
var client = new LicenseSeatClient(new LicenseSeatClientOptions
{
ApiKey = "pk_live_xxxxxxxx",
ProductSlug = "your-product",
ApiBaseUrl = "https://licenseseat.com/api/v1",
AutoValidateInterval = TimeSpan.FromHours(1),
HeartbeatInterval = TimeSpan.FromMinutes(5),
AppVersion = "2.1.0",
AppBuild = "42",
MaxRetries = 3,
RetryDelay = TimeSpan.FromSeconds(1),
OfflineFallbackMode = OfflineFallbackMode.NetworkOnly,
MaxOfflineDays = 7,
MaxClockSkew = TimeSpan.FromMinutes(5),
HttpTimeout = TimeSpan.FromSeconds(30),
Debug = true
});
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
ApiKey |
string |
— | Required. Your publishable API key |
ProductSlug |
string |
— | Required. Your product identifier |
ApiBaseUrl |
string |
https://licenseseat.com/api/v1 |
API endpoint |
AutoValidateInterval |
TimeSpan |
1 hour | Background validation interval (0 = disabled) |
HeartbeatInterval |
TimeSpan |
5 minutes | Standalone heartbeat interval (0 = disabled) |
AppVersion |
string? |
null |
Your app version for telemetry |
AppBuild |
string? |
null |
Your app build number for telemetry |
MaxRetries |
int |
3 | Retry attempts for failed requests |
RetryDelay |
TimeSpan |
1 second | Base delay between retries |
OfflineFallbackMode |
OfflineFallbackMode |
Disabled |
Offline validation mode |
MaxOfflineDays |
int |
0 | Offline grace period (0 = disabled) |
MaxClockSkew |
TimeSpan |
5 minutes | Clock tamper tolerance |
HttpTimeout |
TimeSpan |
30 seconds | Request timeout |
Debug |
bool |
false |
Enable debug logging |
Offline Fallback Modes
| Mode | Description |
|---|---|
Disabled |
Offline fallback disabled. Network failures throw exceptions. |
NetworkOnly |
Fall back to offline only for network errors (not 4xx/5xx). Recommended. |
Always |
Fall back to offline on any validation failure. |
License Lifecycle
Activation
var license = await client.ActivateAsync("LICENSE-KEY");
Console.WriteLine($"Activated: {license.Key}");
Console.WriteLine($"Status: {license.Status}");
Console.WriteLine($"Plan: {license.PlanKey}");
Validation
var result = await client.ValidateAsync("LICENSE-KEY");
if (result.Valid)
{
Console.WriteLine("License is valid!");
Console.WriteLine($"Active Seats: {result.License?.ActiveSeats}/{result.License?.SeatLimit}");
}
else
{
Console.WriteLine($"Invalid: {result.Code} - {result.Message}");
}
Deactivation
await client.DeactivateAsync();
Get Status
var status = client.GetStatus();
Console.WriteLine($"Status: {status.StatusType}");
Entitlements
Simple Check
if (client.HasEntitlement("premium"))
{
// Unlock premium features
}
Detailed Check
var entitlement = client.CheckEntitlement("pro-features");
if (entitlement.Active)
{
EnableProFeatures();
}
else
{
switch (entitlement.Reason)
{
case EntitlementInactiveReason.Expired:
ShowRenewalPrompt();
break;
case EntitlementInactiveReason.NotFound:
ShowUpgradePrompt();
break;
case EntitlementInactiveReason.NoLicense:
ShowActivationPrompt();
break;
}
}
Event Handling
// Subscribe to license events
client.Events.On(LicenseSeatEvents.LicenseValidated, _ =>
Console.WriteLine("License validated!"));
client.Events.On(LicenseSeatEvents.ValidationFailed, _ =>
Console.WriteLine("Validation failed!"));
client.Events.On(LicenseSeatEvents.EntitlementChanged, _ =>
Console.WriteLine("Entitlements updated!"));
client.Events.On(LicenseSeatEvents.LicenseActivated, license =>
Console.WriteLine($"Activated: {((License)license).Key}"));
client.Events.On(LicenseSeatEvents.LicenseDeactivated, _ =>
Console.WriteLine("License deactivated"));
client.Events.On(LicenseSeatEvents.HeartbeatSuccess, _ =>
Console.WriteLine("Heartbeat sent"));
client.Events.On(LicenseSeatEvents.HeartbeatError, _ =>
Console.WriteLine("Heartbeat failed"));
Offline Validation
var client = new LicenseSeatClient(new LicenseSeatClientOptions
{
ApiKey = "pk_live_xxxxxxxx",
ProductSlug = "your-product",
OfflineFallbackMode = OfflineFallbackMode.NetworkOnly,
MaxOfflineDays = 7 // Allow 7 days offline
});
// Validate - falls back to the SDK's cached offline artifact if network fails
var result = await client.ValidateAsync("LICENSE-KEY");
if (result.Offline)
{
Console.WriteLine("Validated offline with cached license");
}
Offline model note: the current C# SDK still uses signed offline tokens today. Machine files are the newer preferred offline artifact at the API level, but this SDK has not migrated yet.
The SDK automatically fetches and caches Ed25519-signed offline tokens after activation. When offline:
- Validates token signature cryptographically
- Checks token expiration (
exptimestamp) - Detects clock tampering
- Returns cached entitlements
ASP.NET Core Integration
Dependency Injection
// Program.cs
builder.Services.AddLicenseSeatClient("pk_live_xxxxxxxx", "your-product");
// Or with full options:
builder.Services.AddLicenseSeatClient(options =>
{
options.ApiKey = "pk_live_xxxxxxxx";
options.ProductSlug = "your-product";
options.AutoValidateInterval = TimeSpan.FromMinutes(30);
});
Using in Controllers
public class LicenseController : ControllerBase
{
private readonly ILicenseSeatClient _client;
public LicenseController(ILicenseSeatClient client) => _client = client;
[HttpPost("activate")]
public async Task<IActionResult> Activate([FromBody] string licenseKey)
{
var license = await _client.ActivateAsync(licenseKey);
return Ok(new { license.Key, license.Status });
}
[HttpGet("status")]
public IActionResult GetStatus()
{
var status = _client.GetStatus();
return Ok(new { status.StatusType, status.Message });
}
}
Unity Integration
using UnityEngine;
using LicenseSeat;
public class LicenseController : MonoBehaviour
{
private LicenseSeatManager _manager;
void Start()
{
_manager = FindObjectOfType<LicenseSeatManager>();
// Subscribe to events
_manager.Client.Events.On(LicenseSeatEvents.LicenseValidated, _ =>
Debug.Log("License validated!"));
}
public void ActivateLicense(string licenseKey)
{
StartCoroutine(_manager.ActivateCoroutine(licenseKey, (license, error) =>
{
if (error != null)
{
Debug.LogError($"Failed: {error.Message}");
return;
}
Debug.Log($"Activated: {license.Key}");
}));
}
}
Unity SDK Features:
- Pure C# - No native DLLs, works everywhere
- IL2CPP Ready - Automatic link.xml injection
- WebGL Support - Uses UnityWebRequest
- Editor Tools - Settings window, inspectors
- Samples - Import from Package Manager
Godot Integration
using Godot;
using LicenseSeat;
public partial class LicenseManager : Node
{
private LicenseSeatClient _client;
public override void _Ready()
{
_client = new LicenseSeatClient(new LicenseSeatClientOptions
{
ApiKey = "pk_live_xxxxxxxx",
ProductSlug = "your-product"
});
}
public async void ValidateLicense(string licenseKey)
{
var result = await _client.ValidateAsync(licenseKey);
if (result.Valid)
GD.Print("License is valid!");
else
GD.Print($"Invalid: {result.Code}");
}
public override void _ExitTree() => _client?.Dispose();
}
Error Handling
try
{
var license = await client.ActivateAsync("INVALID-KEY");
}
catch (ApiException ex) when (ex.Code == "license_not_found")
{
Console.WriteLine("License key not found");
}
catch (ApiException ex) when (ex.Code == "seat_limit_exceeded")
{
Console.WriteLine($"All {ex.Details?["seat_limit"]} seats are in use");
}
catch (ApiException ex)
{
Console.WriteLine($"API Error: {ex.Code} - {ex.Message}");
Console.WriteLine($"Status: {ex.StatusCode}");
Console.WriteLine($"Retryable: {ex.IsRetryable}");
}
Common Error Codes
license_not_found- Invalid license keylicense_expired- License has expiredlicense_suspended- License is suspendedseat_limit_exceeded- All seats are in usedevice_not_activated- Device not activated for this licenseinvalid_api_key- Invalid API key
Test Authentication
Test API key authentication and API health:
try
{
var result = await client.TestAuthAsync();
Console.WriteLine($"Authenticated: {result.Authenticated}"); // true if API key is valid
Console.WriteLine($"Healthy: {result.Healthy}"); // API health status
Console.WriteLine($"API Version: {result.ApiVersion}"); // e.g., "1.0.0"
}
catch (ApiException ex)
{
Console.WriteLine($"Auth test failed: {ex.Message}");
}
Synchronous version (for Unity or contexts without async):
var result = client.TestAuth();
AuthTestResult Properties
| Property | Type | Description |
|---|---|---|
Authenticated |
bool |
Whether the API key is valid |
Healthy |
bool |
Whether the API is healthy |
ApiVersion |
string |
The API version (e.g., 1.0.0) |
API Reference
Client Methods
| Method | Description |
|---|---|
ActivateAsync(licenseKey) |
Activate a license on this device |
ValidateAsync(licenseKey) |
Validate a license (check if valid) |
DeactivateAsync() |
Deactivate the current license |
HasEntitlement(key) |
Check if an entitlement is active |
CheckEntitlement(key) |
Get detailed entitlement status |
GetStatus() |
Get current license status |
GetCurrentLicense() |
Get the cached license |
TestAuthAsync() |
Test API key authentication and API health |
ValidationResult Properties
| Property | Type | Description |
|---|---|---|
Valid |
bool |
Whether the license is valid |
Code |
string? |
Error code if invalid |
Message |
string? |
Error message if invalid |
Offline |
bool |
True if validated offline |
License |
License? |
License data |
ActiveEntitlements |
List<Entitlement>? |
Active entitlements |
License Properties
| Property | Type | Description |
|---|---|---|
Key |
string |
The license key |
Status |
string? |
License status (active, expired, etc.) |
ExpiresAt |
DateTimeOffset? |
When the license expires |
PlanKey |
string? |
Associated plan |
SeatLimit |
int? |
Maximum allowed seats |
ActiveSeats |
int |
Currently used seats |
ActiveEntitlements |
List<Entitlement>? |
Active entitlements |
Telemetry
The SDK automatically collects and sends the following telemetry fields on every API call:
| Field | Source |
|---|---|
sdk_name |
Always csharp |
sdk_version |
LicenseSeatClient.SdkVersion |
os_name |
RuntimeInformation.IsOSPlatform (Windows, macOS, Linux) |
os_version |
Environment.OSVersion.Version |
platform |
native (or unity if Unity runtime detected) |
device_model |
Environment.MachineName |
device_type |
desktop, server, or Unity device type |
architecture |
RuntimeInformation.ProcessArchitecture |
cpu_cores |
Environment.ProcessorCount |
memory_gb |
GC.GetGCMemoryInfo().TotalAvailableMemoryBytes (rounded) |
locale |
CultureInfo.CurrentCulture.Name |
language |
CultureInfo.CurrentUICulture.TwoLetterISOLanguageName |
timezone |
IANA timezone (auto-converted from Windows timezone IDs) |
runtime_version |
RuntimeInformation.FrameworkDescription (e.g., .NET 9.0.0) |
app_version |
From config, or Assembly.GetEntryAssembly version |
app_build |
From config, or AssemblyInformationalVersion |
The SDK automatically converts Windows timezone IDs (e.g., Eastern Standard Time) to IANA format (e.g., America/New_York) for consistency across platforms.
See Telemetry for the full field reference.
Platform Support
| Platform | Package | Install |
|---|---|---|
| .NET (Console, ASP.NET, WPF, MAUI) | NuGet | dotnet add package LicenseSeat |
| Godot 4 | NuGet | dotnet add package LicenseSeat |
| Unity | UPM | See Unity section |
Next Steps
- C++ SDK - For native applications
- JavaScript SDK - For web applications
- Offline Licensing - Air-gapped validation
- API Reference - Direct API access