> This page is for version v1.
> For other versions, use one of these documentation indexes:
> - v2 (default): https://autodesk-poc.ferndocs.com/v2/llms.txt
> - v1: https://autodesk-poc.ferndocs.com/v1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://autodesk-poc.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://autodesk-poc.ferndocs.com/_mcp/server.

# Get a user's products

GET https://developer.api.autodesk.com/construction/admin/v1/accounts/{accountId}/users/{userId}/products

Returns a list of Forma products the user is associated with in their assigned projects.

Only hub administrators can call this endpoint.

**Note:** This operation is forward compatible. Autodesk may add attributes to the response without prior notice, so your app should ignore any attribute it does not recognize.

Reference: https://autodesk-poc.ferndocs.com/v1/acc-admin/hub-users/get-user-products

## Authentication

- OAuth2 — send the obtained token as `Authorization: Bearer <token>`
- OAuth2 — send the obtained token as `Authorization: Bearer <token>`
- `Authorization` header (bearer token, required) — **OAuth 2.0 (3-legged)** — user context required. Also accepts a Secure Service Account (SSA) token, which is headless like a two-legged flow but classified as three-legged because it preserves user context. **Authorization Code Flow** Authorization URL: [https://developer.api.autodesk.com/authentication/v2/authorize](https://developer.api.autodesk.com/authentication/v2/authorize) Token URL: [https://developer.api.autodesk.com/authentication/v2/token](https://developer.api.autodesk.com/authentication/v2/token) Refresh URL: [https://developer.api.autodesk.com/authentication/v2/token](https://developer.api.autodesk.com/authentication/v2/token) Scopes: - `data:read` — read your data - `account:read` — read account and project data - `account:write` — modify account and project data

## Request

### Path parameters

- `accountId` (string, required) — The ID of the hub that contains the projects. This corresponds to the hub ID used in the [Data Management API](https://aps.autodesk.com/en/docs/data/v2/), with the "**b.**\" prefix removed. For example, `b.c8b0c73d-3ae9` becomes `c8b0c73d-3ae9`.
- `userId` (string, required) — The ID of the user. To find the ID call [GET users](https://aps.autodesk.com/en/docs/acc/v1/reference/http/admin-projectsprojectId-users-GET/). You can use either the Forma ID (`id`) or the Autodesk ID (`autodeskId`).

### Query parameters

- `filter[projectId]` (list of string, optional) — A list of project IDs. Only results where the user is associated with one or more of the specified projects are returned.
- `filter[key]` (list of string, optional) — Filters the list of products by product key — a machine-readable identifier for a Forma product (such as `docs`, `build`, or `cost`). You can specify one or more keys to return only those products the user is associated with. Example: `filter[key]=docs,build` Possible values: `accountAdministration`, `autoSpecs`, `build`, `buildingConnected`, `capitalPlanning`, `cloudWorksharing`, `cost`, `designCollaboration`, `docs`, `financials`, `insight`, `modelCoordination`, `projectAdministration`, `takeoff`, and `workshopxr`.
- `fields` (list of enum, optional) — List of fields to return in the response. Defaults to all fields. Possible values: `projectIds`, `name` and `icon`.
  - Allowed values: `projectIds`, `name`, `icon`
- `sort` (list of enum, optional) — The list of fields to sort by. Each property can be followed by a direction modifier of either `asc` (ascending) or `desc` (descending). The default is `asc`. Possible values: `name`. Default is the order in database.
  - Allowed values: `name`, `name asc`, `name desc`
- `limit` (integer, optional, default: 20) — The maximum number of records to return in the response. Default: `20` Minimum: `1` Maximum: `200` (If a larger value is provided, only 200 records are returned)
- `offset` (integer, optional, default: 0) — The index of the first record to return. Used for pagination in combination with the `limit` parameter. Example: `limit=20` and `offset=40` returns records 41–60.

### Headers

- `Accept-Language` (string, optional, default: en) — This header is not currently supported in the hub Admin API.
- `Region` (string, optional) — Specifies the region where your request should be routed. If not set, the request is routed automatically, which may result in a slight increase in latency. Possible values: `US`, `EMEA`. For a complete list of supported regions, see the [Regions](https://aps.autodesk.com/en/docs/acc/v1/overview/acc-regions/) page.
- `User-Id` (string, optional) — The ID of a user on whose behalf your request is acting. Your app has access to all users specified by the administrator in the SaaS integrations UI. Provide this header value to identify the user to be affected by the request. You can use either the user's Forma ID (`id`), or their Autodesk ID (`autodeskId`). Note that this header is required for hub Admin POST, PATCH, and DELETE endpoints if you want to use a 2-legged authentication context. This header is optional for hub Admin GET endpoints.

## Response

### 200

A list of products associated with the user

- `pagination` (pagination, optional) — Contains pagination details for the records returned by the endpoint.
- `results` (list of userProductResponse, optional) — A list of Forma products the user is associated with.

## Errors

### 400 Bad Request Error

The request could not be understood by the server due to malformed syntax.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.
- `errors` (list of fieldError, optional) — List of field validation errors.

### 401 Unauthorized Error

Request has not been applied because it lacks valid authentication credentials for the target resource.

- `authenticationUrl` (string, optional) — The url used for authenticating the user.
- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

### 403 Forbidden Error

The server understood the request but refuses to authorize it.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

### 404 Not Found Error

The resource could not be found.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

### 406 Not Acceptable Error

The server cannot produce a response matching the list of acceptable values defined in the request.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

### 410 Gone Error

Access to the target resource is no longer available.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

### 429 Too Many Requests Error

User has sent too many requests in a given amount of time.

- `any`

### 500 Internal Server Error

An unexpected error occurred on the server.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

### 503 Service Unavailable Error

Server is not ready to handle the request.

- `status` (integer, optional) — The status code for the error.
- `type` (string, optional) — HQ defined error code for the type of error.
- `title` (string, optional) — The title of the error. ex: Bad Request.
- `detail` (string, optional) — A detailed description of the error with possible suggestions for fixing them.
- `id` (string, optional) — A unique id for the request.

## Types

### pagination

Contains pagination details for the records returned by the endpoint.

- `limit` (integer, optional, default: 20) — The maximum number of records returned per page. The last page may contain fewer records than the specified limit.
- `offset` (integer, optional, default: 0) — The index of the first record in the returned page. Used for pagination.
- `totalResults` (integer, optional) — The total number of records matching the request.
- `nextUrl` (string, optional) — The URL for the next page of records, if more results are available. Max length: 2000 characters.
- `previousUrl` (string, optional) — The URL for the previous page of records, if applicable. Max length: 2000 characters.

### userProductResponse

Information about the product associated with the user.

- `key` (enum, optional) — A machine-readable identifier for the product (e.g., docs, build). Each product has a unique key used throughout the API for identification, filtering, and integration logic (e.g., in query parameters like `filter[key]`). Possible values: Forma - `autoSpecs`, `build`, `cost`, `designCollaboration`, `docs`, `insight`, `modelCoordination`, `projectAdministration`, `formaBidBoard` and `takeoff`. BIM 360 - `assets`, `costManagement`, `designCollaboration`, `documentManagement`, `field`, `fieldManagement`, `glue`, `insight`, `modelCoordination`, `plan`, `projectAdministration`, `projectHome`, `projectManagement` and `quantification`. Note that this endpoint returns only Forma products. Other endpoints, such as [GET projects](https://aps.autodesk.com/en/docs/acc/v1/reference/http/admin-accountsaccountidprojects-GET/) and [GET projects/:projectId](https://aps.autodesk.com/en/docs/acc/v1/reference/http/admin-projects-projectId-GET/), may return both Forma and BIM 360 projects. In those responses, product keys may include BIM 360 values.
  - Allowed values: `assets`, `autoSpecs`, `build`, `buildingConnected`, `capitalPlanning`, `cost`, `costManagement`, `designCollaboration`, `docs`, `documentManagement`, `field`, `fieldManagement`, `financials`, `glue`, `workshopxr`, `insight`, `modelCoordination`, `plan`, `projectAdministration`, `projectHome`, `projectManagement`, `quantification`, `takeoff`, `info360Insight`, `info360Plant`, `info360Asset`, `info360Model`, `forma`, `datum`, `datcls`, `formaBidBoard`
- `icon` (string, optional) — The URL of the icon associated with the product.
- `name` (string, optional) — The name of the product.
- `projectIds` (list of string, optional) — The list of projects IDs where the user is associated with the product.

### fieldError

Error description for a field validation error.

- `type` (string, optional) — HQ defined error code for the type of error.
- `field` (string, optional) — Name of the field associated to the error.
- `title` (string, optional) — Title for the error.
- `detail` (string, optional) — A detailed description of the error so the user knows how to fix it.

## Examples

**Response**

```json
{
  "pagination": {
    "limit": 20,
    "offset": 10,
    "totalResults": 121,
    "nextUrl": "https://resource?limit=20&offset=30",
    "previousUrl": "https://resource?limit=20&offset=0"
  },
  "results": [
    {
      "key": "assets",
      "icon": "https://s3.us-east-1.amazonaws.com/product_icon.png",
      "name": "Document Management",
      "projectIds": [
        "3e354e66-ac8b-41dd-9bc1-93fc182c25dd"
      ]
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products"

querystring = {"fields":"name","filter[key]":"build,docs","filter[projectId]":"39712a51-bd64-446a-9c72-48c4e43d0a0d,d1163421-e7eb-4862-ac15-b33777ba42de","limit":"20","offset":"0","sort":"name"}

headers = {
    "Accept-Language": "en",
    "Region": "US",
    "User-Id": "6cc15635-2fbd-4f73-afbe-abd833408a1d",
    "Authorization": "<token>."
}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript
const url = 'https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name';
const options = {
  method: 'GET',
  headers: {
    'Accept-Language': 'en',
    Region: 'US',
    'User-Id': '6cc15635-2fbd-4f73-afbe-abd833408a1d',
    Authorization: '<token>.'
  }
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Accept-Language", "en")
	req.Header.Add("Region", "US")
	req.Header.Add("User-Id", "6cc15635-2fbd-4f73-afbe-abd833408a1d")
	req.Header.Add("Authorization", "<token>.")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Accept-Language"] = 'en'
request["Region"] = 'US'
request["User-Id"] = '6cc15635-2fbd-4f73-afbe-abd833408a1d'
request["Authorization"] = '<token>.'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name")
  .header("Accept-Language", "en")
  .header("Region", "US")
  .header("User-Id", "6cc15635-2fbd-4f73-afbe-abd833408a1d")
  .header("Authorization", "<token>.")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name', [
  'headers' => [
    'Accept-Language' => 'en',
    'Authorization' => '<token>.',
    'Region' => 'US',
    'User-Id' => '6cc15635-2fbd-4f73-afbe-abd833408a1d',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name");
var request = new RestRequest(Method.GET);
request.AddHeader("Accept-Language", "en");
request.AddHeader("Region", "US");
request.AddHeader("User-Id", "6cc15635-2fbd-4f73-afbe-abd833408a1d");
request.AddHeader("Authorization", "<token>.");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Accept-Language": "en",
  "Region": "US",
  "User-Id": "6cc15635-2fbd-4f73-afbe-abd833408a1d",
  "Authorization": "<token>."
]

let request = NSMutableURLRequest(url: NSURL(string: "https://developer.api.autodesk.com/construction/admin/v1/accounts/d73fc742-4538-401c-8d0f-853b49b750b2/users/6cc15635-2fbd-4f73-afbe-abd833408a1d/products?fields=name&filter%5Bkey%5D=build%2Cdocs&filter%5BprojectId%5D=39712a51-bd64-446a-9c72-48c4e43d0a0d%2Cd1163421-e7eb-4862-ac15-b33777ba42de&limit=20&offset=0&sort=name")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```