Skip to main content

Assets

This page documents the asset management endpoints in the VAMS API. Assets are the core entities in VAMS, representing 3D models, point clouds, CAD files, and other visual content stored within databases.

For general API information, see the API Overview. For file-level operations within assets, see Files. For asset metadata, see Metadata.

Free-text whitespace

Surrounding whitespace is removed from a submitted description before the length constraint is applied and before the value is stored, so a subsequent read returns the trimmed value. A padded value whose trimmed length falls below the documented minimum is rejected with 400. Interior whitespace is preserved.


Concepts

  • Asset: A logical container for one or more files within a database. Assets have metadata, tags, version history, and storage locations.
  • Database: A logical grouping of assets. Each database has an associated S3 bucket for storage.
  • Asset Version: A point-in-time snapshot of an asset's files. Versions are created manually or when files are uploaded.
  • Archive: Soft-deletion of an asset. Archived assets can be unarchived. Permanent deletion removes all data.

Endpoints

List Assets in Database

GET /database/{databaseId}/assets

Returns a paginated list of all assets in the specified database. By default, archived assets are excluded.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier. Pattern: ^[-_a-zA-Z0-9]{3,63}$
showArchivedquerybooleanNoWhen true, returns archived (soft-deleted) assets instead of active assets. Default: false.
maxItemsqueryintegerNoMaximum number of assets to return. Default: 30000.
pageSizequeryintegerNoPage size for pagination. Default: 3000.
startingTokenquerystringNoContinuation token from a previous response.

Response:

{
"Items": [
{
"databaseId": "my-database",
"assetId": "asset-001",
"assetName": "Building Model",
"assetType": "ifc",
"description": "Main building 3D model",
"isDistributable": true,
"tags": ["architecture", "building"],
"currentVersionId": "v1",
"assetLocation": {
"Bucket": "vams-asset-bucket",
"Key": "my-database/asset-001"
},
"previewLocation": {
"Bucket": "vams-asset-bucket",
"Key": "my-database/asset-001/preview.jpg"
},
"currentVersion": {
"Version": "v1",
"DateModified": "2024-06-15T10:30:00Z",
"Comment": "Initial upload",
"description": "",
"createdBy": "user@example.com"
},
"dateCreated": "2024-06-15T10:30:00Z",
"dateModified": "2024-06-15T10:30:00Z"
}
],
"NextToken": "eyJ..."
}

Error Responses:

StatusDescription
404Database not found.
500Internal server error.

List All Assets

GET /assets

Returns a paginated list of all assets across all databases that the user has permission to access.

Request Parameters:

ParameterLocationTypeRequiredDescription
maxItemsqueryintegerNoMaximum number of assets to return. Default: 30000.
pageSizequeryintegerNoPage size for pagination. Default: 3000.
startingTokenquerystringNoContinuation token from a previous response.

Response:

{
"Items": [
{
"databaseId": "my-database",
"assetId": "asset-001",
"assetName": "Building Model",
"assetType": "ifc",
"description": "Main building 3D model",
"isDistributable": true,
"tags": ["architecture"]
}
],
"NextToken": "eyJ..."
}

Error Responses:

StatusDescription
500Internal server error.

Create Asset

POST /assets

Creates a new asset in the specified database. This endpoint creates the asset record in DynamoDB. File uploads are handled separately through the upload endpoints.

Request Body:

{
"databaseId": "my-database",
"assetName": "New Building Model",
"description": "A detailed 3D model of the new building",
"isDistributable": true,
"tags": ["architecture", "new-building"]
}
FieldTypeRequiredDescription
databaseIdstringYesTarget database identifier.
assetNamestringYesDisplay name for the asset (1-256 characters).
descriptionstringYesAsset description (4-256 characters).
isDistributablebooleanYesWhether the asset can be downloaded.
assetIdstringNoExplicit asset identifier (2-255 characters), ASCII characters only. Cannot contain forward slashes. Auto-generated if omitted.
tagsarray[string]NoTags for categorization. Each name must resolve in the asset's database or GLOBAL, and every required tag type that has tags must be represented. See Tags.
bucketExistingKeystringNoExisting key in the database default Amazon S3 bucket to associate with the new asset.

Response:

{
"message": "Asset created successfully",
"assetId": "xd130a6d6-abcd-1234-efgh-567890abcdef"
}

Error Responses:

StatusDescription
400Invalid parameters or validation error.
403Not authorized to create assets in this database.
404Database not found.
500Internal server error.

Ingest Asset

POST /ingest-asset

Creates an asset and uploads its files in one call, combining Create Asset with the upload endpoints. Use it to bring an asset and its complete file set into VAMS without orchestrating the two APIs separately. If the asset already exists, its files are added to it; otherwise the asset is created first.

The endpoint runs in two stages against the same path, distinguished by the presence of uploadId in the request body:

  1. Initialize — describe the asset and the files to upload. The response returns an uploadId and presigned part-upload URLs.
  2. Complete — after uploading every part to its presigned URL, send the same asset fields plus the uploadId and each file's part ETags.

Both stages require PUT permission on the asset (objectType: "asset") in addition to route access.

Initialize request body

FieldTypeRequiredDescription
databaseIdstringYesTarget database identifier (4-256 characters).
assetIdstringYesAsset identifier (2-255 characters), ASCII characters only. Every file's relativeKey must begin with {assetId}/.
assetNamestringYesDisplay name for the asset (1-256 characters).
descriptionstringYesAsset description (4-256 characters).
filesarrayYesFiles to upload; at least one entry, each with a unique relativeKey.
isDistributablebooleanNoWhether the asset can be downloaded. Defaults to true.
tagsarray[string]NoTags for categorization, applied when the asset is created. See Tags.

Each entry in files is an object:

FieldTypeRequiredDescription
relativeKeystringYesRelative file path for the upload. Must begin with {assetId}/.
file_sizeintegerNoFile size in bytes. Either file_size or num_parts must be provided.
num_partsintegerNoNumber of multipart upload parts. Either file_size or num_parts must be provided.
{
"databaseId": "my-database",
"assetId": "building-model-001",
"assetName": "New Building Model",
"description": "A detailed 3D model of the new building",
"isDistributable": true,
"tags": ["architecture"],
"files": [
{
"relativeKey": "building-model-001/models/building.ifc",
"file_size": 15728640
}
]
}

Initialize response

{
"message": "Upload initialized successfully",
"uploadId": "upload-12345",
"files": [
{
"relativeKey": "building-model-001/models/building.ifc",
"uploadIdS3": "multipart-upload-id",
"numParts": 1,
"partUploadUrls": [
{
"PartNumber": 1,
"UploadUrl": "https://bucket.s3.amazonaws.com/...?X-Amz-..."
}
]
}
]
}

Complete request body

Repeat the asset fields from the initialize request, and add:

FieldTypeRequiredDescription
uploadIdstringYesIdentifier returned by the initialize stage. Its presence selects this stage.
filesarrayYesCompleted files, each with relativeKey, uploadIdS3, and a parts array of { "PartNumber", "ETag" } objects. At least one part per file.
{
"databaseId": "my-database",
"assetId": "building-model-001",
"assetName": "New Building Model",
"description": "A detailed 3D model of the new building",
"uploadId": "upload-12345",
"files": [
{
"relativeKey": "building-model-001/models/building.ifc",
"uploadIdS3": "multipart-upload-id",
"parts": [
{
"PartNumber": 1,
"ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}
]
}
]
}

Complete response

{
"message": "Multipart upload and asset ingestion completed successfully.",
"uploadId": "upload-12345",
"assetId": "building-model-001",
"fileResults": [
{
"relativeKey": "building-model-001/models/building.ifc",
"uploadIdS3": "multipart-upload-id",
"success": true
}
],
"overallSuccess": true,
"largeFileAsynchronousHandling": false
}

Error Responses:

StatusDescription
400Invalid parameters, a relativeKey that does not begin with {assetId}/, duplicate keys, a database that does not exist, or a failure creating the asset or the upload.
403Not authorized to write this asset.
500Internal server error.

Get Asset

GET /database/{databaseId}/assets/{assetId}

Retrieves detailed information about a specific asset, including version information, storage locations, and preview data.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.
showArchivedquerybooleanNoWhen true, also searches archived assets. Default: false.

Response:

{
"databaseId": "my-database",
"assetId": "asset-001",
"assetName": "Building Model",
"assetType": "ifc",
"description": "Main building 3D model",
"isDistributable": true,
"tags": ["architecture", "building"],
"bucketId": "bucket-001",
"currentVersionId": "v1",
"assetLocation": {
"Bucket": "vams-asset-bucket",
"Key": "my-database/asset-001"
},
"previewLocation": {
"Bucket": "vams-asset-bucket",
"Key": "my-database/asset-001/preview.jpg"
},
"currentVersion": {
"Version": "v1",
"DateModified": "2024-06-15T10:30:00Z",
"Comment": "Initial upload",
"description": "",
"createdBy": "user@example.com"
},
"dateCreated": "2024-06-15T10:30:00Z",
"dateModified": "2024-06-15T10:30:00Z"
}

Error Responses:

StatusDescription
403Not authorized to view this asset.
404Database or asset not found.
500Internal server error.

Update Asset

PUT /database/{databaseId}/assets/{assetId}

Updates the editable fields of an existing asset. Only the provided fields are updated; omitted fields remain unchanged.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.

Request Body:

{
"assetName": "Updated Building Model",
"description": "Updated description for the building model",
"isDistributable": false,
"tags": ["architecture", "building", "updated"]
}
FieldTypeRequiredDescription
assetNamestringNoUpdated asset name.
descriptionstringNoUpdated description.
isDistributablebooleanNoUpdated distributable flag.
tagsarray[string]NoUpdated tags (replaces existing tags). Only newly added names are checked for existence, so an asset keeps a tag that was deleted; required tag types that have tags must still be represented.

Response:

{
"success": true,
"message": "Asset updated successfully",
"assetId": "asset-001",
"operation": "update",
"timestamp": "2024-06-15T10:30:00Z"
}

Error Responses:

StatusDescription
400Invalid parameters or validation error.
403Not authorized to update this asset.
404Asset not found.
500Internal server error.

Archive Asset

DELETE /database/{databaseId}/assets/{assetId}/archiveAsset

Soft-deletes an asset by archiving it. Archived assets can be restored using the Unarchive Asset endpoint. The asset's files in S3 are archived using delete markers on the versioned bucket, and each archived file is recorded with assetArchive provenance in the file version history so a later unarchive can selectively restore them.

Reversible Operation

Archiving is a soft-delete. The asset data is preserved and can be restored. Unarchiving restores the asset record only by default; restoring the archived files is a separate opt-in (unarchiveFiles). For permanent deletion, use the Delete Asset endpoint.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.

Request Body:

An empty JSON object ({}) is sufficient. Both fields are optional.

{
"confirmArchive": true,
"reason": "Superseded by a newer model"
}
FieldTypeRequiredDescription
confirmArchivebooleanNoConfirmation flag.
reasonstringNoReason for archiving (max 256 characters).

Response:

{
"success": true,
"message": "Asset archived successfully",
"assetId": "asset-001",
"operation": "archive",
"timestamp": "2024-06-15T10:30:00Z"
}

Error Responses:

StatusDescription
400Invalid parameters or missing body.
403Not authorized to archive this asset.
404Asset not found.
500Internal server error.

Unarchive Asset

PUT /database/{databaseId}/assets/{assetId}/unarchiveAsset

Restores a previously archived asset record, making it active again. The asset's files remain archived by default. Setting unarchiveFiles to true also restores the files that the asset archive operation archived (matched by assetArchive provenance in the file version history); files archived individually before the asset archive always remain archived and can be restored with the Unarchive File endpoint. Assets archived before provenance tracking have no restorable file set, so no files are restored for them.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.
confirmUnarchivebodybooleanYesMust be true.
reasonbodystringNoReason for unarchiving.
unarchiveFilesbodybooleanNoAlso restore files archived by the asset archive. Default is false.

Response:

{
"success": true,
"message": "Asset unarchived successfully",
"assetId": "asset-001",
"operation": "unarchive",
"timestamp": "2024-06-15T10:30:00Z"
}

Error Responses:

StatusDescription
403Not authorized to unarchive this asset.
404Asset not found or not archived.
500Internal server error.

Delete Asset

DELETE /database/{databaseId}/assets/{assetId}/deleteAsset

Permanently deletes an asset, including all associated files, metadata, versions, and auxiliary data.

Irreversible Operation

This operation permanently removes the asset and all its data. It cannot be undone. Consider using Archive Asset for soft-deletion instead.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.

Request Body:

A body is required; confirmPermanentDelete must be true. An empty body returns 400.

{
"confirmPermanentDelete": true,
"reason": "Data retention period elapsed"
}
FieldTypeRequiredDescription
confirmPermanentDeletebooleanYesMust be true to confirm permanent deletion.
reasonstringNoReason for deletion (max 256 characters).

Response:

{
"success": true,
"message": "Asset deleted successfully",
"assetId": "asset-001",
"operation": "delete",
"timestamp": "2024-06-15T10:30:00Z"
}

Error Responses:

StatusDescription
400Missing body or confirmPermanentDelete not set to true.
403Not authorized to delete this asset.
404Asset not found.
500Internal server error.

Download Asset

POST /database/{databaseId}/assets/{assetId}/download

Generates presigned S3 URLs for downloading files from an asset. The URLs are time-limited and provide direct access to the files in S3. A request can target a single file (key) or multiple files of the same asset in one call (keys, up to 1,500 per request).

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.

Request Body (single file):

{
"downloadType": "assetFile",
"key": "/models/building.ifc",
"versionId": "abc123"
}

Request Body (bulk, latest versions):

{
"downloadType": "assetFile",
"keys": ["/models/building.ifc", "/textures/wall.png"]
}

Request Body (bulk, per-file versions):

{
"downloadType": "assetFile",
"keys": [{ "key": "/models/building.ifc", "versionId": "abc123" }, "/textures/wall.png"]
}
FieldTypeRequiredDescription
keystringNoRelative file path within the asset. If omitted, the asset's primary file is used. Mutually exclusive with keys.
keys(string|object)[]NoFiles to generate URLs for in bulk (max 1,500 per request; assetFile only). Each entry is a path string (latest) or {key, versionId}. Mutually exclusive with key.
versionIdstringNoS3 version ID for a single key. Cannot be combined with keys (put versions on individual keys instead).
assetVersionIdstringNoVAMS asset version ID. Resolves the S3 version from the version snapshot for all requested file(s).
assetVersionIdAliasstringNoNamed version alias. Resolves to an asset version ID, then to the S3 version.
Version Resolution and Exclusivity

Version resolution is applied per file: assetVersionId/assetVersionIdAlias pins all files to that asset version snapshot; otherwise a per-file versionId (or the single versionId for a single key) selects that S3 version; with no version specified the latest file version is returned. Only one of versionId, assetVersionId, or assetVersionIdAlias can be specified at the request level. Per-file versionIds in keys cannot be combined with assetVersionId/assetVersionIdAlias. Version parameters are not allowed for asset preview downloads, and key/keys are mutually exclusive.

Response (single file):

{
"downloadUrl": "https://vams-asset-bucket.s3.amazonaws.com/...?X-Amz-...",
"expiresIn": 86400,
"downloadType": "assetFile",
"versionId": "abc123",
"files": null,
"message": "Download URL generated successfully"
}

Response (bulk):

{
"downloadUrl": "https://vams-asset-bucket.s3.amazonaws.com/...?X-Amz-...",
"expiresIn": 86400,
"downloadType": "assetFile",
"files": [
{
"key": "/models/building.ifc",
"downloadUrl": "https://vams-asset-bucket.s3.amazonaws.com/...?X-Amz-...",
"versionId": "abc123",
"success": true,
"error": null
},
{
"key": "/textures/missing.png",
"downloadUrl": null,
"versionId": null,
"success": false,
"error": "File not found in S3"
}
],
"message": "Generated 1 of 2 download URLs. Warning: 1 file path(s) do not exist or are not downloadable and were skipped."
}

Bulk requests return one entry per requested key. File paths that do not exist or are not downloadable are skipped (reported with success: false and an error reason, plus a warning in message); the request fails with 400 only when no URL can be generated at all. The top-level downloadUrl carries the first successful URL for compatibility with single-URL consumers.

Error Responses:

StatusDescription
400Invalid parameters, multiple version parameters specified, key/keys combined, over 1,500 keys, no URLs generatable, version parameters used with preview downloads, or asset is not distributable.
403Not authorized to download this asset.
404Database, asset, version, or file not found.
410The requested file version has been archived and cannot be downloaded.
500Internal server error.

Export Asset

POST /database/{databaseId}/assets/{assetId}/export

Exports comprehensive asset data including the asset hierarchy (child relationships), metadata, files, versions, and relationships. Supports pagination for large asset trees and optional response compression.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesRoot asset identifier.

Request Body:

{
"generatePresignedUrls": false,
"includeFolderFiles": false,
"includeOnlyPrimaryTypeFiles": false,
"includeFileMetadata": true,
"includeAssetLinkMetadata": true,
"includeAssetMetadata": true,
"fetchAssetRelationships": true,
"fetchEntireChildrenSubtrees": false,
"includeParentRelationships": false,
"includeArchivedFiles": false,
"fileExtensions": [".pdf", ".jpg"],
"maxAssets": 100,
"maxFiles": 2000,
"startingToken": null
}
FieldTypeDefaultDescription
generatePresignedUrlsbooleanfalseGenerate presigned S3 URLs for file downloads.
includeFolderFilesbooleanfalseInclude folder markers in file listings.
includeOnlyPrimaryTypeFilesbooleanfalseInclude only files with primaryType metadata set.
includeFileMetadatabooleantrueInclude file-specific metadata.
includeAssetLinkMetadatabooleantrueInclude asset link relationship metadata.
includeAssetMetadatabooleantrueInclude asset-level metadata.
fetchAssetRelationshipsbooleantrueFetch asset relationships. When false, returns only the root asset.
fetchEntireChildrenSubtreesbooleanfalseFetch complete child tree hierarchy instead of one level.
includeParentRelationshipsbooleanfalseInclude parent relationships in the relationship data.
includeArchivedFilesbooleanfalseInclude archived files in export.
fileExtensionsarray[string]--Filter files to specified extensions only.
maxAssetsinteger100Maximum assets per page (minimum 1, maximum 1000).
maxFilesinteger2000Maximum files per page across all of the page's assets (1-10000).
startingTokenstring--Pagination token from a previous response.

Response:

{
"assets": [
{
"is_root_lookup_asset": true,
"databaseid": "my-database",
"assetid": "asset-001",
"assetname": "Building Model",
"assettype": "ifc",
"description": "Main building",
"isdistributable": true,
"tags": ["architecture"],
"archived": false,
"metadata": { ... },
"files": [ ... ],
"files_truncated": false
}
],
"relationships": [ ... ],
"totalAssetsInTree": 5,
"assetsInThisPage": 5,
"NextToken": null
}
A Large Asset Is Returned Over Several Pages

maxFiles bounds the files one page returns across all of its assets, so a page can end before maxAssets assets when the budget runs out. An asset holding more files than the budget is returned over successive pages: its entry sets files_truncated to true, and NextToken resumes that asset's file list where the page stopped rather than moving on to the next asset.

The same asset therefore appears on more than one page, each entry carrying a different part of its files. A client that accumulates pages merges an asset's files on its databaseid and assetid instead of appending a second entry for it; vamscli assets export --auto-paginate does this. The budget bounds one request and never limits what an export can retrieve.

Large Export Payloads Are Delivered by Presigned URL

A serialized payload of 100KB or less is returned inline with status 200, as shown above. A larger payload is staged as a JSON object in the VAMS auxiliary Amazon S3 bucket, and the endpoint responds with status 303 redirecting to a presigned URL for it:

{
"message": "Export payload exceeds the inline response size and is available at the redirect target",
"presignedExportPayloadUrl": "https://<auxiliary-bucket>.s3.<region>.amazonaws.com/assetExports/...",
"presignedExportPayloadExpiresIn": 3600
}

The presigned URL is also returned in the Location header. Fetching it yields exactly the response body documented above, so a client that follows redirects — which most HTTP clients, including the VAMS CLI, do by default — sees no difference between the two cases. Clients that disable redirect following must read presignedExportPayloadUrl from the body and request it separately.

Two constraints apply to the redirect target:

  • Send no Authorization header to the presigned URL. It carries its own authorization in the query string, and Amazon S3 rejects a request presenting two authorization mechanisms. Standard clients strip the header automatically on a cross-host redirect.
  • Issue a GET, not a POST. The status is 303 rather than 307 precisely so that redirect-following clients switch the method; the URL is signed for a GET and rejects any other verb.

presignedExportPayloadExpiresIn reports the URL lifetime in seconds, taken from the deployment's presigned-URL timeout. Request the payload before it elapses.

Error Responses:

StatusDescription
400Invalid parameters.
403Not authorized to export this asset.
404Asset not found.
500Internal server error.

Get Asset History

GET /database/{databaseId}/assets/{assetId}/assetHistory

Returns the lifecycle history records for an asset, newest first. Each record captures one lifecycle operation (create, edit, archive, unarchive, or permanent delete) with the acting user, the origin of the change, and an open-schema snapshot of the asset fields as they stood after the operation.

History records persist across permanent deletion. If an asset is permanently deleted and later recreated with the same asset ID, the prior history (including the permanentDelete record) is returned again for that ID. When no asset record exists (live or archived) for the ID, the endpoint returns 404.

Request Parameters:

ParameterLocationTypeRequiredDescription
databaseIdpathstringYesDatabase identifier.
assetIdpathstringYesAsset identifier.
pageSizequeryintegerNoMaximum records per page (1-1000, default 100).
startingTokenquerystringNoContinuation token from a previous response's NextToken.

Change Sources:

ValueOperation
createAsset created through the VAMS API.
createDirectAsset auto-created by S3 bucket-sync ingestion.
editAsset fields updated.
archiveAsset archived.
unarchiveAsset unarchived through the VAMS API.
unarchiveDirectAsset auto-restored by S3 bucket-sync ingestion.
permanentDeleteAsset permanently deleted.

Response:

{
"message": "Success",
"Items": [
{
"historyRecordId": "2026-07-05T14:23:01.123456Z#a1b2c3d4",
"databaseId": "my-database",
"assetId": "my-asset",
"recordDate": "2026-07-05T14:23:01.123456Z",
"changeSource": "edit",
"changeUserId": "user@example.com",
"assetSnapshot": {
"assetName": "My Asset",
"description": "Updated description",
"isDistributable": true,
"tags": ["tag1"],
"bucketId": "xbucket1",
"assetLocationKey": "my-asset/"
}
},
{
"historyRecordId": "2026-07-01T09:00:00Z#migrated",
"databaseId": "my-database",
"assetId": "my-asset",
"recordDate": "2026-07-01T09:00:00Z",
"changeSource": "create",
"changeUserId": "SYSTEM_USER",
"assetSnapshot": { "assetName": "My Asset" },
"migratedRecord": true
}
],
"NextToken": "eyJkYXRhYmFzZUlkOmFzc2V0SWQiOiAi..."
}

The assetSnapshot object is open-schema: snapshot fields may grow over time, and consumers should render whatever keys are present. Archive and unarchive records include archivedReason/unarchivedReason in the snapshot when a reason was provided. Records with migratedRecord: true were backfilled by the deployment data migration from inferred data.

Error Responses:

StatusDescription
400Invalid parameters or pagination token.
403Not authorized to view this asset's history.
404Asset not found.
500Internal server error.