---
id: updating-assets
title: 'Updating Assets'
description: 'Update a single asset by OID token, or every asset matching a query, in one Bulk API request.'
sidebar_label: 'Updating Assets'
sidebar_position: 4
tags: ['api', 'bulk', 'asset', 'update']
---

An update command is a command object with a `from` clause and an `update`
clause. `from` selects the target — either a single OID token or an asset type
narrowed by `where` or `filter`.

## Updating a Single Asset

```json
{
  "from": "Story:1585",
  "update": {
    "Name": "Update members",
    "Description": "Given I am an admin, when I open a member's profile, I can edit fields."
  }
}
```

```json title="Response"
{
  "assetsCreated": { "oidTokens": [], "count": 0 },
  "assetsModified": { "oidTokens": ["Story:1585"], "count": 1 },
  "assetsOperatedOn": { "oidTokens": [], "count": 0 },
  "commandFailures": { "commands": [], "count": 0 },
  "queryResult": { "results": [], "count": -1 }
}
```

## Updating Everything That Matches a Query

Give `from` an asset type and add a `where` (attribute equality) or `filter`
(full [query.v1 filter expression](/apis/query-v1)) clause — `with`
[variables](/reference/query-grammar/filter-context) work in the filter here
exactly as they do in a query, arrays included. Every matching
asset receives the same update.

```json
{
  "from": "Workitem",
  "where": { "Parent": "Story:1591" },
  "update": {
    "Owners": ["Member:20", "Member:1575"]
  }
}
```

```json title="Response"
{
  "assetsModified": { "oidTokens": ["Task:1592", "Test:1593"], "count": 2 }
}
```

:::warning There is no implicit limit
`from: Story` with no `where` matches every Story the calling identity can see.
Use [preview mode](/apis/asset-bulk/preview-mode) to see exactly which assets a
query-scoped update will touch before you run it.
:::


## Scalar and Single-Value Relation Attributes

Scalars and single-value relations use the same syntax — assign the value
directly. A single-value relation accepts an OID token or a name, optionally
[qualified with a path](/apis/asset-bulk/creating-assets#qualifying-a-name-with-a-path);
the name must resolve to exactly one asset.

```json
{
  "from": "Task:1489",
  "update": {
    "Description": "TODO -- get details from Andre Agile",
    "Status": "In Progress"
  }
}
```

Internally both are projected into the same `add` / `remove` shape used for
multi-value relations. That normalisation is visible in
[preview mode](/apis/asset-bulk/preview-mode):

```json
[
  {
    "@526e8128-41be-44ef-8401-bb3bd19b03ef": {
      "oid": "Task:1489",
      "update": {
        "Description": { "add": ["TODO -- get details from Andre Agile"], "remove": [] },
        "Status": { "add": ["In Progress"], "remove": [] }
      }
    }
  }
]
```

The projection does not distinguish scalar from relation. The processing phase
resolves the attribute definition and applies the value appropriately.


For multi-value relations, the `add` / `remove` structure becomes meaningful
and gains several convenience forms — see
[Updating multi-value relations](/apis/asset-bulk/relation-updates).

## Reordering with The `Order` Attribute

`Order` is a **`Rank`** attribute, and rank attributes work differently from
every other type in two ways that matter.

:::warning Never read a rank value — sort by it

The value of a rank attribute is an **opaque token whose only meaning is relative
to other ranks**. It is not a position, not an index, and not a number you can
reason about. Reading it, storing it, comparing it arithmetically or trying to
compute a "next" value will all mislead you, and the representation is free to
change.

The supported way to consume ordering is to **sort by the attribute** and take the
sequence you get back:

```yaml
from: Story
where:
 Scope: Scope:1000
sort:
- +Order
```

This applies to every `Rank` attribute, not just `Story.Order`. Most asset types
have one — `Defect.Order`, `Epic.Order`, `Issue.Order`, and an `Order` on nearly
every list type, which is what drives the sequence of values in a dropdown.

:::

Writing follows the same principle: **you do not assign a rank, you name a
neighbour.** The `Order` attribute takes a positional object rather than a value,
identifying the sibling the asset should sit before or after.

Both
`Before` and `After` are supported, and each takes the OID token of a sibling.

```yaml title="Move Story B before Story A"
from: Story:1002
update:
 Order:
  Before: Story:1001
```

```json title="Move Story C after Story D"
{
  "from": "Story:1003",
  "update": {
    "Order": { "After": "Story:1004" }
  }
}
```

Verify with a sorted query:

```yaml
from: Story
where:
 Scope: Scope:1000
sort:
- +Order
```


## Updating a Deleted Asset

Deleted assets are excluded from queries by default. To target one, add
`IsDeleted: true` to the `where` clause — even when `from` is an explicit OID
token.

```yaml
from: Story:1591
where:
 IsDeleted: true
update:
 Owners: Member:20
```

The update succeeds and is reported in `assetsModified` as normal. The asset
remains deleted.


The same rule applies to [operations](/apis/asset-bulk/operations#undelete) —
`Undelete` needs `IsDeleted: true` to find its target.

## Recording a Change Comment

Include `ChangeComment` **inside the `update` node** to attach an audit comment
to the change:

```json
{
  "from": "Story:1024",
  "update": { "Estimate": "5", "ChangeComment": "Re-estimated after grooming" }
}
```

`ChangeComment` is not stored as an attribute — it becomes the change's audit
comment, read back from history as
[`ChangeComment`](/concepts/moments-and-history#reading-history-as-a-relation).
On a **create** command it goes at the command root instead, alongside
`AssetType` (see [creating assets](/apis/asset-bulk/creating-assets#recording-a-change-comment)).

Omit it and the change is stamped **`Modified through api/asset`** (creates
get `Created through api/asset`). The default distinguishes Bulk API writes
from UI edits in the audit trail; `rest-1.v1` stamps nothing.

## Unknown Attributes

Naming an attribute that does not exist on the target asset type produces a
command failure, not an HTTP error. The snippet below shows only the
`commandFailures` member of the response — the full envelope still carries
`assetsCreated`, `assetsModified`, `assetsOperatedOn` and `queryResult`
alongside it, as shown at the top of this page:

```json title="commandFailures (other response members omitted)"
{
  "commands": [
    {
      "@update": {
        "oid": "Scope:0",
        "update": { "MuddleBubble": { "add": ["BubbleMuddle"], "remove": [] } }
      },
      "error": {
        "message": "Unknown AttributeDefinition: Scope.MuddleBubble",
        "sourceCommandIndex": 0
      }
    }
  ],
  "count": 1
}
```


See [Errors and partial failures](/apis/asset-bulk/errors) for the full
failure model.

## Related Topics

- [Updating multi-value relations](/apis/asset-bulk/relation-updates)
- [Find and replace](/apis/asset-bulk/find-and-replace)
- [Preview mode](/apis/asset-bulk/preview-mode)
- [Attributes](/concepts/attributes)
