Resources

API – Metadata Upload

API Base URL: https://api-platform.vntana.com

For a collection of all Endpoints in our Admin API, view the documentation here. To view the collection of Public Endpoints, view the documentation hereNote: In the following guide and aforementioned documentation, you will see referenced in numerous Endpoints something called a Client. This refers to the folders one can create on the Platform within an Organization. The Client nomenclature is a legacy reference being replaced with Folder. These will be referenced as Client / Folder in all guides.

Uploading Metadata in a File

The VNTANA Metadata App allows users to upload metadata for their Assets using a CSV/XLSX file. Like with Report Generation, this is accomplished via endpoints which are accessible outside the App and can be directly implemented into any automation pipeline. There are a number of finer details to consider when setting up your data to ensure there are no unintentional results.

The process to upload data is a 2-3 step process, starting with a call to upload your data as a file, either in XLSX or CSV format, using multipart/form-data.

1
2
3
Method: POST
Endpoint: /v1/product-data/upload
Headers: { x-auth-token : ‘Bearer ‘ + refreshToken }

A successful upload will return the following response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
“success”: true,
“errors”: [],
“response”: {
“blobId”: “id.csv”,
“blobSize”: 21498,
“metaData”: {
“content-length”: “21498”,
“content-type”: “text/csv”,
“original-name”: “myfile.csv”,
“creator-uuid”: “”,
“requested-ip”: “”,
“created-date”: “2023-10-15 23:11:49”
}
}
}

The blobId will be needed when using the next request. Once you’ve uploaded the file, you need to tell our BE how the file should be read, otherwise the data within doesn’t mean anything. To do this, you can call the following endpoint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Method: POST
Endpoint: /v1/product-data/import
Headers: { x-auth-token: ‘Bearer ‘ + refreshToken }
Body: {
“blobId”:”ID.csv”,
“importType”: “CREATE_EMPTY_PRODUCTS”,
“columnsInfo”:[
{
“index”: 0,
“parameter”: “PRODUCT_NAME”,
“isFilter”: true
},
{
“index”: 1,
“parameter”: “TAG”,
“isFilter”: false
},
{
“index”: 2,
“parameter”: “CLIENT_UUID”,
“isFilter”: false
}
{
“index”: 3,
“parameter”: “ATTRIBUTE”,
“isFilter”: false
},
{
“index”: 5,
“parameter”: “TAG”,
“isFilter”: false
},
{
“index”: 5,
“parameter”: “DESCRIPTION”,
“isFilter”: false
}
]
}

The data in this request will allow the BE to identify the purpose of each column, as well as where to find/create the Asset it pertains to on the Platform. To begin, you must pass the blobId returned from the uploading of the file containing your data. There is nothing more to this parameter. The other two require a bit more explanation.

Before diving into the other parameters, the expected response for this request looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
“success”: true,
“errors”: [],
“response”: {
“blobId”: null,
“reportSize”: null,
“reportName”: “”,
“contentType”: “text/csv”,
“signedUrl”: null,
“generationUuid”: “”,
“status”: “IN_PROGRESS”
}
}

The generationUuid can be used to track the progress of the upload as well as retrieve any errors that may occur in the processing of the data. More on this below.

Setting the importType

The parameter importType tells the BE what you want to happen with the data. The data can be used to either create empty Assets containing any metadata you pass, or update existing Assets with the data. The possible values for this parameter are:

  • CREATE_EMPTY_PRODUCTS – This indicates that all we are requesting is to create new Assets with our data. There is no need to search for Assets.

    • This carries the requirement that your data includes the Asset Name and a Workspace identifier, either slug or uuid. Otherwise it can’t know where the Asset should be created.

  • ADD_OR_UPDATE_METADATA – This is one of two approaches to updating existing Assets. This option can be considered a lossless update, meaning it will only impact data fields you tell it to, everything else that may already exist on the Asset will be retained.

    • To illustrate this, if an Asset has the tag Blue and attribute Width: 10m, and you use this method to add the tag Green and attribute Width: 12m, what you will see is an updated Asset with the tags Blue, Green and the attribute Width: 12m. In this case, because the attribute Width can only exist once on an Asset, it replaces the value with the new value, but for tags, there is no replacing since they don’t share a key and are just unique entities.

  • OVERWRITE_METADATA – This is one of two approaches to updating existing Assets. This option is a lossy operation, it will effectively wipe any metadata on the Asset and then add the new data you have passed.

    • Using the same example as above, the result would instead be the tag Green and attribute Width: 12m. The attribute behaves the same since they share a key, but in this case the tag Blue isn’t retained as it doesn’t exist in the new data.

With both update options, there is the possibility to also create new Assets if no match can be found. This can a bit confusing at first as it relates to the columnsInfo parameter which will be discussed in more detail below. In order to create Assets when no match is found, the same requirements as normal creation are present, the Asset Name must be included with either the Workspace Slug or UUID. For just updating, you must have either the Asset Name or UUID and either the Workspace Slug or UUID.

Setting the columnsInfo Parameter

The columnsInfo parameter links your metadata columns to specific data fields, telling the BE what your data means. This parameter takes a list of objects that contain the following information:

  • index – The index for the column in the data. Index starts at 0.

  • parameter – The type of data the column contains. See below for a full list of all possible parameters.

  • isFilter – Used by the BE to identify the column as being used as an Asset identifier. This can be tricky to use, but essentially there are only four parameters that may need to have this set to true:

    • PRODUCT_NAME

    • PRODUCT_UUID

    • CLIENT_SLUG

    • CLIENT_UUID

Examples

To better illustrate how to use these parameters with your data, we will go through some examples using the following data.

PRODUCT_NAMEPRODUCT_UUIDCLIENT_SLUGCLIENT_UUIDTAG
Example 11234workspace15678Blue
Example 24321workspace28765Green
Example 3


workspace15678Yellow
Example 4



8765Orange
Example 5


workspace2


Red

The above data contains a few examples of Assets that need to have a tag applied to them. These can be handled differently depending on how you set up the request to handle this data. Let’s start with the simplest scenario which is just creating Assets.

CREATE_EMPTY_PRODUCTS

Using the above data when simply creating empty assets, you can structure your request body two ways:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
‘blobId’ : ‘id.csv’,
‘importType’ : ‘CREATE_EMPTY_PRODUCTS’,
‘columnsInfo’ : [
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : true
},
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : false
},
{
‘index’ : 2,
‘parameter’ : ‘CLIENT_SLUG’,
‘isFilter’ : true
},
{
‘index’ : 4,
‘parameter’ : ‘TAG’,
‘isFilter’ : false
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
‘blobId’ : ‘id.csv’,
‘importType’ : ‘CREATE_EMPTY_PRODUCTS’,
‘columnsInfo’ : [
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : true
},
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : false
},
{
‘index’ : 3,
‘parameter’ : ‘CLIENT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 4,
‘parameter’ : ‘TAG’,
‘isFilter’ : false
}
]
}

Note that in both options, the PRODUCT_UUID is ignored. When simply creating Assets, there is no need for Asset UUID’s as they would only refer to already existing Assets. If you were to include this column and set isFilter to true, it would prevent the creation of the rows that contain a value for the UUID.

You can add both the Workspace slug and UUID, just note that one of them has to have isFilter set to true for the creation to work, and if both are set to true then CLIENT_UUID will take precedence.

Based on the above data, using either payload will result in all rows being created as new Assets with the tag in their row.

The standout aspect of the data is the duplicate inclusion of the PRODUCT_NAME parameter covering both options for isFilter. This behavior indicates that an Asset should be created, but then what is the purpose of CREATE_EMPTY_ASSETS as the importType? This requirement exists to allow for creating assets when using one of the update importType’s and is more a side-effect in the case of just wanting to create assets. The importType acts more to indicate factors such as whether there is a need to search for existing assets or whether to retain old data or not.

ADD_OR_UPDATE_METADATA and OVERWRITE_METADATA

The payload options are the same for both update options but the setup of the payload starts to differ depending on what you wish to see happen. A simple update would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
‘blobId’ : ‘id.csv’,
‘importType’ : ‘ADD_OR_UPDATE_METADATA’,
‘columnsInfo’ : [
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : true
},
{
‘index’ : 3,
‘parameter’ : ‘CLIENT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 4,
‘parameter’ : ‘TAG’,
‘isFilter’ : false
}
]
}

Just like above, we identify the product and workspace as filters and indicate the column indexes of the data. The only change here is the importType. This will use the PRODUCT_NAME and CLIENT_UUID to locate the correct assets to update. From the example data, we will see the following effects:

  • Example 1 – Updated with tag Blue

  • Example 2 – Updated with tag Green

  • Example 3 – Updated with tag Yellow

  • Example 4 – Updated with tag Orange

  • Example 5 – Skipped

Note: These are based on the assumption that the assets do in fact exist with the exact same name in the identified workspace. If there does not exist an Asset named Example 1 in the Workspace with UUID 5678 then that row would be skipped as we did not indicate that it should be created.

Example 5 is skipped because it does not contain a value for CLIENT_UUID and thus cannot be properly located. An error would be generated which can be downloaded, more on this below in the Status section. The others would be updated so long as there exists some Asset(s) with the same exact name in the indicated workspace. Keep in mind, when using the name to identify an Asset, it is not unique and can match to multiple Assets, in which case all matches will be updated.

Now, if we were to change the payload to instead use PRODUCT_UUID as the identifier for the Asset, only Example 1 and Example 2 would be updated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
‘blobId’ : ‘id.csv’,
‘importType’ : ‘ADD_OR_UPDATE_METADATA’,
‘columnsInfo’ : [
{
‘index’ : 1,
‘parameter’ : ‘PRODUCT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 3,
‘parameter’ : ‘CLIENT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 4,
‘parameter’ : ‘TAG’,
‘isFilter’ : false
}
]
}

The rest of the rows are ignored because we neither provide an Asset UUID value or indicate that Assets should be created if not identified. In this example, we could swap CLIENT_UUID for CLIENT_SLUG (and update the index accordingly) and have the same result as both of these values are present for the only two rows that have a UUID provided.

Now, what if you wish to create any Assets that cannot be identified? To do this we have to add an additional object to the columnsInfo field, but before that let’s revisit what is required for creating assets:

  • Asset Name must be provided, otherwise the API won’t know what to call the new Asset.

  • Either Workspace UUID or Slug must be provided. This is true for any of these operations.

To construct a payload based on the above example data that will create any Assets a match cannot be found for, you can pass the following data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
‘blobId’ : ‘id.csv’,
‘importType’ : ‘ADD_OR_UPDATE_METADATA’,
‘columnsInfo’ : [
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : true
},
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : false
}
{
‘index’ : 3,
‘parameter’ : ‘CLIENT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 4,
‘parameter’ : ‘TAG’,
‘isFilter’ : false
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
‘blobId’ : ‘id.csv’,
‘importType’ : ‘ADD_OR_UPDATE_METADATA’,
‘columnsInfo’ : [
{
‘index’ : 1,
‘parameter’ : ‘PRODUCT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 0,
‘parameter’ : ‘PRODUCT_NAME’,
‘isFilter’ : false
}
{
‘index’ : 3,
‘parameter’ : ‘CLIENT_UUID’,
‘isFilter’ : true
},
{
‘index’ : 4,
‘parameter’ : ‘TAG’,
‘isFilter’ : false
}
]
}

The key here is the inclusion of the object identifying the PRODUCT_NAME column with isFilter set to false. As was mentioned when covering the CREATE_EMPTY_ASSET importType, the inclusion of PRODUCT_NAME with isFilter set to false indicates that a creation operation should happen. As we are using one of the update importType’s it will only execute this operation if no matching asset is found to update.

If we were to use the PRODUCT_NAME payload, we would see only Example 5 skipped once again as we do not provide a Workspace UUID for that row. If we swapped CLIENT_UUID for CLIENT_SLUG in our columnsInfo, then we would see Example 4 skipped instead of Example 5.

If we make the assumption that we do not have PRODUCT_UUID’s for some because they don’t actually exist yet, then we would see the following results regardless of which payload is used:

  • Example 1 – Updated with tag Blue

  • Example 2 – Updated with tag Green

  • Example 3 – Not found, thus created in the Workspace with UUID 5678 with the tag Yellow.

  • Example 4 – Not found, thus created in the Workspace with UUID 8765 with the tag Orange.

  • Example 5 – Skipped, no workspace identifier.

List of Parameters

The full list of parameters for the columnsInfo can be found below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PRODUCT_NAME
PRODUCT_UUID
CLIENT_SLUG
CLIENT_UUID
TAG
ATTRIBUTE
DESCRIPTION
VARIANT_NAME
VARIANT_UUID
PROJECT_NAME
PROJECT_UUID
OPTIMIZATION_PRESET_NAME
OPTIMIZATION_PRESET_UUID
VIEWER_PRESET_NAME
VIEWER_PRESET_UUID

A couple of notes on some of these parameters:

  • PROJECT_NAME and PROJECT_UUID refer to a single Project identifier to add the Asset to. If no match is found with PROJECT_NAME (not UUID) then it will create the project.

  • VARIANT_NAME and VARIANT_UUID refer to a single Variant identifier to add the Asset to. If no match is found with VARIANT_NAME (not UUID) then if will create the Variant Group.

  • For ATTRIBUTES, the key of the attribute will be the entire header for the column. So if you have a column titled Width (m) then the attribute will appear on assets as Width (m) : value.

  • Both Optimization and Viewer presets have to have unique names so those can be used over UUID as it is easier to obtain, however keep in mind they may have been set up as only Workspace level and can’t be applied to assets in another workspace.

Upload Status and Error Reports

After uploading your file and importing the definition using the above endpoints, you can track the progress of the upload and generate an error report if any occur. To check the status of you will need the generationUuid returned from the successful use of the /v1/product-data/import method:

1
2
3
Method: GET
Endpoint: /v1/product-data/import/{generationUuid}/status
Headers: { x-auth-token : ‘Bearer ‘ + refreshToken }

The status will be shown in the response like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
“success”: true,
“errors”: [],
“response”: {
“errors”: [
{
“lineNumber”: 1,
“error”: “Error occurred while trying to create product with name [product 1]: [[500] during [POST] to [http://localhost:8081/bulk/products] [ProductBulkResourceClient#create(ProductBulkCreationRequest)]: [Error occurred having uuid – b8707e22-3260-4977-9875-094c76a0c60a]]”
},
{
“lineNumber”: 2,
“error”: “Error occurred while trying to create product with name [product 2]: [[500] during [POST] to [http://localhost:8081/bulk/products] [ProductBulkResourceClient#create(ProductBulkCreationRequest)]: [Error occurred having uuid – b8707e22-3260-4977-9875-094c76a0c60a]]”
},
{
“lineNumber”: 3,
“error”: “Error occurred while trying to create product with name [null]: [[500] during [POST] to [http://localhost:8081/bulk/products] [ProductBulkResourceClient#create(ProductBulkCreationRequest)]: [Error occurred having uuid – b8707e22-3260-4977-9875-094c76a0c60a]]”
},
{
“lineNumber”: 4,
“error”: “Error occurred while trying to create product with name [product 4]: [[500] during [POST] to [http://localhost:8081/bulk/products] [ProductBulkResourceClient#create(ProductBulkCreationRequest)]: [Error occurred having uuid – b8707e22-3260-4977-9875-094c76a0c60a]]”
}
],
“blobId”: “”,
“reportSize”: null,
“reportName”: “”,
“contentType”: “application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”,
“signedUrl”: null,
“generationUuid”: “”,
“status”: “COMPLETED”,
“result”: null
}
}

The status will show IN_PROGRESS while still processing, and COMPLETED when it is done, regardless of any errors. The errors field of the response will give a list containing any errors that occurred in the processing of the data, indicating the lineNumber the error occurred on. This lineNumber is the row in the original data with 0 referring to the header row (so lineNumber 1 is the first row of actual data). The errors may not always be something that clearly makes sense when comparing to the original data, if you are unsure what the issue is you can contact our support with the error and original data and we can assist.

Additional Information

There are a number of smaller details about how these endpoints will behave versus what the App allows that warrant acknowledgement here.

  • When including columns for attributes, the default behavior of the endpoint is to skip any empty cell in an attribute column. This means, if you have a column for the Width attribute but a specific row does not have a value for this, the endpoint will simply not apply that attribute to the Asset in question.

    • The App gives the option to include a placeholder value of ‘-‘ for any empty attribute values. This is a feature only handled by the App itself by filling in the placeholder into the data before uploading it. The endpoint does not do this.

  • The App gives the user the option of using prefixes in their column headers to auto-identify a column of your data for the columnsInfo field. This is entirely handled by the App and has nothing to do with these endpoints, the API will not look for or remove these prefixes from your data.

  • The update functionality does not provide the ability to rename Assets.

On This Page

Accelerate Your
Digital Transformation

Learn how our platform can automate your 3D process.

Tap the magnifying glass to the left of your screen to search our resources.