Implementing Autodesk Entitlement API in Desktop Applications
Implementing Autodesk Entitlement API in Desktop Applications

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:
- User purchases your app from the Autodesk App Store
- The purchase is recorded against their Autodesk account
- Your app queries the Entitlement API using the user's ID and your app ID
- 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();
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
Message | Cause | Solution |
---|---|---|
"Invalid parameter(s)" | Missing or incorrect userid/appid | Verify parameters are not null or empty |
"Please use https" | Request made over HTTP instead of HTTPS | Always use HTTPS for the API call |
"Ok" with IsValid: false | User hasn't purchased the app | Prompt 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].