---
id: relation-updates
title: 'Updating Multi-Value Relations'
description: 'The complete add/remove grammar for multi-value relations on the Bulk API, including object-keys, subquery, and mixed forms.'
sidebar_label: 'Multi-Value Relations'
sidebar_position: 5
tags: ['api', 'bulk', 'asset', 'update', 'relations']
---

Multi-value relations — `Owners`, `Subs`, `Children`, `Workitems`,
`RegressionSuites` — accept a family of syntaxes that all normalise to the same
projected form:

```json
{
  "update": {
    "Owners": { "add": [ /* ... */ ], "remove": [ /* ... */ ] }
  }
}
```

Everything below is a shorthand for filling those two arrays. Pick whichever
form reads best for the job.

## Adding

### One Reference

All five of these are equivalent.

```json title="Implicit single-add with scalar"
{ "from": "Task:1489", "update": { "Owners": "Member:20" } }
```

```json title="Implicit single-add with array"
{ "from": "Task:1489", "update": { "Owners": ["Member:20"] } }
```

```json title="Explicit single-add with object scalar"
{ "from": "Task:1489", "update": { "Owners": { "add": "Member:20" } } }
```

```json title="Explicit single-add with object array"
{ "from": "Task:1489", "update": { "Owners": { "add": ["Member:20"] } } }
```

```json title="Explicit single-add with object keys"
{ "from": "Task:1489", "update": { "Owners": { "Member:20": "add" } } }
```

Each projects to:

```json
[
  {
    "@bd9598ad-66af-4cec-b03e-bf273d91bfb5": {
      "oid": "Task:1489",
      "update": { "Owners": { "add": ["Member:20"], "remove": [] } }
    }
  }
]
```


### Several References

```json title="Implicit multi-add with array"
{ "from": "Task:1489", "update": { "Owners": ["Member:20", "Member:1575"] } }
```

```json title="Explicit multi-add with object array"
{ "from": "Task:1489", "update": { "Owners": { "add": ["Member:20", "Member:1575"] } } }
```

```json title="Explicit multi-add with object keys"
{
  "from": "Task:1489",
  "update": { "Owners": { "Member:20": "add", "Member:1575": "add" } }
}
```


## Removing

Swap `add` for `remove`. The object-scalar and object-keys forms are the most
convenient:

```json title="Explicit single-remove with object scalar"
{ "from": "Task:1489", "update": { "Owners": { "remove": "Member:20" } } }
```

```json title="Explicit single-remove with object keys"
{ "from": "Task:1489", "update": { "Owners": { "Member:20": "remove" } } }
```

Object keys scale to several removals in one command:

```json
{
  "from": "Epic:1600",
  "update": {
    "Name": "I have zero Subs",
    "Subs": { "Story:1601": "remove", "Story:1602": "remove" }
  }
}
```


## Adding and Removing in One Command

```yaml title="Object context pointing to scalars"
from: Story:1700
update:
 Owners:
  remove: Member:20
  add: Member:1800
```

```yaml title="Object context pointing to arrays"
from: Story:1700
update:
 Owners:
  remove:
  - Member:20
  add:
  - Member:1800
```

```json title="Object keys"
{
  "from": "Story:1700",
  "update": {
    "Owners": { "Member:20": "remove", "Member:1800": "add" }
  }
}
```

All three project to:

```json
[
  {
    "@ca414bbc-8dc5-41ef-9626-244b80145059": {
      "oid": "Story:1700",
      "update": {
        "Owners": { "add": ["Member:1800"], "remove": ["Member:20"] }
      }
    }
  }
]
```


:::tip
`remove` is applied together with `add`, so it is safe to reference the same
attribute in both — the result is the set difference plus the additions, not
an ordering-sensitive sequence of edits.
:::

## Referencing by Subquery

`add` and `remove` also accept a **subquery** in place of a literal list. The
subquery runs during processing and every match participates.

```yaml title="Add every Story matching a filter"
from: Epic:1900
update:
 Subs:
  add:
   from: Story
   filter:
   - ID='Story:1901','Story:1902','Story:1903'
```

```yaml title="Remove every Story currently under this Epic"
from: Epic:1900
update:
 Subs:
  remove:
   from: Story
   filter:
   - Super='Epic:1900'
```


:::warning Subquery cardinality differs by position
In a **relation update**, every asset the subquery matches is used. In an
[operation argument](/apis/asset-bulk/operations#subquery-arguments), only the
_first_ match is used. Do not assume one behavior from the other.
:::

## Mixing Existing References with New Assets

An `add` list may contain OID tokens and inline create commands side by side.
The new assets are created and related in the same request.

```json
{
  "from": "Epic:1850",
  "update": {
    "Name": "I should now be related to Story:1840 and another new Story via the Subs relation",
    "Subs": [
      "Story:1840",
      { "AssetType": "Story", "Name": "I am a brand new Story" }
    ]
  }
}
```

The response reports the Epic in `assetsModified` and the new Story in
`assetsCreated`.

The inline create is projected once **per matched asset**. Under a
[query-scoped update](/apis/asset-bulk/updating-assets#updating-everything-that-matches-a-query)
that means one new asset for every asset the `from`/`filter` selects, each
related to its own parent — the shape to reach for when the same child
belongs on many parents.

### Adding Only Where It Is Missing

Because the filter runs before the creates are projected, a filter that
excludes the parents already holding an equivalent child makes the whole
command **idempotent**: run it twice and the second run creates nothing.
This example puts a `Link` to a pull request on every listed asset that does
not already carry that URL:

```json
{
  "from": "BaseAsset",
  "filter": ["ID=$oids", "-Links[URL=$url]"],
  "with": {
    "$oids": ["Story:1227", "Defect:1310"],
    "$url": "https://github.com/example/repo/pull/12"
  },
  "update": {
    "Links": [
      {
        "AssetType": "Link",
        "Name": "GitHub PR",
        "URL": "https://github.com/example/repo/pull/12",
        "OnMenu": true
      }
    ]
  }
}
```

`assetsModified` names the parents that received a Link; a parent the
filter left out is absent from it and from `assetsCreated`. The check and
the write happen inside one command, so there is no window between them for
a second client to slip into — the closest thing the Bulk API offers to an
idempotency key.

:::warning Provisos for inline creates
- **Use the array or `add` form.** A bare mapping directly under the
  relation — `"Links": { "AssetType": "Link", ... }` — is half-handled: the
  child is created and related, and the command is still reported in
  `commandFailures` with a StructureMap error naming `Relation`. Only the
  array under the relation, or `add:` with a list, is the supported syntax.
- **Leave the update command unaliased.** In an array of commands, an alias
  on an update that carries an inline create makes every projected create
  fail with `The given key was not present in the dictionary`; the same
  command with no alias, in the same batch, succeeds. Aliases on the other
  commands in the batch are fine.
- **One level only.** A create nested inside an inline create is projected
  after the child that needs it, so a required parent — an `Expression`'s
  `Conversation`, say — is not there yet and the child fails validation.
  An inline create also cannot reference an alias defined elsewhere in the
  same request. Create the dependency in its own command first and use its
  OID.
- **Match exactly in the guard filter.** `-Links[URL=$url]` compares the
  stored value; `~=` is [full-text search](/reference/query-grammar/filter-expressions#comparison-operators)
  and its index catches up with writes asynchronously, so a guard written
  with `~=` still sees a child created seconds ago as missing and creates
  it again.
:::


## Replacing One Reference with Another

To swap a reference only where it already exists, use
[find and replace](/apis/asset-bulk/find-and-replace) rather than a paired
`add`/`remove`. `add`/`remove` applies unconditionally to every matched asset;
`find`/`replace` only touches assets that already hold the `find` value.

## Related Topics

- [Updating assets](/apis/asset-bulk/updating-assets)
- [Find and replace](/apis/asset-bulk/find-and-replace)
- [Preview mode](/apis/asset-bulk/preview-mode)
- [Relations](/concepts/relations)
