Migration Guide

Migrating from other licensing platforms to elm, and upgrading existing elm integrations to the current client API (v3.4)

Common Migration Scenarios

Migrating from other licensing platforms to elm? This guide covers the most common migration scenarios and provides practical steps for transitioning your existing licensing implementation. Already on elm and moving to a newer SDK or client API version? See Upgrading elm.

Code samples use the .NET SDK (evoleap.Licensing), which is the reference implementation; the Java, Python, JavaScript/TypeScript, C++ and Ruby SDKs follow the same model with language-appropriate naming.

FlexNet Publisher/FNP

Traditional concurrent licensing with floating licenses and network-based license servers.

  • License server-based architecture
  • Concurrent user management
  • Feature-based licensing
  • Complex network configuration

10Duke Enterprise

Cloud-based licensing with JWT tokens and modern API-first architecture.

  • OAuth2 token authentication
  • RESTful API integration
  • Entitlement-based licensing
  • Cloud-native design

Zentitle/Nalpeiron

Software protection with licensing enforcement and anti-piracy features.

  • Embedded licensing engines
  • Hardware fingerprinting
  • Activation-based licensing
  • Protection and obfuscation

Custom Solutions

In-house licensing systems, simple key validation, or database-driven approaches.

  • Database-driven validation
  • Simple key verification
  • Custom restriction logic
  • Basic user management

Migration Planning Checklist

Thorough planning is essential for a successful migration. Use this checklist to ensure all aspects are considered.

1. Assessment Phase

  • Current Architecture: Document existing licensing integration points
  • License Models: Identify current restriction types (named user, concurrent, node-locked)
  • Feature Control: Catalog how features are currently enabled/disabled
  • Customer Data: Inventory existing customer licenses and entitlements
  • Integration Points: Map CRM, billing, and support system integrations
  • Usage Patterns: Analyze current usage metrics and peak loads
  • Dependencies: Identify systems that depend on current licensing

2. elm Product Configuration

  • Product Type: Choose Desktop, Server, or Web API based on deployment model
  • Restriction Mapping: Map existing license types to elm restriction models
  • Feature Migration: Define features and components in elm portal
  • RSA Key Generation: Generate public/private key pairs for signature verification
  • Test Environment: Set up staging environment for testing
  • License Templates: Create license templates matching current offerings

3. SDK Integration

  • Code Integration: Replace existing licensing SDK calls with elm SDK
  • State Management: Implement elm state persistence patterns
  • Error Handling: Update error handling for elm-specific scenarios
  • Testing: Comprehensive testing with various license scenarios
  • Backwards Compatibility: Plan transition period for existing customers
  • Performance Testing: Validate performance with elm integration

Migrating from FlexNet Publisher (FNP)

FlexNet Publisher users typically need to migrate from license server-based architecture to elm's cloud-native approach.

Architecture Comparison

FlexNet Publisher Architecture
  • License server (lmgrd) deployment
  • Network-based license checkout
  • Vendor daemon configuration
  • License file management
  • Firewall and network setup
elm Architecture
  • Cloud-based licensing service
  • HTTPS API communication
  • No server installation required
  • License key-based registration
  • Simplified network requirements

Code Migration Example

FlexNet Publisher Code
// FlexNet checkout
checkout_status = lc_checkout(lm_job, "FEATURE_NAME", "1.0", 1, 
                             LM_CO_NOWAIT, &lm_handle, context);
if (checkout_status != 0) {
    // Handle checkout failure
    char error_msg[MAX_ERROR_LEN];
    lc_errstring(lm_job, error_msg, MAX_ERROR_LEN);
    fprintf(stderr, "Checkout failed: %s\n", error_msg);
    return -1;
}

// Use the feature
perform_licensed_operation();

// Check in when done
lc_checkin(lm_handle, LM_CI_ALL_FEATURES, 0);
elm Equivalent
// elm component checkout
var result = await controlManager.CheckOutComponentsAsync(new[] { "FeatureName" });
if (result.Success) {
    try {
        // Use the feature
        PerformLicensedOperation();
    } finally {
        // Always check in
        await controlManager.CheckInComponentsAsync(new[] { "FeatureName" });
    }
} else {
    // Handle checkout failure with structured error handling
    switch (result.FailureReason) {
        case ComponentCheckoutFailureReason.InsufficientSessions:
            ShowUserMessage("Feature is currently in use by maximum number of users");
            break;
        case ComponentCheckoutFailureReason.InsufficientTokens:
            ShowUpgradeDialog("Additional licenses required for this feature");
            break;
        default:
            LogError($"Checkout failed: {result.FailureReason}");
            break;
    }
}

Key Migration Benefits

Advantages of Moving to elm:
  • No Infrastructure: Eliminate license server deployment and maintenance
  • Hosted Service: evoleap operates the licensing service instead of you managing license servers
  • Modern APIs: HTTPS JSON APIs with async/await patterns in the SDKs
  • Better Analytics: Usage analytics and reporting in the elm portal
  • Simplified Networking: Standard HTTPS (port 443) instead of custom ports
  • Air-gapped Sites: Offline checkout, or elm Lite as a local license server, when there is no internet access

License Server Feature Mapping

FlexNet Concept elm Equivalent Notes
Feature Component or Feature Use elm Features for simple on/off, Components for usage tracking
Concurrent Users Session-based Component elm components provide similar concurrent session control
Named User Named User Restriction Direct equivalent with better identity integration
License File License Key Simpler key-based activation instead of file management
Vendor Daemon elm Cloud Service Managed service eliminates custom daemon development

Migrating from 10Duke Enterprise

10Duke Enterprise users will find elm familiar as both are cloud-native platforms, but elm provides simpler integration patterns.

API Comparison

10Duke Enterprise
// 10Duke JWT validation and entitlement check
var tokenRequest = new OAuth2TokenRequest {
    GrantType = "client_credentials",
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret"
};
var token = await client.RequestTokenAsync(tokenRequest);

// Check entitlements using Authorization API
var authzApi = new AuthzApi(token.AccessToken);
var decision = await authzApi.CheckPermissionAsync("read", "feature1");

if (decision.Allow) {
    // User has permission to use feature1
    UseFeature1();
} else {
    ShowAccessDeniedMessage();
}

// Token refresh and management
if (token.IsExpired) {
    token = await client.RefreshTokenAsync(token.RefreshToken);
}
elm Equivalent
// elm session validation - no token management needed
var sessionValidity = await controlManager.ValidateSessionAsync();
if (sessionValidity.IsValid) {
    // IsValid stays true while elm is unreachable but the license's
    // validation-failure grace period has not run out
    if (sessionValidity.IsInValidationFailureGracePeriod) {
        ShowOfflineWarning(sessionValidity.GracePeriodExpiration);
    }
    // Features granted by the license are cached in the validation state
    if (controlManager.ValidationState.Features.Contains("Feature1")) {
        UseFeature1();
    } else {
        ShowUpgradeMessage("Feature1 requires a Pro license");
    }
} else {
    // Handle various invalid session reasons
    switch (sessionValidity.InvalidReason) {
        case InvalidReason.LicenseExpired:
            ShowRenewalDialog();
            break;
        case InvalidReason.ServiceUnreachable:
            // Unreachable and the grace period has expired
            ShowConnectivityError();
            break;
        default:
            ShowLicenseErrorMessage(sessionValidity.InvalidReason);
            break;
    }
}

Key Differences from 10Duke

elm Simplifications:
  • No OAuth2 Management: elm handles authentication internally
  • Persistent State: Built-in state caching and offline support
  • Hardware Binding: Native instance identity without custom implementation
  • Simpler APIs: Single SDK instead of multiple API clients
  • Automatic Refresh: Session management handled by the SDK
  • Better Offline Support: Configurable offline checkout periods

Entitlement Model Mapping

10Duke Concept elm Equivalent Migration Notes
Permission Feature Direct mapping for basic entitlements
Resource Access Component Use components for usage-based licensing
JWT Token Session State elm manages authentication internally
User Context User Identity Similar user identification with better AD integration
License Pool License Restrictions Concurrent user limits map to elm restriction models

Migrating from Zentitle/Nalpeiron

Zentitle users will appreciate elm's cloud-native approach while maintaining strong license security and hardware binding capabilities.

Activation Model Comparison

Zentitle/Nalpeiron
// Zentitle license activation and validation
var licenseManager = new LicenseManager();
var activationResult = licenseManager.ActivateLicense("LICENSE-KEY");

if (activationResult.IsValid) {
    var entitlements = licenseManager.GetEntitlements();
    if (entitlements.Contains("premium_features")) {
        EnablePremiumFeatures();
    }
    
    // Check hardware binding
    if (licenseManager.IsHardwareChanged()) {
        var reactivationResult = licenseManager.ReactivateLicense();
        if (!reactivationResult.IsValid) {
            ShowHardwareMismatchError();
            return;
        }
    }
} else {
    ShowActivationFailureMessage(activationResult.ErrorCode);
}

// Periodic license validation
var validationResult = licenseManager.ValidateLicense();
if (!validationResult.IsValid) {
    HandleLicenseInvalid(validationResult.Reason);
}
elm Equivalent
// elm registration and validation with automatic hardware binding
// (one-time activation; skip when ValidationState.Registered is already true)
if (!controlManager.ValidationState.Registered) {
    var userInfo = new UserInfo("User Name", "user@company.com");
    var registrationResult = await controlManager.RegisterAsync("LICENSE-KEY", userInfo);
    if (!registrationResult.Success) {
        // RegistrationResult carries a server message, not a reason code
        ShowActivationFailureMessage(registrationResult.ErrorMessage);
        return;
    }
    SaveState(controlManager.ValidationState); // persist after every call
}

// Each launch: begin a session. The hardware identity is re-checked here.
var sessionValidity = await controlManager.ValidateSessionAsync();
if (sessionValidity.IsValid) {
    if (controlManager.ValidationState.Features.Contains("PremiumFeatures")) {
        EnablePremiumFeatures();
    }
} else {
    switch (sessionValidity.InvalidReason) {
        case InvalidReason.InconsistentRegistration:
            ShowHardwareMismatchDialog();   // machine no longer matches its instance
            break;
        case InvalidReason.InstanceDisabled:
            ShowInstanceDisabledMessage();  // vendor disabled this machine in the portal
            break;
        case InvalidReason.NoSeatsAvailable:
            ShowSeatLimitMessage();
            break;
        default:
            HandleSessionInvalid(sessionValidity.InvalidReason);
            break;
    }
}

Migration Advantages

Benefits of Moving to elm:
  • Cloud-Native Security: Server-side validation instead of embedded engines
  • Hardware Monitoring: Fuzzy hardware matching, with per-instance overrides in the portal
  • Simplified Integration: Single SDK call instead of complex activation flows
  • Better User Experience: Seamless license management for end users
  • Server-side Changes: License changes take effect at the next session validation, without client updates
  • Modern Architecture: HTTPS JSON APIs instead of embedded components

Protection Model Evolution

Zentitle Feature elm Approach Security Improvement
Embedded License Engine Cloud Validation Server-side security reduces client-side attack surface
Hardware Fingerprinting Instance Identity Fuzzy matching of the hardware identity; mismatches report InvalidReason.InconsistentRegistration
Activation Codes License Keys Simpler key management with better user experience
Local License Storage Application-persisted state The SDK exposes a serializable ValidationState that you store (and protect) yourself; clock roll-back is detected (InvalidReason.UserTamperingDetected)
Manual Reactivation Portal-controlled hardware updates Allow a one-time hardware update for an instance, or ignore hardware matching for instances on cloud VMs, from the elm portal

Migrating from Custom Solutions

Custom licensing implementations vary widely, but elm can typically replace most functionality with improved reliability and features.

Common Custom Implementation Patterns

Database-Driven Licensing

License validation through database queries

  • User credentials in database
  • License status checks via SQL
  • Feature flags in database tables
  • Custom expiration logic

Simple Key Validation

Basic license key verification

  • Algorithm-based key generation
  • Checksum or hash validation
  • No server-side verification
  • Limited feature control

File-Based Licensing

License files with custom formats

  • XML or JSON license files
  • Digital signature verification
  • Machine-specific binding
  • Manual file distribution

Web Service Validation

Custom REST API for license checking

  • HTTP-based validation calls
  • Custom authentication
  • Basic usage tracking
  • Limited offline support

Migration from Database-Driven Systems

Custom Database Solution
// Custom database license validation
public class CustomLicenseManager {
    public bool ValidateLicense(string username, string productKey) {
        using (var connection = new SqlConnection(connectionString)) {
            var query = @"
                SELECT l.ExpirationDate, l.Features, u.IsActive 
                FROM Licenses l 
                JOIN Users u ON l.UserId = u.Id 
                WHERE u.Username = @username AND l.ProductKey = @productKey";
            
            var result = connection.QueryFirstOrDefault(query, 
                new { username, productKey });
            
            if (result == null || !result.IsActive) 
                return false;
            
            if (result.ExpirationDate < DateTime.Now) 
                return false;
            
            // Store features for later use
            currentUserFeatures = result.Features.Split(',');
            return true;
        }
    }
    
    public bool HasFeature(string featureName) {
        return currentUserFeatures?.Contains(featureName) ?? false;
    }
}
elm Replacement
// elm replaces custom database with cloud service
public class ElmLicenseManager {
    private readonly ControlManager controlManager;
    
    public async Task<bool> ValidateLicenseAsync() {
        var sessionValidity = await controlManager.ValidateSessionAsync();
        return sessionValidity.IsValid;
    }
    
    public bool HasFeature(string featureName) {
        return controlManager.ValidationState.Features.Contains(featureName);
    }
    
    public async Task<bool> RegisterUserAsync(string licenseKey, string userName, string email) {
        var userInfo = new UserInfo(userName, email);
        var result = await controlManager.RegisterAsync(licenseKey, userInfo);
        return result.Success;
    }
    
    // elm handles server-side:
    // - License expiration and seat limits
    // - Feature and component entitlements
    // - Usage tracking
    // The SDK handles validation-failure grace periods and offline checkouts;
    // your code persists controlManager.ValidationState between runs.
}

Benefits of elm vs Custom Solutions

  • Reduced Development Time: Eliminate months of custom licensing development
  • Production-Ready Security: Proven encryption and validation mechanisms
  • Scalability: Cloud infrastructure handles any number of users
  • Reliability: Hosted service plus client-side grace periods, instead of custom server maintenance
  • Advanced Features: Components, offline checkout, usage analytics
  • Cross-Platform Support: SDKs for all major platforms and languages
  • Professional Support: Technical support from evoleap

Upgrading elm: Client API Versions

If you already use elm, upgrading is mostly a matter of taking a newer SDK. Each SDK is built against one client API version and calls only that version's endpoints. The elm server still serves every version below, so existing installations keep working while you roll out the new build.

API version Endpoint prefix What it adds
v1 (legacy) /register, /validate Instance registration and validation only
v2 (legacy) /<method> register_instance, begin_session, extend_session, end_session
v3.0 /api/v3/auth/ Separate instance and user registration (register_user, register_app, validate_instance), user and app sessions (begin_user_session, begin_app_session), current_release
v3.1 /api/v3.1/auth/ Components: check_out_components, components_status
v3.2 /api/v3.2/auth/ Offline use and tokens: check_out_license, check_in_license, check_out_component_tokens, check_in_component_tokens, record_offline_session, get_user_license, check_out_consumable_component, check_out_webapi_components
v3.3 /api/v3.3/auth/ get_license_info, check_in_components
v3.4 (current) /api/v3.4/auth/ check_out_full_license (full-license checkout for elm Lite)
v3.5 Preview /api/v3.5/auth/ custom_data (the license's key/value properties) on successful begin_app_session, begin_user_session, validate_instance, extend_session and check_out_license responses; new validate_user endpoint. Not yet released.
Upgrading from v2 or earlier:
  • Sessions split: v2 begin_session became begin_user_session (desktop and per-user) or begin_app_session (server/app). The SDKs choose the right one; in .NET call ControlManager.ValidateSessionAsync().
  • License key moved to registration: pass it to RegisterAsync(licenseKey, userInfo), not to the ControlManager constructor.
  • Re-registration: existing instances keep their registration; persist and reload ValidationState so the new build does not register again.

SDK Upgrade Notes

Current releases of every SDK except Python target client API v3.4. Python targets v3.2.

SDK Package Released version API Custom data (v3.5)
.NET (reference) NuGet evoleap.Licensing 5.4.1 v3.4 Preview ValidationState.CustomData, SessionState.CustomData
Java Maven com.evoleap.licensing:com.evoleap.licensing 1.1.0 v3.4 Preview parsed by the web-service layer only
JavaScript / TypeScript npm evoleap-licensing 0.7.108 v3.4 Preview customData on the user state
Python PyPI evoleap_licensing 1.1.4 v3.2 Preview parsed on the branch, but the SDK calls v3.2 endpoints, so none is returned yet
C++ vcpkg port evoleap-licensing 0.7.0 v3.4 Preview parsed on session results only
Ruby RubyGems evoleap-licensing 1.0.3.18 v3.4 Preview session_state.custom_data

Changes in current releases

  • Unlimited entitlements: the .NET, Java, JavaScript, Python and Ruby SDKs now handle licenses and component entitlements with unlimited counts correctly. Update if you issue unlimited licenses.
  • Expired sessions (.NET, Java): set ControlStrategy.StartSessionForExpiredSession = true to end an expired session and start a new one automatically on the next validation (default false).
  • UTF-8: .NET, Java and C++ send UTF-8, so user names and license data with non-ASCII characters round-trip correctly.
  • .NET: set ConnectionSettings.Log (Action<Exception, string>) to log HTTPS and RSA errors. The package assemblies, including the licensing model and hardware-info DLLs, are now signed. The obsolete UserControlManager.CheckoutFullLicenseAsync and UserControlManager.RegisterOfflineAsync were removed (Feb 2025); use CheckOutFullLicenseAsync / RegisterOfflineAsync on ServerControlManager or ControlManager.
  • Python: the released SDK covers desktop licensing (ControlManager) and calls v3.2 endpoints.
  • Ruby: SDK for Ruby/Rails web apps (Ruby 3.1+): server instance registration, per-user sessions, components and grace periods.
  • Server-side entitlement check: a server acting for a desktop client can ask what that client's session is entitled to with the access-token endpoint POST /api/session_entitlements (session_validate scope). It is read-only and does not extend the session.

Preview — API v3.5, not yet released. The following is on development branches of the backend and SDKs and may change before release.

  • License properties / custom data: vendors set key/value properties on a license (in the portal or with the set_license_properties action). v3.5 returns them as custom_data on successful session responses, and the SDKs cache them in state like features (see the table above for where each SDK exposes them).
  • validate_user: lets a web app check whether a user is still entitled without starting a session or using a seat (Ruby ServerControlManager#validate_user).
  • Python parity: server-side licensing (ServerControlManager, UserControlManager), offline checkout and the remaining web-service calls. The branch still sends requests to the v3.2 endpoint, so its v3.3/v3.4 calls will need a newer endpoint before release.
// .NET (preview): read a license property after validating
var validity = await controlManager.ValidateSessionAsync();
if (validity.IsValid &&
    controlManager.ValidationState.CustomData.TryGetValue("max_projects", out var value)) {
    maxProjects = int.Parse(value);   // all values are strings
}
// JavaScript (preview): the default prefix is v3.4; opt in to v3.5
import { user } from 'evoleap-licensing';
const ucm = user.getUserControlManager(
  instanceId, serverManager, userState, sessionState,
  'https://elm.evoleap.com/api/v3.5/auth');
// after a successful validateSession, the user state carries customData

Data Migration Strategies

Successfully migrating customer licenses and historical data requires careful planning and execution.

Customer License Migration

1. Data Export and Analysis

Extract customer license data from your existing system:

// Example: Export existing license data
public class LicenseExporter {
    public List<LegacyLicense> ExportLicenses() {
        var licenses = new List<LegacyLicense>();
        
        // Extract from your existing system
        foreach (var customer in GetAllCustomers()) {
            var license = new LegacyLicense {
                LicenseId = customer.LicenseId,
                CustomerId = customer.Id,
                CustomerName = customer.Name,
                Email = customer.Email,
                ProductName = customer.ProductName,
                LicenseType = customer.LicenseType, // Named, Concurrent, etc.
                UserLimit = customer.UserLimit,
                ExpirationDate = customer.ExpirationDate,
                EnabledFeatures = customer.Features.ToList(),
                HardwareId = customer.HardwareId,
                ActivationDate = customer.ActivationDate,
                IsActive = customer.IsActive
            };
            licenses.Add(license);
        }
        
        return licenses;
    }
}

2. License Mapping and Conversion

Map each legacy license onto the request body of the elm create_license action. elm has four license types and five commercial models, addressed by numeric id:

Field Values Notes
license.license_type_id 1 Named User, 2 Concurrent, 3 Unrestricted, 4 Node Locked Decides which seat count applies (users, sessions or instances)
license.commercial_model_id 1 Perpetual, 2 SaaS, 3 Lease, 4 Free Trial, 5 Free Free licenses are always Unrestricted
entitlement.seat_count integer Applied as user, session or instance count according to the license type
term.start_date / term.end_date ISO dates License term; required for every model except Free
license.features / license.components arrays of names Must already exist on the product in the elm portal
// Example: map a legacy license to an elm create_license request body
public class LicenseMapper {
    private const string ElmProductName = "My Product"; // product NAME as shown in the portal

    public object MapToElm(LegacyLicense legacy) {
        return new {
            license = new {
                product = ElmProductName,
                company_name = legacy.CustomerName,   // or company_id / crm_id to attach
                company_email = legacy.Email,         // to an existing company
                crm_id = legacy.CustomerId,
                license_type_id = MapLicenseType(legacy.LicenseType),
                commercial_model_id = legacy.ExpirationDate.HasValue ? 2 /* SaaS */ : 1 /* Perpetual */,
                features = MapFeatures(legacy.EnabledFeatures),
                description = $"Migrated from legacy license {legacy.LicenseId}"
            },
            entitlement = new { seat_count = legacy.UserLimit },
            term = new {
                start_date = legacy.ActivationDate.ToString("yyyy-MM-dd"),
                end_date = legacy.ExpirationDate?.ToString("yyyy-MM-dd")
            },
            comment = "Legacy license migration"
        };
    }

    private int MapLicenseType(string legacyType) {
        return legacyType.ToLower() switch {
            "named" => 1,       // Named User
            "concurrent" => 2,  // Concurrent
            "unlimited" => 3,   // Unrestricted
            "site" => 4,        // Node Locked (per machine)
            _ => 1
        };
    }

    private List<string> MapFeatures(List<string> legacyFeatures) {
        // Map legacy feature names to the feature names defined in the elm portal
        return legacyFeatures.Select(f => f switch {
            "advanced_reporting" => "AdvancedReporting",
            "api_access" => "APIAccess",
            _ => f
        }).ToList();
    }
}

3. Bulk License Creation

Create licenses through the elm license-administration API. Requests are authenticated with a personal access token (PAT) created in the elm portal, sent as Authorization: Bearer <token>. A token with the license_admin scope applies changes immediately; with only license_maintainer, actions are queued for approval in the portal. elm does not ship a client library for this API; the example below calls it with HttpClient.

Things to know before a bulk import:
  • Keys are generated by elm. create_license cannot import an existing key; the response contains the new license_key, which you send to the customer.
  • Keep the legacy id. Put it in the license description (as below) and keep the legacy-id-to-key mapping in your own system. Preview With API v3.5, set_license_properties will store it as a license property returned to clients as custom_data (see SDK Upgrade Notes).
  • Avoid duplicate companies. Look up existing ones with GET /api/companies?q= and pass company_id.
// Example: bulk license migration through the elm PAT API
public class BulkMigration {
    private readonly HttpClient http = new HttpClient {
        BaseAddress = new Uri("https://elm.evoleap.com/")
    };

    public BulkMigration(string personalAccessToken) {
        http.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", personalAccessToken);
    }

    public async Task<MigrationResult> MigrateLicensesAsync(List<LegacyLicense> legacyLicenses) {
        var results = new MigrationResult();
        var mapper = new LicenseMapper();

        foreach (var legacy in legacyLicenses) {
            var response = await http.PostAsJsonAsync(
                "api/license_actions/create_license", mapper.MapToElm(legacy));
            var body = await response.Content.ReadFromJsonAsync<JsonElement>();

            if (!response.IsSuccessStatusCode) {
                results.FailedMigrations.Add(new MigrationFailure {
                    CustomerId = legacy.CustomerId,
                    Error = body.ToString(),
                    LegacyLicense = legacy
                });
                continue;
            }

            var licenseKey = body.GetProperty("license_key").GetString();

            // PREVIEW (API v3.5, not yet released): also store the legacy id as a
            // license property so clients receive it as custom_data. Replaces all properties.
            // await http.PostAsJsonAsync("api/license_actions/set_license_properties", new {
            //     license_key = licenseKey,
            //     properties = new[] { new { key = "legacy_license_id", value = legacy.LicenseId } }
            // });

            results.SuccessfulMigrations.Add(new MigrationMapping {
                CustomerId = legacy.CustomerId,
                CustomerEmail = legacy.Email,
                OldLicenseId = legacy.LicenseId,
                NewLicenseKey = licenseKey
            });
            await UpdateCustomerDatabase(legacy.CustomerId, licenseKey);
        }

        return results;
    }
}

Other actions on the same API include modify_license, modify_entitlement, create_term, add_feature, create_component_entitlement, deactivate_license and activate_license (all POST /api/license_actions/<action>, addressed by license_key). Read-only endpoints include GET /api/licenses, GET /api/licenses/show?license_key= and GET /api/users.

Historical Data Migration

Usage History and Analytics

Consider what historical data to preserve:

  • License Activation History: When customers first activated licenses
  • Usage Patterns: Historical usage data for analytics
  • Support History: Previous license-related support cases
  • Billing Records: Payment and renewal history
  • User Assignments: Historical user-to-license mappings

Phased Migration Approach

For large deployments, a gradual migration minimizes risk and allows for learning and adjustment.

Four-Phase Migration Strategy

Phase 1: Dual Stack Deployment

Goal: Deploy elm alongside existing licensing for testing and validation

  • Install elm SDK in parallel with existing licensing
  • Implement feature flags to switch between systems
  • Test with internal users and beta customers
  • Validate license migration scripts and processes
  • Train support team on elm platform
// Dual stack implementation example
public class HybridLicenseManager {
    private readonly LegacyLicenseManager legacyManager;
    private readonly ControlManager elmManager;
    private readonly bool useElm; // Feature flag
    
    public async Task<bool> ValidateLicenseAsync() {
        if (useElm) {
            var sessionValidity = await elmManager.ValidateSessionAsync();
            return sessionValidity.IsValid;
        } else {
            return legacyManager.ValidateLicense();
        }
    }
}

Phase 2: New Customer Migration

Goal: All new customers use elm exclusively

  • Configure sales and provisioning systems for elm
  • Update onboarding processes and documentation
  • Migrate pilot existing customers to validate processes
  • Collect feedback and refine migration procedures
  • Monitor elm system performance and reliability

Phase 3: Existing Customer Migration

Goal: Migrate existing customers during renewal cycles

  • Migrate customers at renewal time
  • Provide migration assistance and support
  • Handle special cases and custom requirements
  • Maintain legacy system for remaining customers
  • Communicate migration benefits and timeline

Phase 4: Legacy System Decommission

Goal: Complete migration and shutdown legacy systems

  • Migrate remaining customers with incentives if needed
  • Archive legacy license data and documentation
  • Decommission legacy license servers and infrastructure
  • Remove legacy code from applications
  • Celebrate successful migration completion!

Risk Mitigation Strategies

Rollback Planning

Always have a rollback plan in case of issues:

  • Feature Flags: Ability to instantly revert to legacy system
  • Data Backup: Complete backup of legacy license data
  • Parallel Operation: Keep legacy system running during transition
  • Customer Communication: Clear communication about any issues
  • Support Escalation: Direct escalation path to elm support team

Success Metrics

Define clear metrics to measure migration success:

  • Technical Metrics: System uptime, API response times, error rates
  • Customer Metrics: Customer satisfaction, support ticket volume
  • Business Metrics: License utilization, revenue impact, operational costs
  • Operational Metrics: Migration velocity, rollback incidents, team productivity

Common Migration Challenges and Solutions

Learn from common migration challenges and their proven solutions.

Challenge: Complex Feature Mappings

Problem: Legacy licensing has complex feature hierarchies or dependencies that don't map directly to elm's feature model.

Solution: Model editions and bundles as feature groups in the elm portal (they are reported in ValidationState.FeatureGroups), and keep any remaining dependency logic in a small adapter:

// Handling complex feature relationships in elm
public class FeatureManager {
    private readonly ControlManager controlManager;
    
    public bool IsFeatureAvailable(string featureName) {
        // Simple feature check
        if (controlManager.ValidationState.Features.Contains(featureName)) {
            return true;
        }
        
        // Handle feature dependencies and hierarchies
        return featureName switch {
            "AdvancedReporting" => HasReportingBundle(),
            "DataExport" => HasDataAccess() && HasExportRights(),
            "APIAccess" => HasIntegrationPackage(),
            _ => false
        };
    }
    
    private bool HasReportingBundle() {
        return controlManager.ValidationState.Features.Contains("ReportingBundle") ||
               controlManager.ValidationState.Features.Contains("EnterpriseEdition");
    }
    
    private bool HasDataAccess() {
        return controlManager.ValidationState.Features.Contains("DataAccess") ||
               controlManager.ValidationState.Features.Contains("ProfessionalEdition") ||
               controlManager.ValidationState.Features.Contains("EnterpriseEdition");
    }
}

Challenge: Customer Resistance to Change

Problem: Existing customers are reluctant to migrate to a new licensing system.

Solution: Provide clear value proposition and migration support:

  • Improved Reliability: Hosted licensing instead of self-managed license servers
  • Better User Experience: Simpler activation and license management
  • Enhanced Security: Modern encryption and cloud-based validation
  • New Capabilities: Offline checkout, component tokens, usage analytics
  • Reduced IT Overhead: No license servers to maintain
  • Migration Assistance: Professional support during transition
  • Incentives: Consider offering migration incentives or grace periods

Challenge: State Migration and Compatibility

Problem: Existing installations hold legacy activation state that doesn't map to elm's state model.

Solution: Don't convert legacy state into elm state. elm state (IValidationState) records a server-side registration, so it must come from RegisterAsync. On the first launch of the elm-enabled version, use the legacy activation only to find the customer's new elm license key, then register:

// First launch after upgrading: bridge legacy activation to elm registration
var savedState = LoadElmState();            // null on first launch
var controlManager = new ControlManager(productId, version, publicKey,
    userIdentity, instanceIdentity, controlStrategy, savedState);

if (!controlManager.ValidationState.Registered) {
    var legacy = LegacyLicenseStore.TryRead();
    // Your own lookup (e.g. your customer portal) from legacy license id to the
    // elm key created during data migration; fall back to asking the user.
    var elmKey = legacy != null
        ? await LookUpMigratedKeyAsync(legacy.LicenseId)
        : PromptForLicenseKey();

    var result = await controlManager.RegisterAsync(elmKey,
        new UserInfo(legacy?.UserName ?? "", legacy?.Email ?? ""));
    if (result.Success) {
        SaveElmState(controlManager.ValidationState);
        LegacyLicenseStore.MarkMigrated();  // keep it until registration succeeds
    }
}

var validity = await controlManager.ValidateSessionAsync();
SaveElmState(controlManager.ValidationState);

Challenge: Network and Connectivity Requirements

Problem: Some customer environments have restricted internet access.

Solution: Use elm's connectivity options:

  • Proxy Configuration: The .NET SDK uses the system default proxy with the current user's credentials; elm needs outbound HTTPS to elm.evoleap.com
  • Grace Periods: The license's grace period (set in the portal) keeps sessions valid while elm is unreachable
  • Offline Checkout: Check out the license, or component tokens, for a fixed period before going offline
  • elm Lite: A local license server for isolated networks, fed by a full-license checkout (API v3.4+)
// Connection settings are static and apply to every ControlManager
ConnectionSettings.RequestTimeout = TimeSpan.FromSeconds(30);
// Only for elm Lite or a dedicated elm host - not for a proxy:
// ConnectionSettings.Host = "https://elm-lite.internal.example.com";

// Allow use before activation completes (e.g. while waiting on IT)
var strategy = new ControlStrategy {
    GracePeriodForUnregisteredProduct = TimeSpan.FromDays(7)
};

// Before going offline: check out the license for up to 30 days
var offline = await controlManager.CheckOutLicenseForOfflineUseAsync(TimeSpan.FromDays(30));
if (!offline.Success) {
    ShowMessage($"Offline checkout failed: {offline.FailureReason}");
} else {
    ShowMessage($"Licensed offline until {offline.ExpirationTime:g}");
}

// Back online: return it early so the seat is released
await controlManager.CheckInOfflineLicenseAsync(
    controlManager.ValidationState.OfflineCheckouts.First().CheckoutKey);

Challenge: Integration with Existing Business Systems

Problem: CRM, billing, and support systems need to integrate with elm instead of legacy licensing.

Solution: Drive elm from those systems with the PAT license-administration API (see Data Migration for authentication):

// Integration with CRM and billing systems (http is an HttpClient
// with BaseAddress https://elm.evoleap.com/ and a Bearer PAT)
public class ElmBusinessIntegration {
    private readonly HttpClient http;
    private readonly CrmSystem crmSystem;

    // Pull the current state of a license into the CRM (license_read scope)
    public async Task SyncLicenseAsync(string customerId, string licenseKey) {
        var license = await http.GetFromJsonAsync<JsonElement>(
            $"api/licenses/show?license_key={Uri.EscapeDataString(licenseKey)}");
        await crmSystem.UpdateLicenseStatusAsync(customerId, license);
    }

    // Renewal: add a new term to the existing license; the key stays the same
    public async Task<bool> ProcessRenewalAsync(string licenseKey, DateTime start, DateTime end) {
        var response = await http.PostAsJsonAsync("api/license_actions/create_term", new {
            license_key = licenseKey,
            term = new { start_date = start.ToString("yyyy-MM-dd"), end_date = end.ToString("yyyy-MM-dd") },
            comment = "Renewal from billing system"
        });
        return response.IsSuccessStatusCode;
    }

    // Seat change after an upgrade/downgrade
    public Task<HttpResponseMessage> SetSeatsAsync(string licenseKey, int seats) =>
        http.PostAsJsonAsync("api/license_actions/modify_entitlement", new {
            license_key = licenseKey,
            entitlement = new { seat_count = seats }
        });
}

Migration Timeline Template

Use this timeline template as a starting point for planning your elm migration project.

Weeks 1-2: Assessment and Planning

Goals: Understand current state and design migration approach

Discovery Tasks:
  • Audit existing licensing implementation and integration points
  • Document current license models, features, and restrictions
  • Inventory customer licenses and identify special cases
  • Map current features to elm feature/component model
  • Identify integration requirements (CRM, billing, support systems)
Planning Tasks:
  • Create elm product configuration design
  • Develop migration project plan and timeline
  • Set up elm development environment and test accounts
  • Design license migration scripts and data mapping
  • Plan customer communication strategy
Deliverables:
  • Current state assessment document
  • elm product configuration specification
  • Migration project plan with timeline and resources
  • Risk assessment and mitigation strategies

Weeks 3-6: Development and Testing

Goals: Implement elm integration and validate migration processes

Development Tasks:
  • Integrate elm SDK into application(s)
  • Implement state management and persistence
  • Add feature/component access control
  • Implement comprehensive error handling
  • Create license migration scripts and tools
  • Develop dual-stack support for gradual migration
Testing Tasks:
  • Unit testing of elm integration
  • Integration testing with various license scenarios
  • Performance testing and optimization
  • Test migration scripts with sample data
  • User acceptance testing with internal teams
Deliverables:
  • Application with elm SDK integration
  • Comprehensive test suite
  • License migration tools and scripts
  • Performance benchmarks and optimization report

Weeks 7-8: Pilot Deployment

Goals: Validate migration process with real customers

Pilot Tasks:
  • Deploy to staging environment
  • Select and migrate 5-10 pilot customers
  • Monitor system performance and reliability
  • Collect feedback from pilot customers
  • Test customer support processes
  • Refine migration procedures based on learnings
Validation Tasks:
  • Verify all license scenarios work correctly
  • Test offline and connectivity edge cases
  • Validate business system integrations
  • Confirm customer communication processes
Deliverables:
  • Pilot migration results and lessons learned
  • Refined migration procedures
  • Customer feedback analysis
  • Go/no-go decision for full migration

Weeks 9-12: Full Migration

Goals: Complete migration of all customers

Production Tasks:
  • Production deployment of elm integration
  • Begin new customer onboarding with elm
  • Execute bulk migration of existing customers
  • Provide customer migration assistance and support
  • Monitor system performance and customer satisfaction
Communication Tasks:
  • Send migration announcements to customers
  • Provide migration guides and documentation
  • Host migration webinars and support sessions
  • Handle customer questions and concerns
  • Update marketing and sales materials
Deliverables:
  • Production elm deployment
  • All customers migrated to elm
  • Legacy system decommissioning plan
  • Post-migration performance report

Weeks 13-16: Optimization and Cleanup

Goals: Optimize performance and complete migration

Optimization Tasks:
  • Performance tuning based on production usage
  • Address any customer issues or edge cases
  • Optimize license utilization and allocation
  • Fine-tune monitoring and alerting
Cleanup Tasks:
  • Remove legacy licensing code from applications
  • Decommission legacy license servers
  • Archive legacy license data
  • Update documentation and procedures
  • Conduct post-migration review and lessons learned
Deliverables:
  • Optimized elm deployment
  • Legacy system fully decommissioned
  • Updated documentation and procedures
  • Migration project completion report

Timeline Considerations

Factors That May Extend Timeline

  • Complex Legacy System: Heavily customized licensing may require additional mapping
  • Large Customer Base: More customers mean more migration coordination
  • Multiple Products: Each product may need separate migration planning
  • Extensive Integrations: CRM, billing, and support system integrations take time
  • Custom Requirements: Special customer needs may require custom development
  • Regulatory Compliance: Some industries have specific licensing audit requirements

Factors That May Accelerate Timeline

  • Simple Legacy System: Basic licensing models migrate quickly
  • Cloud-Native Applications: Already cloud-ready applications integrate faster
  • Professional Services: elm migration consultants can accelerate the process
  • Staged Rollout: Phased approach allows parallel development and migration
  • Modern SDK Usage: Applications already using modern patterns integrate more easily

Professional Migration Support

evoleap offers migration support to help your transition to elm go smoothly.

Support Options

  • Consulting Services: Guidance on migration planning, product configuration and license mapping
  • Technical Support: Help with SDK integration and migration-related issues
  • Staging Accounts: A free account on the elm staging server (elm-staging.evoleap.com) for testing your integration and migration scripts
  • Documentation: This site, plus the README and samples shipped with each SDK

Getting Started with Migration Support

  • Contact evoleap to discuss your migration needs
  • Schedule a discovery call to assess your current licensing setup
  • Get a staging account and develop a migration plan with evoleap
  • Prepare your team with the integration guides on this site

Contact Information

See also evoleap support and contact options.