8 Oct 2025

Run work item from desktop app

Almost all of our samples and tutorials are creating web services, however sometimes you might just want to create a small standalone desktop app to automate something.

If the app is just for your personal use then 2-legged authentication might be OK, but if it's for other people as well then you should use PKCE instead:
Getting a Token with PKCE - Desktop App
PKCE is generating a 3-legged token which means that you would also have to sign your activity to use it in a work item:
Using 3-legged OAuth Tokens
The Automation CLI Tool can help with that too with the --key, --patch and --sign commands.

The below sample code of a .NET 8 C# Console App is using 2-legged authentication to run a work item based on the resources that are created via the Automation API tutorial.

using System.Text;
using System.Text.Json;

const string _clientId = "your-client-id";
const string _clientSecret = "your-client-secret";
string _activityId = _clientId + ".UpdateIPTParam+dev";
const string _inputFile = "inventor_sample_file.ipt";
const string _outputFile = "output.ipt";

string _bucketKey = _clientId.ToLower() + "-designautomation";
string _inputFileUrn = $"urn:adsk.objects:os.object:{_bucketKey}/{_inputFile}";
string _outputFileUrn = $"urn:adsk.objects:os.object:{_bucketKey}/{_outputFile}";

// Get access token
static async Task<string> GetTokenAsync(string clientId, string clientSecret)
{
    using var httpClient = new HttpClient();
    var requestBody = new StringContent($"client_id={clientId}&client_secret={clientSecret}&grant_type=client_credentials&scope=code:all data:read data:write", Encoding.UTF8, "application/x-www-form-urlencoded");
    var response = await httpClient.PostAsync("https://developer.api.autodesk.com/authentication/v2/token", requestBody);
    response.EnsureSuccessStatusCode();
    var responseContent = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(responseContent);
    var token = doc.RootElement.GetProperty("access_token").GetString();
    return token ?? string.Empty;
}

// Start work item
static async Task<string> StartWorkItemAsync(string token, string activityId, string inputFileUrn, string outputFileUrn)
{
    using var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
    var workItemPayload = new
    {
        activityId = activityId,
        arguments = new
        {
            inputFile = new
            {
                url = inputFileUrn,
                headers = new
                {
                    Authorization = $"Bearer {token}"
                },
                verb = "get"
            },
            inputJson = new
            {
                url = "data:application/json, {\"width\": 10, \"height\": 20}"
            },
            outputFile = new
            {
                url = outputFileUrn,
                headers = new
                {
                    Authorization = $"Bearer {token}"
                },
                verb = "put"
            }
        }
    };
    var jsonPayload = JsonSerializer.Serialize(workItemPayload);
    var requestBody = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
    var response = await httpClient.PostAsync("https://developer.api.autodesk.com/da/us-east/v3/workitems", requestBody);
    response.EnsureSuccessStatusCode();
    var responseContent = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(responseContent);
    var workItemId = doc.RootElement.GetProperty("id").GetString();
    return workItemId ?? string.Empty;
}

// Check work item status
static async Task<string> CheckWorkItemInfoAsync(string token, string workItemId)
{
    using var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
    var response = await httpClient.GetAsync($"https://developer.api.autodesk.com/da/us-east/v3/workitems/{workItemId}");
    response.EnsureSuccessStatusCode();
    var responseContent = await response.Content.ReadAsStringAsync();
    return responseContent;
}

// Fetch work item report
static async Task<string> GetReportAsync(string reportUrl)
{
    using var httpClient = new HttpClient();
    var reportResponse = await httpClient.GetAsync(reportUrl);
    reportResponse.EnsureSuccessStatusCode();
    var reportContent = await reportResponse.Content.ReadAsStringAsync();

    return reportContent;
}

Console.WriteLine("Started");

var token = await GetTokenAsync(_clientId, _clientSecret);
Console.WriteLine(token);

var workItemId = await StartWorkItemAsync(token, _activityId, _inputFileUrn, _outputFileUrn);
Console.WriteLine(workItemId);

while (true)
{
    var info = await CheckWorkItemInfoAsync(token, workItemId);
    using var doc = JsonDocument.Parse(info);
    var status = doc.RootElement.GetProperty("status").GetString();
    if (status == "success" || status!.StartsWith("failed"))
    {
        Console.WriteLine($"Work item {workItemId} finished with status: {status}");

        var reportUrl = doc.RootElement.GetProperty("reportUrl").GetString();
        var report = await GetReportAsync(reportUrl!);
        Console.WriteLine(report);

        break;
    }
    else
    {
        Console.WriteLine($"Work item {workItemId} is still in progress with status: {status}");
        await Task.Delay(5000);
    }
}

Console.WriteLine("Finished");

You just have to set the values of _clientId and _clientSecret. If you don't want to use the Inventor engine then you should also update the values of _activityId, _inputFile and _outputFile

If all goes well you should see something like this in the Debug Console:

Result in Console

Related Article