Essential·

Implementing Autodesk Entitlement API in Desktop Applications

A practical guide to securing your Autodesk plugins with the Entitlement API, including complete implementation examples for AutoCAD, Revit, and Inventor.
Aa DevTeam8 min read

Implementing Autodesk Entitlement API in Desktop Applications

Autodesk Entitlement API diagram showing authentication flow

What is the Entitlement API?

The Entitlement API provides a straightforward way to implement copy protection for your Autodesk App Store desktop applications. It verifies whether users have purchased your app or have a valid subscription, allowing you to control access to your software features accordingly.

How the Entitlement API Works

When users download an app from the Autodesk App Store, they must sign in with their Autodesk account. This login process allows the App Store to maintain a record of users who have legitimately acquired your app. The Entitlement API leverages this information to verify user entitlements.

The workflow is simple:

  1. User purchases your app from the Autodesk App Store
  2. The purchase is recorded against their Autodesk account
  3. Your app queries the Entitlement API using the user's ID and your app ID
  4. The API returns whether the user is entitled to use your app

API Details

  • Base URL: https://apps.autodesk.com
  • Endpoint: webservices/checkentitlement
  • HTTP Method: GET
  • Parameters: ?userid=***&appid=***
  • Return: JSON object with entitlement status

Complete Implementation Example for AutoCAD

Here's a fully functional implementation for AutoCAD using modern C# practices:

using Autodesk.AutoCAD.ApplicationServices.Core;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace ACAD_EntitlementApi
{
    public class Plugin : IExtensionApplication
    {
        /// <summary>
        /// This Utility import is necessary to invoke Autodesk Login.
        /// </summary>
        [DllImport("AcConnectWebServices.arx", EntryPoint = "AcConnectWebServicesLogin")]
        public static extern bool AcConnectWebServicesLogin();
        
        public static Editor Ed => Application.DocumentManager.MdiActiveDocument.Editor;
            
        public void Initialize()
        {
            // Initialization code could go here
            // You might want to check entitlement when the plugin loads
        }
        
        public void Terminate()
        {
            // Cleanup code if needed
        }
        
        [CommandMethod("CheckEntitlement")]
        public static async void CheckEntitlement()
        {
            // App Id is unique to your app, assigned when published to the store
            var pr = Ed.GetString("Enter the AppId that you would like to check entitlement for:\n");
            if (pr.Status != PromptStatus.OK) return;
            string appId = pr.StringResult;
            // For production, you would hardcode this:
            // string appId = "2024453975166401172";
            
            var userId = (string)Application.GetSystemVariable("ONLINEUSERID");
            
            // If ONLINEUSERID is empty or null, user is not logged in
            if (String.IsNullOrEmpty(userId))
            {
                Ed.WriteMessage("\nUser not logged in. Initiating login...\n");
                AcConnectWebServicesLogin();
                
                // Get the user ID again after login
                userId = (string)Application.GetSystemVariable("ONLINEUSERID");
                
                if (String.IsNullOrEmpty(userId))
                {
                    Ed.WriteMessage("\nLogin failed or was canceled. Cannot proceed with entitlement check.\n");
                    return;
                }
            }
            
            try
            {
                Ed.WriteMessage($"\nChecking entitlement for user: {userId}\n");
                bool isValid = await IsEntitled(userId, appId);
                Ed.WriteMessage($"\nEntitlement check result: {(isValid ? "Valid" : "Not Valid")}\n");
                
                if (isValid)
                {
                    // Enable full functionality here
                    Ed.WriteMessage("\nUser is entitled to use this app. Enabling all features.\n");
                }
                else
                {
                    // Restrict functionality or show purchase information
                    Ed.WriteMessage("\nUser is not entitled to use this app. Please purchase from the Autodesk App Store.\n");
                }
            }
            catch (Exception ex)
            {
                Ed.WriteMessage($"\nError checking entitlement: {ex.Message}\n");
            }
        }
        
        public static async Task<bool> IsEntitled(string userId, string appId)
        {
            if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(appId))
            {
                throw new ArgumentException("User ID and App ID are required");
            }
            
            var url = string.Format("https://apps.autodesk.com/webservices/checkentitlement?userid={0}&appid={1}",
                            Uri.EscapeUriString(userId),
                            Uri.EscapeUriString(appId));
                            
            using (var httpClient = new HttpClient())
            using (var httpResponse = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
            {
                httpResponse.EnsureSuccessStatusCode(); // throws if not 200-299
                
                if (httpResponse.Content is object && 
                    httpResponse.Content.Headers.ContentType.MediaType == "application/json")
                {
                    var content = await httpResponse.Content.ReadAsStringAsync();
                    try
                    {
                        EntitlementResponse entitlementResponse = 
                            JsonConvert.DeserializeObject<EntitlementResponse>(content);
                        return entitlementResponse.IsValid;
                    }
                    catch (JsonException ex)
                    {
                        throw new Exception($"Invalid response format: {ex.Message}");
                    }
                }
                else
                {
                    throw new Exception("Unexpected content type in response");
                }
            }
        }
    }
    
    public class EntitlementResponse
    {
        public string UserId { get; set; }
        public string AppId { get; set; }
        public bool IsValid { get; set; }
        public string Message { get; set; }
    }
}

Implementation for Different Autodesk Products

Each Autodesk product requires a different approach to obtain the user ID. Here's how to get the user ID in various products:

AutoCAD (and Verticals)

string userId = Application.GetSystemVariable("ONLINEUSERID") as string;

Revit

string userId = Application.LoginUserId;

Inventor

// Import the necessary function
[DllImport("AdWebServices", EntryPoint = "GetUserId", CharSet = CharSet.Unicode)]
private static extern int AdGetUserId(StringBuilder userid, int buffersize);

// Get the user ID
int buffersize = 128;
StringBuilder sb = new StringBuilder(buffersize);
int len = AdGetUserId(sb, buffersize);
sb.Length = len;
string userId = sb.ToString();
💡 Note for Inventor 2020+: The login mechanism changed from AdWebService to AdSSO. You may need to call Application.Login (a hidden API) before using the entitlement API.

Sample Response

The API returns a JSON object with this structure:

{
  "UserId": "2N5FMZW9CCED",
  "AppId": "2024453975166401172",
  "IsValid": true,
  "Message": "Ok"
}

The key fields are:

  • IsValid: true if the user has entitlement, false otherwise
  • Message: Status of the API call ("Ok", "Invalid parameter(s)", etc.)

Best Practices for Implementing Entitlement Checks

1. Handle Offline Scenarios

Since the Entitlement API requires an internet connection, implement offline support:

private bool CheckOfflineEntitlement()
{
    try
    {
        // Check if we have a cached valid entitlement
        if (File.Exists(entitlementCachePath))
        {
            var cachedData = File.ReadAllText(entitlementCachePath);
            var cachedEntitlement = JsonConvert.DeserializeObject<EntitlementCache>(cachedData);
            
            // Allow offline use for 7 days
            if (DateTime.Now < cachedEntitlement.ExpirationDate)
            {
                return cachedEntitlement.IsEntitled;
            }
        }
    }
    catch (Exception)
    {
        // If anything goes wrong with reading cache, require online verification
    }
    
    return false;
}

private void CacheEntitlement(bool isEntitled)
{
    try
    {
        var cacheData = new EntitlementCache
        {
            IsEntitled = isEntitled,
            ExpirationDate = DateTime.Now.AddDays(7)
        };
        
        string json = JsonConvert.SerializeObject(cacheData);
        File.WriteAllText(entitlementCachePath, json);
    }
    catch (Exception)
    {
        // Cache writing failed, but we can continue
    }
}

public class EntitlementCache
{
    public bool IsEntitled { get; set; }
    public DateTime ExpirationDate { get; set; }
}

2. Optimize API Call Frequency

Don't check entitlement before every operation. Instead:

  • Check once when your app starts
  • Store the result for the session
  • Periodically recheck (e.g., daily or weekly)

3. Provide Graceful Degradation

If entitlement check fails, consider allowing limited functionality:

private void HandleEntitlementResult(bool isEntitled)
{
    if (isEntitled)
    {
        EnableFullFunctionality();
    }
    else if (IsTrialPeriodActive())
    {
        EnableTrialFunctionality();
        ShowTrialMessage();
    }
    else
    {
        DisablePremiumFeatures();
        ShowPurchaseDialog();
    }
}

4. Secure Your Implementation

Protect your entitlement check logic from tampering:

  • Don't hardcode your App ID in plaintext
  • Consider obfuscating your compiled code
  • Add integrity checks to detect modification

Common Error Messages

MessageCauseSolution
"Invalid parameter(s)"Missing or incorrect userid/appidVerify parameters are not null or empty
"Please use https"Request made over HTTP instead of HTTPSAlways use HTTPS for the API call
"Ok" with IsValid: falseUser hasn't purchased the appPrompt user to purchase from App Store

Complete Example with Error Handling and Offline Support

Here's a more comprehensive implementation:

public class EntitlementManager
{
    private const string APP_ID = "2024453975166401172"; // Your app ID
    private const string CACHE_FILENAME = "entitlement_cache.json";
    private const int OFFLINE_DAYS_ALLOWED = 7;
    
    private string _cachePath;
    private bool? _currentEntitlementStatus = null;
    
    public EntitlementManager()
    {
        _cachePath = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "YourCompany",
            "YourApp",
            CACHE_FILENAME);
            
        // Ensure directory exists
        Directory.CreateDirectory(Path.GetDirectoryName(_cachePath));
    }
    
    public async Task<bool> CheckEntitlement()
    {
        if (_currentEntitlementStatus.HasValue)
        {
            // Return cached result for this session
            return _currentEntitlementStatus.Value;
        }
        
        string userId = GetCurrentUserId();
        
        if (string.IsNullOrEmpty(userId))
        {
            // Not logged in - try to prompt for login
            if (!PromptForLogin())
            {
                // User declined login, check offline cache
                return CheckOfflineEntitlement();
            }
            
            // Get ID again after login
            userId = GetCurrentUserId();
            if (string.IsNullOrEmpty(userId))
            {
                return CheckOfflineEntitlement();
            }
        }
        
        try
        {
            // Try online check
            bool isEntitled = await IsEntitledOnline(userId, APP_ID);
            _currentEntitlementStatus = isEntitled;
            
            // Cache the result for offline use
            CacheEntitlement(isEntitled);
            
            return isEntitled;
        }
        catch (Exception)
        {
            // Online check failed, fall back to offline
            return CheckOfflineEntitlement();
        }
    }
    
    private string GetCurrentUserId()
    {
        // Implementation depends on the Autodesk product
        #if AUTOCAD
            return Application.GetSystemVariable("ONLINEUSERID") as string;
        #elif REVIT
            return Application.LoginUserId;
        #elif INVENTOR
            // Inventor implementation...
            return GetInventorUserId();
        #else
            throw new NotImplementedException("Product not supported");
        #endif
    }
    
    private async Task<bool> IsEntitledOnline(string userId, string appId)
    {
        var url = $"https://apps.autodesk.com/webservices/checkentitlement?userid={Uri.EscapeUriString(userId)}&appid={Uri.EscapeUriString(appId)}";
        
        using (var httpClient = new HttpClient())
        {
            httpClient.Timeout = TimeSpan.FromSeconds(10); // Set reasonable timeout
            
            using (var response = await httpClient.GetAsync(url))
            {
                response.EnsureSuccessStatusCode();
                
                var content = await response.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject<EntitlementResponse>(content);
                
                return result.IsValid;
            }
        }
    }
    
    private bool CheckOfflineEntitlement()
    {
        try
        {
            if (File.Exists(_cachePath))
            {
                var json = File.ReadAllText(_cachePath);
                var cache = JsonConvert.DeserializeObject<EntitlementCache>(json);
                
                if (DateTime.Now <= cache.ExpirationDate)
                {
                    return cache.IsEntitled;
                }
            }
        }
        catch
        {
            // Ignore cache errors
        }
        
        // No valid cache found
        return false;
    }
    
    private void CacheEntitlement(bool isEntitled)
    {
        try
        {
            var cache = new EntitlementCache
            {
                IsEntitled = isEntitled,
                ExpirationDate = DateTime.Now.AddDays(OFFLINE_DAYS_ALLOWED)
            };
            
            File.WriteAllText(_cachePath, JsonConvert.SerializeObject(cache));
        }
        catch
        {
            // Ignore cache write errors
        }
    }
    
    private bool PromptForLogin()
    {
        // Implementation depends on the product
        #if AUTOCAD
            return AcConnectWebServicesLogin();
        #elif REVIT
            // Show login dialog for Revit
        #elif INVENTOR
            // Inventor login implementation
        #endif
        
        return false;
    }
}

public class EntitlementCache
{
    public bool IsEntitled { get; set; }
    public DateTime ExpirationDate { get; set; }
}

Summary

The Autodesk Entitlement API provides a reliable way to verify whether users have legitimately purchased or subscribed to your app. By implementing this API correctly, you can:

  • Protect your intellectual property
  • Enable license verification for online users
  • Support offline usage through caching
  • Create flexible licensing strategies for trials and subscriptions

For any questions about implementing the Entitlement API, contact [email protected].

Autodesk App Store

Browse and publish Autodesk apps

Developer Documentation

Official Autodesk developer resources

Publisher Portal

Manage your published applications

API Support Forums

Get help from the developer community