---
id: creating-assets
title: 'Creating Assets'
description: 'Create one asset, many assets, or a whole hierarchy in a single Bulk API request.'
sidebar_label: 'Creating Assets'
sidebar_position: 3
tags: ['api', 'bulk', 'asset', 'create']
---

A create command is any command object that carries an `AssetType` property.
Everything else in the object is either an attribute value, a relation value,
or a nested hierarchy of further create commands.

## A Single Asset

```json
{
  "AssetType": "Story",
  "Scope": "Scope:0",
  "Name": "First Story"
}
```

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

## Many Assets

Send an array (JSON) or a multi-document stream (YAML). Asset types may be
mixed freely.

```json
[
  { "AssetType": "Story", "Scope": "Scope:0", "Name": "Second Story" },
  { "AssetType": "Story", "Scope": "Scope:0", "Name": "Third Story" },
  { "AssetType": "Defect", "Scope": "Scope:0", "Name": "First Defect" }
]
```

## Referencing a Relation by OID Token or by Name

A single-value relation can be given either an OID token or the target asset's
**name**. Both forms resolve to the same asset.

```yaml title="By name"
AssetType: Story
Name: My Story
Scope: System (All Projects)
```

```yaml title="By OID token"
AssetType: Story
Name: My Story
Scope: Scope:0
```


A name is matched against the target type's naming attributes — `Name` for
most types, `Name` or `Number` for types that carry a display number, and
`Name`, `Nickname`, `Username`, or `Email` for a Member — and deleted assets
never match.

:::note `Role.Name'Project Admin` is a name, not a syntax

Some built-in assets store a localization tag as their `Name`; every Role is
named `Role.Name'<name>`, for example (see
[roles and permissions](/reference/roles-and-permissions)). So this is an
ordinary lookup by name:

```yaml
AssetType: Member
Name: User1
Username: User1
Password: User1
Nickname: User1
DefaultRole: Role.Name'Project Admin
```

There is no `<AssetType>.<Attribute>'<Value>` form for relation values. A
value of `Scope.Name'Optimize` looks for a Scope literally named
`Scope.Name'Optimize`, and fails.

:::

### Qualifying a Name with a Path

A bare name only works when it is unambiguous. When it is not, qualify it with a
backslash-separated **path** through the asset's naming context:

```yaml
AssetType: Story
Name: My Story
Scope: Offerings\Agility 25.2\Optimize
```

The path needs to be **only deep enough to resolve the name uniquely** — it does
not have to start at the root. Everything after the last backslash is the target
asset's name; everything before it is context.

:::warning Backslashes in a name must be doubled

The last **unescaped** backslash is what separates context from name, so a
backslash that is part of an asset's own name must be written `\\`.

An asset genuinely called `Q1\Q2 Planning` inside `Offerings` is therefore
written `Offerings\Q1\\Q2 Planning`. Get this wrong and the name splits in the
wrong place, resolving to a different asset or to none.

:::

**The context may itself be an OID token.** Each segment is resolved by trying it
as an OID token first and falling back to a name lookup, so
`Scope:1003\Optimize` is as valid as spelling out the parent by name — useful
when you know the container's OID but not the child's.

#### Which types accept a path {#path-capable-types}

A path walks a single relation, the target type's **naming context**, and only
the types below (and the types derived from them) have one. Any other type
reads the whole value, backslashes and all, as one literal name.

| Target type | A segment matches on | Naming context |
| --- | --- | --- |
| `Scope` | `Name` | `Parent`, another Scope |
| `Workitem` and every type derived from it — `Story`, `Defect`, `Epic`, `Task`, `Test`, `TestSet`, `Theme` | `Name` or `Number` | `Scope` |
| `Issue`, `Request`, `Goal`, `StrategicTheme`, `Environment`, `RegressionTest`, `RegressionPlan` | `Name` or `Number` | `Scope` |
| `Room` | `Name` | `Scope` |
| `RegressionSuite` | `Name` or `Number` | `RegressionPlan` |
| `Timebox` | `Name` | `Schedule` |
| `StoryStatus` | `Name` | `Team` |

Two consequences follow:

- **A segment can be a Number where the table allows it.** `Parent: S-01001`
  resolves a Task's parent by display number, and `Acme\S-01001` narrows the
  same lookup to one project.
- **A path can cross types.** Each context segment is resolved by the context
  type's own rule, so `Offerings\Agility\Production` as an `Environment` value
  finds `Production` inside the Scope that `Offerings\Agility` resolves to,
  which is in turn the `Agility` child of the `Offerings` Scope. The chain
  ends at the first type with no naming context: `Acme\Platform` as a `Team`
  value is a Team literally named `Acme\Platform`.

#### Find a type's naming context {#naming-context-discovery}

`meta.v1` does not report it. The rule is an attribute of the `AssetType`
metadata asset, which any query endpoint can read with any member's token,
whatever its role:

```bash title="rest-1.v1"
curl -H "Authorization: Bearer <access-token>" \
  "${AGILITY_BASE_URL}/rest-1.v1/Data/AssetType?sel=Name,Base.Name,NameResolutionAttributes,Resolver&where=Name='Scope'&page=5,0"
```

```json title="The same read as a Bulk API query"
{
  "from": "AssetType",
  "select": ["Name", "Base.Name", "NameResolutionAttributes", "Resolver"],
  "where": { "Name": "Scope" },
  "page": { "size": 5, "start": 0 }
}
```

```json title="Result (one row)"
{
  "_oid": "AssetType:-5034",
  "Name": "Scope",
  "Base.Name": "BaseAsset",
  "NameResolutionAttributes": "Name,Parent",
  "Resolver": "VersionOne.Meta.RelatedAssetResolver,VersionOne"
}
```

Read it like this:

- **`Resolver` set:** the last entry of `NameResolutionAttributes` is the
  naming context, and the entries before it are what a segment matches on.
- **`Resolver` empty:** the type has no naming context. The whole value is
  matched as a name against each listed attribute in turn, which is why a
  `Member` (`Name,Nickname,Username,Email`) can be referenced by any of the
  four.
- **Both empty:** the type inherits its base type's rule. `Story` reports
  nothing of its own; follow `Base` to `PrimaryWorkitem` and then to
  `Workitem`.

### How a Relation Value Is Interpreted

The rule is the same for every relation attribute, and it is worth knowing
because it explains why names work at all:

1. The value is tried as an **OID token**.
2. If that fails, it is treated as a **moniker** and resolved by name, path
   and all.

There is no flag and no syntax to choose between them — the fallback is
automatic. A consequence worth noting: a mistyped OID token does not fail as a
bad token, it becomes a name lookup, and the error you get back is about an
unresolvable name rather than a malformed identifier.

The same resolution applies when setting a relation in an **update** command,
not just a create. See [Updating assets](/apis/asset-bulk/updating-assets)
and [multi-value relations](/apis/asset-bulk/relation-updates).

#### A moniker must resolve to exactly one asset {#moniker-must-be-unique}

Name resolution has three outcomes, and only one of them sets the relation:

| Matches | Outcome | `error.message` |
| --- | --- | --- |
| Exactly one | The relation is set | — |
| None | The command fails | `Resolution'Unresolved'<Type>.<Attribute>'<TargetType>` |
| More than one | The command fails | `Resolution'Ambiguous'<Type>.<Attribute>'<TargetType>` |

`Resolution'Ambiguous'Story.Scope'Scope` names the attribute being set and the
type it searched; the value you sent is not echoed back. The failure is per
command: the rest of the request still runs, and the failed command comes back
in `commandFailures` ready to correct and resubmit (see
[errors and partial failures](/apis/asset-bulk/errors)).

A path does not change the rule, it narrows the candidates. `Acme\Optimize`
that still matches two child Scopes of `Acme` named `Optimize` is still
`Resolution'Ambiguous'`; the only unambiguous reference to one of them is its
OID token.

Resolution searches every asset of the type, not just the ones you can see. A
member with access to only one of two Scopes named `Moniker Test` sees one
match when querying for that name, yet `Scope: Moniker Test` still fails with
`Resolution'Ambiguous'`. When a query returns exactly one asset and the name
still fails as ambiguous, the duplicate is one you cannot see: use the OID
token from your query result.

:::note The same rule applies outside the API

Agility's Excel import resolves relation columns the same way and reports the
same two outcomes in words — "Unable to resolve attribute value for asset
type …" and "Multiple … assets matching provided name". An import that fails
on a duplicate name is the ambiguity a Bulk request reports as
`Resolution'Ambiguous'`, with the same remedies: qualify the value with a
path, or use the OID token.

:::


## Creating List Values

The values behind a dropdown — statuses, priorities, defect types, and any custom
list an administrator has added — are **ordinary assets**, not a special kind of
configuration. Each list is an asset type deriving from the abstract `List` type,
and each value in it is one asset of that type.

So populating a list is just creating assets:

```json
[
  { "AssetType": "Custom_RiskTier", "Name": "Low" },
  { "AssetType": "Custom_RiskTier", "Name": "Medium" },
  { "AssetType": "Custom_RiskTier", "Name": "High" }
]
```

Custom list types carry the `Custom_` prefix, matching the convention for custom
attributes. Built-in lists use their own names — `StoryStatus`,
`WorkitemPriority`, `DefectType` and so on. Confirm the type name against
[`meta.v1`](/apis/meta-v1) rather than guessing it; list type names are not
localized the way [role names](/reference/roles-and-permissions) are, but they do
vary with the methodology the instance was installed with.

Some list types carry more than a `Name`. `StoryStatus`, for example, takes a
`RollupState` and can be scoped to a `Team`:

```json
{ "AssetType": "StoryStatus", "Name": "In Progress", "RollupState": "64", "Team": "@team" }
```

Check the type in `meta.v1` for what it requires.

:::tip This is where the Bulk API earns its keep

Creating list values one at a time through `rest-1.v1` is a request per value.
The Bulk API takes the whole set in a single call, which turns populating a
several-thousand-entry list from a long-running script into one request.

The same [size considerations](/apis/asset-bulk/request-format) apply as for any
large request — a very large list is still better split into a few batches than
sent as one enormous body.

:::

## Creating a Hierarchy

Nest child create commands inside a relation property on the parent. Any
relation that accepts child assets works — `Children`, `Subs`, `Workitems`.

```json
{
  "AssetType": "Epic",
  "Scope": "Scope:0",
  "Name": "Epic with Subs",
  "Category": "Epic",
  "Subs": [
    {
      "AssetType": "Epic",
      "Name": "Feature Epic",
      "Category": "Feature",
      "Subs": [
        {
          "AssetType": "Story",
          "Name": "Feature Story 1",
          "Children": [
            { "AssetType": "Task", "Name": "Code the feature" },
            { "AssetType": "Test", "Name": "Test the feature" }
          ]
        }
      ]
    }
  ]
}
```

```json title="Response"
{
  "assetsCreated": {
    "oidTokens": ["Epic:1589", "Epic:1590", "Story:1591", "Task:1592", "Test:1593"],
    "count": 5
  },
  "assetsModified": { "oidTokens": [], "count": 0 },
  "assetsOperatedOn": { "oidTokens": [], "count": 0 },
  "commandFailures": { "commands": [], "count": 0 },
  "queryResult": { "results": [], "count": -1 }
}
```

Nesting is not limited in depth, and the child does not repeat the `Scope` —
it inherits its context from the containing asset. Internally the server
flattens the hierarchy into a linear command list before processing it; see
[Request hierarchy flattening](/apis/asset-bulk/aliases-and-projection#request-hierarchy-flattening).


### Omitted Attributes Are Inherited from the Containing Asset

A nested create command that omits `Name` and `Description` inherits them from
its parent.

```yaml
AssetType: Epic
Name: Epic name
Description: Epic description
Subs:
- AssetType: Story
  Name: My own name
  Description: My own description
- AssetType: Story          # inherits "Epic name" / "Epic description"
- AssetType: Story
  Name: Story with Children
  Description: Story with Children that has its own Description
```


## Setting a Multi-Value Relation from a Subquery

A relation value in a create command can be a **subquery** instead of a literal
reference. The subquery runs during processing and its matches become the
relation's members.

```yaml
AssetType: Story
Name: I need to drop one of these Owners!
Scope: Remove one owner
Owners:
 from: Member
 filter: ["Name='Administrator','User2'"]
```


:::tip
Subquery-valued relations are the cleanest way to write portable setup
scripts: they let you reference assets by a stable business value rather than
by an OID token that differs per instance.
:::

## Recording a Change Comment {#recording-a-change-comment}

Add `ChangeComment` at the **command root**, alongside `AssetType`, to attach an
audit comment to the creation:

```json
{ "AssetType": "Story", "Scope": "Scope:0", "Name": "New Story", "ChangeComment": "Imported from tracker" }
```

It is recorded as the change's comment, not as an attribute of the new asset,
and reads back from history as `ChangeComment`. (On an **update** command it goes
inside the `update` node instead — see
[updating assets](/apis/asset-bulk/updating-assets#recording-a-change-comment).)

When you do not supply one, the Bulk API stamps a default: a create's history
entry reads **`Created through api/asset`**. This marks Bulk API writes in the
audit trail on its own — `rest-1.v1` writes no comment at all unless one is
supplied. Supply your own `ChangeComment` when the integration has something
more specific to say.

## Creating Assets Through an Update Command

Assets can also be created as a side effect of an update command, by nesting
create commands inside a relation being updated:

```yaml
from: Scope:0
update:
 Workitems:
 - AssetType: Story
   Name: Story 1
 - AssetType: Story
   Name: Story 2
 - AssetType: Epic
   Name: Epic that will get new Subs
```

The response reports these in `assetsCreated`, and the updated container in
`assetsModified`. See
[Updating multi-value relations](/apis/asset-bulk/relation-updates#mixing-existing-references-with-new-assets).


## Naming a Create Command with an Alias

Wrapping a create command in a user alias lets later commands in the same
request refer to the asset before it exists:

```json
[
  {
    "@story": { "AssetType": "Story", "Scope": "Scope:0", "Name": "Story Time" }
  },
  {
    "AssetType": "Test", "Parent": "@story", "Name": "Test Time"
  }
]
```

See [Projection, processing and aliases](/apis/asset-bulk/aliases-and-projection).

## Related Topics

- [Request and response format](/apis/asset-bulk/request-format)
- [Projection, processing and aliases](/apis/asset-bulk/aliases-and-projection)
- [Errors and partial failures](/apis/asset-bulk/errors)
- [Assets](/concepts/assets)
