> For the complete documentation index, see [llms.txt](https://umber.gitbook.io/umber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://umber.gitbook.io/umber/api-reference-v2/virtual-assets.md).

# Virtual Assets API

A [Virtual Asset](/umber/concepts/virtual-assets.md) is a named container in your domain — a logo, a hero image, a 3D model, whatever you're shipping. It doesn't hold content directly; each version uploaded under it does. This reference covers creating, reading, searching, updating and deleting virtual asset containers themselves, plus two read-only sub-resources (deployed URLs and version count). Versions — uploading actual files — are a separate API and reference ([Asset Versions API](/umber/api-reference-v2/virtual-assets/versions.md)); deploying them per [environment](/umber/concepts/environments.md) is a third ([Version Deployment API](/umber/api-reference-v2/virtual-assets/version-deployment.md)).

{% hint style="info" %}
This is a low-level HTTP reference. If you're onboarding an existing project's assets in bulk, the [Umber CLI](/umber/cli-tools/umber-cli.md) can migrate them for you — see [Migrating Assets with the CLI](/umber/guides/migrating-assets-with-cli.md). For creating a single asset through the dashboard, see [Creating a Virtual Asset](/umber/guides/creating-a-virtual-asset.md).
{% endhint %}

## Base URL and authentication

```
https://api.umbercloud.io/api/v2/{domainId}/va
```

`{domainId}` is always a path segment — there's no header or query-param fallback for it.

Authenticate with this header:

| Header          | Who this is for                     | Notes                                                                              |
| --------------- | ----------------------------------- | ---------------------------------------------------------------------------------- |
| `apikey: <key>` | Third-party / customer integrations | A key scoped to your domain. This is the credential most API consumers should use. |

If it's not present, or the key doesn't check out, every endpoint below returns `403`.

## The virtual asset object

| Field                     | Type                                                                         | On create                                                                 | On update                                                                                                                                                                 | Returned in responses                         |
| ------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `assetId`                 | string, ≤ 64 chars                                                           | required, must be unique in the domain                                    | not accepted — permanent once created                                                                                                                                     | yes                                           |
| `assetName`               | string, ≤ 64 chars                                                           | required                                                                  | optional                                                                                                                                                                  | yes                                           |
| `description`             | string, ≤ 400 chars                                                          | optional                                                                  | optional                                                                                                                                                                  | **no** — accepted as input, never echoed back |
| `assetType`               | string (an [asset type](/umber/concepts/asset-types.md) code, e.g. `"0001"`) | required, must match an active asset type in your domain                  | required, must match an active asset type — the value itself can't actually be changed once set, but the field must still be present and valid for the request to succeed | yes                                           |
| `secured`                 | boolean                                                                      | optional, defaults to `true`                                              | not accepted — permanent once created                                                                                                                                     | yes                                           |
| `localCacheControl`       | boolean                                                                      | optional, defaults to `false`                                             | optional                                                                                                                                                                  | yes                                           |
| `localCacheMaxAge`        | integer, 1–525600                                                            | optional, defaults to `30`                                                | optional                                                                                                                                                                  | yes                                           |
| `createdBy`               | string (email)                                                               | required — must be the email of an active admin/owner user in your domain | not accepted                                                                                                                                                              | yes                                           |
| `updatedBy`               | string (email)                                                               | optional, defaults to `createdBy`                                         | required — same admin/owner requirement                                                                                                                                   | yes                                           |
| `active`                  | boolean                                                                      | set to `true` automatically                                               | optional                                                                                                                                                                  | yes                                           |
| `createdAt` / `updatedAt` | ISO 8601 timestamp                                                           | set automatically                                                         | set automatically                                                                                                                                                         | yes                                           |

## Errors

Every error is a small JSON body:

```json
{
  "type": "https://docs.umbercloud.io/errors/asset-service",
  "title": "No Asset found for hero-skin-01",
  "status": 404,
  "detail": "No Asset found for hero-skin-01"
}
```

| Status | Meaning                                                                                                                                                                                    |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400`  | A required field is missing or invalid — e.g. `createdBy`/`updatedBy`/`deletedBy` not supplied, `localCacheMaxAge` out of range — or you tried to delete an asset that still has versions. |
| `403`  | Missing, invalid, or mismatched credentials.                                                                                                                                               |
| `404`  | No virtual asset exists for the given `assetId`, or `assetType` doesn't match a known, active asset type in your domain.                                                                   |
| `409`  | `assetId` is already in use.                                                                                                                                                               |
| `500`  | Unexpected server error.                                                                                                                                                                   |

## Pagination

The list-style endpoints share this envelope:

```json
{
  "totalResults": 137,
  "pageNumber": 1,
  "pageSize": 50,
  "results": []
}
```

`pageNumber` (default `1`) and `pageSize` (default `50`) are query params.

***

## Create a virtual asset

`POST /api/v2/{domainId}/va`

{% tabs %}
{% tab title="JSON" %}

```json
{
  "assetId": "hero-skin-01",
  "assetName": "Hero Skin 01",
  "assetType": "0001",
  "createdBy": "dev@acme-games.com",
  "description": "Default hero skin",
  "localCacheMaxAge": 60
}
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl -X POST "https://api.umbercloud.io/api/v2/acme-games/va" \
  -H "apikey: $ASSET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "assetId": "hero-skin-01",
    "assetName": "Hero Skin 01",
    "assetType": "0001",
    "createdBy": "dev@acme-games.com",
    "description": "Default hero skin",
    "localCacheMaxAge": 60
  }'
```

{% endtab %}
{% endtabs %}

`201 Created`

```json
{
  "assetId": "hero-skin-01",
  "assetName": "Hero Skin 01",
  "assetType": "0001",
  "createdBy": "dev@acme-games.com",
  "updatedBy": "dev@acme-games.com",
  "createdAt": "2026-09-07T12:00:00.000Z",
  "updatedAt": "2026-09-07T12:00:00.000Z",
  "active": true,
  "secured": true,
  "localCacheControl": false,
  "localCacheMaxAge": 60
}
```

At this stage, the [virtual asset](/umber/concepts/virtual-assets.md) definition is ready — you can add a version to it whenever you're ready.

## Get a virtual asset

`GET /api/v2/{domainId}/va/{assetId}`

```bash
curl "https://api.umbercloud.io/api/v2/acme-games/va/hero-skin-01" \
  -H "apikey: $ASSET_API_KEY"
```

`200 OK`

```json
{
  "assetId": "hero-skin-01",
  "assetName": "Hero Skin 01",
  "assetType": "0001",
  "createdBy": "dev@acme-games.com",
  "updatedBy": "dev@acme-games.com",
  "createdAt": "2026-09-07T12:00:00.000Z",
  "updatedAt": "2026-09-07T12:00:00.000Z",
  "active": true,
  "secured": true,
  "localCacheControl": false,
  "localCacheMaxAge": 60
}
```

`404` if `assetId` doesn't exist.

## List virtual assets

`GET /api/v2/{domainId}/va`

```bash
curl "https://api.umbercloud.io/api/v2/acme-games/va?pageNumber=1&pageSize=50" \
  -H "apikey: $ASSET_API_KEY"
```

`200 OK`

```json
{
  "totalResults": 2,
  "pageNumber": 1,
  "pageSize": 50,
  "results": [
    {
      "assetId": "hero-skin-01",
      "assetName": "Hero Skin 01",
      "assetType": "0001",
      "createdBy": "dev@acme-games.com",
      "updatedBy": "dev@acme-games.com",
      "createdAt": "2026-09-07T12:00:00.000Z",
      "updatedAt": "2026-09-07T12:00:00.000Z",
      "active": true,
      "secured": true,
      "localCacheControl": false,
      "localCacheMaxAge": 60
    },
    {
      "assetId": "hero-skin-02",
      "assetName": "Hero Skin 02",
      "assetType": "0001",
      "createdBy": "dev@acme-games.com",
      "updatedBy": "dev@acme-games.com",
      "createdAt": "2026-08-20T09:30:00.000Z",
      "updatedAt": "2026-08-20T09:30:00.000Z",
      "active": true,
      "secured": true,
      "localCacheControl": false,
      "localCacheMaxAge": 30
    }
  ]
}
```

There's no filtering — every call returns the domain's full set, paginated.

## Search virtual assets

`GET /api/v2/{domainId}/va/search`

Query params: `text` (free-text, optional), `page` (default `1`), `pageSize` (default `50`).

```bash
curl "https://api.umbercloud.io/api/v2/acme-games/va/search?text=hero" \
  -H "apikey: $ASSET_API_KEY"
```

`200 OK` — a different envelope from List:

```json
{
  "results": {
    "pageSize": 50,
    "total": 3,
    "hits": [
      {
        "_id": "6710a1b2c3d4e5f6a7b8c9d0",
        "_score": 1.23,
        "_source": {
          "assetId": "hero-skin-01",
          "assetName": "Hero Skin 01",
          "assetType": "0001",
          "description": "Default hero skin",
          "secured": true,
          "active": true,
          "domain": "acme-games",
          "createdAt": "2026-09-07T12:00:00.000Z",
          "updatedAt": "2026-09-07T12:00:00.000Z"
        }
      }
    ]
  }
}
```

Each match is wrapped in `_source`; `_score` may be `null` when the match came from a plain-text fallback rather than the search index.

## Update a virtual asset

`PUT /api/v2/{domainId}/va/{assetId}`

Body must include `updatedBy` and `assetType` (a valid, active asset type code). Include any of `assetName`, `description`, `localCacheControl`, `localCacheMaxAge`, `active` to change them. `assetId`, `secured`, and `createdBy` can't be changed after creation.

{% tabs %}
{% tab title="JSON" %}

```json
{
  "updatedBy": "dev@acme-games.com",
  "assetType": "0001",
  "assetName": "Hero Skin 01 (v2)",
  "localCacheMaxAge": 120
}
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl -X PUT "https://api.umbercloud.io/api/v2/acme-games/va/hero-skin-01" \
  -H "apikey: $ASSET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "updatedBy": "dev@acme-games.com",
    "assetType": "0001",
    "assetName": "Hero Skin 01 (v2)",
    "localCacheMaxAge": 120
  }'
```

{% endtab %}
{% endtabs %}

`200 OK` — same shape as the create response. `404` if `assetId` doesn't exist or `assetType` is invalid.

## Delete a virtual asset

`DELETE /api/v2/{domainId}/va/{assetId}`

Body must include `deletedBy`. This is a **hard delete** — the record is permanently removed, not archived — and it fails if the asset still has any versions.

{% tabs %}
{% tab title="JSON" %}

```json
{ "deletedBy": "dev@acme-games.com" }
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl -X DELETE "https://api.umbercloud.io/api/v2/acme-games/va/hero-skin-01" \
  -H "apikey: $ASSET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "deletedBy": "dev@acme-games.com" }'
```

{% endtab %}
{% endtabs %}

`204 No Content`. `400` (`"Please make sure that Virtual Asset does not contain any versions."`) if versions still exist — delete those first. `404` if `assetId` doesn't exist.

{% hint style="info" %}
See [Deleting Assets and Versions](/umber/guides/deleting-assets-and-versions.md) for the equivalent dashboard flow and the reasoning behind the versions-first order.
{% endhint %}

## List an asset's deployed URLs

`GET /api/v2/{domainId}/va/{assetId}/asset-urls`

Query params: `pageNumber`, `pageSize`, and optional filters `targetPlatform` and `envId`. By default only versions that aren't in the `OFFLINE` [deployment state](/umber/concepts/deployment-states.md) are included — pass `state` explicitly to see offline versions too.

```bash
curl "https://api.umbercloud.io/api/v2/acme-games/va/hero-skin-01/asset-urls?envId=prod" \
  -H "apikey: $ASSET_API_KEY"
```

`200 OK` — the [pagination envelope](#pagination); each result:

```json
{
  "assetId": "hero-skin-01",
  "version": 3,
  "versionId": "IOS3",
  "versionName": "3.0.0",
  "targetPlatform": "ios",
  "assetType": "0001",
  "secured": true,
  "state": "LIVE",
  "envId": "prod",
  "downloadUrl": "https://assets.umbercloud.io/acme-games/hero-skin-01/prod/ios"
}
```

`downloadUrl` is omitted for versions in the `OFFLINE` state. `404` if `assetId` doesn't exist.

## Version count

`GET /api/v2/{domainId}/va/{assetId}/version-count`

Query param: `active` (`"true"` / `"false"`, optional). Counts only the versions belonging to `assetId`.

```bash
curl "https://api.umbercloud.io/api/v2/acme-games/va/hero-skin-01/version-count" \
  -H "apikey: $ASSET_API_KEY"
```

`200 OK`

```json
{ "counts": 3 }
```

`404` if `assetId` doesn't exist.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://umber.gitbook.io/umber/api-reference-v2/virtual-assets.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
