17 Jun 2025

Extending the Archived Forge SDK to Use BIM 360 APIs in .NET Framework 4.8

Default blog image

When Autodesk transitioned from BIM 360 to Autodesk Construction Cloud (ACC), many APIs were migrated, consolidated, or restructured. The new official.NET SDK targets .NET 8.0 and is designed specifically for ACC.

While we strongly encourage customers to transition to ACC, there may be business-related reasons why the migration isn’t feasible or requires significant investment. This post offers a small respite for legacy customers who still rely on BIM 360 and wish to remain on .NET Framework 4.8.

Although the original NuGet Gallery | Autodesk.Forge 1.9.9 has been archived, it’s still possible to extend it and integrate newer endpoints for BIM 360, while staying in .NET 4.8.

This post demonstrates how to extend the archived Forge SDK to call the BIM 360 Account Admin API using a familiar API/client model pattern.

Extending the SDK

To retrieve projects from a BIM 360 account, you can define a new IAccountAdminApi interface and a corresponding implementation AccountAdminApi.

Here’s a snapshot of the extension:

public interface IAccountAdminApi : IApiAccessor
{
    Task<ApiResponse<dynamic>> GetProjectsAsyncWithHttpInfo(
        string accountId,
        string region = null,
        string userId = null,
        Dictionary<string, string> queryParams = null);
}

 

Implementation

You can use RestClient from the existing Forge client, leveraging its configuration, OAuth handling, and exception factories. The code constructs the HTTP request manually and parses the response using ApiResponse<T> for consistency with the SDK’s conventions.

public class AccountAdminApi : IAccountAdminApi
{
    private Autodesk.Forge.Client.ExceptionFactory _exceptionFactory = (name, response) => null;
    public Configuration Configuration { get; set; }

    public ExceptionFactory ExceptionFactory
    {
        get
        {
            if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1)
                throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported.");
            return _exceptionFactory;
        }
        set { _exceptionFactory = value; }
    }

    public AccountAdminApi(Configuration configuration = null)
    {
        Configuration = configuration ?? Configuration.Default;
        ExceptionFactory = Configuration.DefaultExceptionFactory;
    }

    public string GetBasePath() => Configuration.ApiClient.RestClient.Options.BaseUrl.ToString();

    public async Task<ApiResponse<dynamic>> GetProjectsAsyncWithHttpInfo(
 string accountId,
 string region = null,
 string userId = null,
 Dictionary<string, string> queryParams = null)
    {
        if (string.IsNullOrEmpty(accountId))
            throw new ApiException(400, "Missing required parameter 'accountId'");

        var path = $"/construction/admin/v1/accounts/{accountId}/projects";

        var pathParams = new Dictionary<string, string>(); // Do not add "format"
        var query = queryParams ?? new Dictionary<string, string>();
        var headers = new Dictionary<string, string>(Configuration.DefaultHeader);
        var formParams = new Dictionary<string, string>();
        var fileParams = new Dictionary<string, FileParameter>();
        object postBody = null;

        if (!string.IsNullOrEmpty(region))
            headers["Region"] = Configuration.ApiClient.ParameterToString(region);

        if (!string.IsNullOrEmpty(userId))
            headers["User-Id"] = Configuration.ApiClient.ParameterToString(userId);

        if (!string.IsNullOrEmpty(Configuration.AccessToken))
            headers["Authorization"] = "Bearer " + Configuration.AccessToken;

        // Set Accept header
        string[] acceptHeaders = new string[] { "application/json" };
        var accept = Configuration.ApiClient.SelectHeaderAccept(acceptHeaders);
        if (accept != null)
            headers["Accept"] = accept;

        // Set Content-Type
        string[] contentTypes = new string[] { "application/json" };
        var contentType = Configuration.ApiClient.SelectHeaderContentType(contentTypes);

        // Make the HTTP request
        RestResponse response = (RestResponse)await Configuration.ApiClient.CallApiAsync(
            path,
            Method.Get,
            query,
            postBody,
            headers,
            formParams,
            fileParams,
            pathParams,
            contentType
        );

        if (ExceptionFactory != null)
        {
            Exception exception = ExceptionFactory("GetProjects", response);
            if (exception != null) throw exception;
        }

        return new ApiResponse<dynamic>(
            (int)response.StatusCode,
            response.Headers
            .GroupBy(x => x.Name)
            .ToDictionary(g => g.Key, g => g.First().Value?.ToString()),
            Configuration.ApiClient.Deserialize(response, typeof(object))
        );
    }


}

 

Example Usage

// 2-legged OAuth
var token = await OAuthHandler.Get2LeggedTokenAsync(SCOPES);

// Extend the Forge configuration
var accountAdminApi = new AccountAdminApi(new Configuration
{
    AccessToken = token.access_token,
    ApiClient = new ApiClient("https://developer.api.autodesk.com")
});

// Use custom query parameters
var result = await accountAdminApi.GetProjectsAsyncWithHttpInfo(
    accountId: "your-account-id",
    region: "US",
    userId: "your-user-id",
    queryParams: new ProjectQueryParams { Status = new List<string> { "archived" } }.ToDictionary());

// Pretty print
Console.WriteLine(JsonSerializer.Serialize(result.Data, new JsonSerializerOptions { WriteIndented = true }));

 

A complete working sample is provided below:

BIM360.AccountAdmin.App.cs

 

Related Article