Marble 2 beta

Assets

Assets are project-owned media records used for task inputs and outputs. They are isolated to the API key's project and can contain one object or typed parts such as RGB, mask, and depth.

text
CREATED -> UPLOADING -> READY                         |                         v                      DELETED

Use assets.create for ingestion, assets.get for metadata, download URLs for private bytes, and assets.delete for cleanup.

Choose an ingestion path

Fetch a public URL

The platform can fetch a public HTTPS URL. A successful create returns a READY asset.

bash
curl --fail-with-body --silent --show-error \  --request POST "$WLT_API_BASE_URL/assets" \  --header "WLT-Api-Key: $WLT_API_KEY" \  --header "Content-Type: application/json" \  --data '{    "asset": {      "displayName": "room.jpg",      "url": "https://example.com/room.jpg"    }  }'

Use this for stable public media, not expiring links or private origin URLs.

Send small inline media

Set asset.base64 for media smaller than 10 MiB decoded. A data URL prefix is optional. Prefer upload URLs for larger files so bytes do not pass through the JSON API.

Upload one object

Create an asset record without url, base64, or parts, then create an upload URL:

bash
curl --request POST "$WLT_API_BASE_URL/assets/${WLT_ASSET_ID}:createUploadUrl" \  --header "WLT-Api-Key: $WLT_API_KEY" \  --header "Content-Type: application/json" \  --data '{ "contentType": "image/png", "contentLength": 248901 }'

Send the bytes to the returned uploadUrl using its exact method and every returned header. Do not send WLT-Api-Key to the upload URL. After the upload succeeds, call POST /assets/{asset}:completeUpload.

Upload typed parts

Declare every part while creating the asset:

json
{  "asset": {    "displayName": "masked-view-01",    "type": "image",    "parts": [      { "role": "rgb", "contentType": "image/png", "contentLength": 248901 },      { "role": "mask", "contentType": "image/png", "contentLength": 18220 }    ]  }}

The create response includes one upload grant per part. Use :createUploadUrls to create new grants for all parts or selected roles. Call :completeUpload only after every declared part is present.

Choose a download method

Asset metadata does not include a permanent download URL. Choose the narrowest short-lived credential that fits the job:

NeedEndpointHow to download
One asset, or a few independent downloadsPOST /assets/{asset}:createReadUrlFetch the returned readUrl directly.
Many assets from one projectPOST /assets:createReadPrefixCombine the prefix with each asset's contentPath.

Fetch either result directly, without adding WLT-Api-Key.

Download one asset

Create a self-contained URL:

bash
curl --request POST "$WLT_API_BASE_URL/assets/${WLT_ASSET_ID}:createReadUrl" \  --header "WLT-Api-Key: $WLT_API_KEY"

The returned readUrl works until expiresAt; create a fresh URL after that. This is the simplest option and limits access to one asset.

Download many project assets

Create one prefix credential when an application needs to read several assets from the same project:

bash
curl --fail-with-body --silent --show-error \  --request POST "$WLT_API_BASE_URL/assets:createReadPrefix" \  --header "WLT-Api-Key: $WLT_API_KEY" \  > read-prefix.json
curl --fail-with-body --silent --show-error \  --request GET "$WLT_API_BASE_URL/assets/$WLT_ASSET_ID" \  --header "WLT-Api-Key: $WLT_API_KEY" \  > asset.json
BASE_URL=$(jq -er '.baseUrl' read-prefix.json)CONTENT_PATH=$(jq -er '.contentPath' asset.json)SIGNED_QUERY=$(jq -er '.signedQuery' read-prefix.json)
curl --fail --output asset.bin \  "${BASE_URL}${CONTENT_PATH}?${SIGNED_QUERY}"

Use the response fields as follows:

  • baseUrl is the project content prefix and already ends in /;
  • contentPath is the asset's already percent-encoded path beneath that prefix; append it unchanged. The asset's url is baseUrl + contentPath for the requester's web name; it carries no access by itself, so append signedQuery to it the same way;
  • signedQuery is opaque, already encoded, and has no leading ?; append it unchanged, and do not parse or edit it;
  • expiresAt tells you when to create a new prefix.

One prefix authorizes reads across the API key's whole project, so prefer a single-asset read URL when that broader access is unnecessary. contentPath and url are null before the asset is READY; if they are null for a ready asset, fall back to :createReadUrl.

Use assets in tasks

Keep the short ID from the final segment of the resource name:

json
{  "imageAsset": { "assetId": "asset_example" }}

The asset must be READY and belong to the same project as the API key.

Read a task's output assets

Every asset a task returns names itself twice:

json
{  "imageAsset": {    "assetId": "asset_generated_view",    "url": "https://example.com/generated-view.png"  }}

Download from url within 24 hours. Keep assetId for anything after that: create a fresh read with :createReadUrl, or send it straight back as the next task's input. Sending the ID is also the cheaper input: a URL the platform has to go and fetch is stored again as a new asset.

List, inspect, and delete

text
GET    /api/v2/assets?pageSize=50&pageToken=...GET    /api/v2/assets/{asset}DELETE /api/v2/assets/{asset}

List returns all non-deleted states by default, including incomplete uploads. Use GET /api/v2/assets?state=READY to list usable assets, or filter by CREATED or UPLOADING to find incomplete uploads. Deleted assets remain excluded, so state=DELETED returns an empty list.

State filtering happens before pagination. List responses include an opaque nextPageToken; pass it back unchanged and keep the same state filter on each page. The service caps page size, so always handle pagination. Deletion is soft and makes later reads return 404. Do not reuse a deleted asset ID.

The API reference is the source of truth for supported asset types, part roles, metadata, and response fields.