---
id: assets
title: 'Assets in Agility'
description: 'Understanding assets, asset types, OIDs and the core data model in Digital.ai Agility'
sidebar_label: 'Assets'
sidebar_position: 1
tags: ['concepts', 'assets', 'data-model']
---

Assets are the fundamental building blocks of Digital.ai Agility. Every work
item, project, sprint and configuration element is an asset with a unique
identifier, a type, and a set of attributes.

## What Is an Asset?

An **asset** is any entity stored in the Agility system:

- **Work items**: Stories, Defects, Test Sets, Tasks, Tests
- **Portfolio items**: Epics (displayed as *Portfolio Item*), Themes
  (displayed as *Backlog Group*)
- **Organization and planning**: Scopes (*Projects*), Timeboxes
  (*Iterations/Sprints*), Teams, Members
- **Tracking**: Issues, Requests, Goals

Each asset has:

- A unique [**Object ID (OID)**](#object-ids-oids) such as `Story:1207`
- An [**asset type**](#the-asset-type-hierarchy) that defines its structure
  and behaviour
- A set of [**attributes**](/concepts/attributes) that store its data,
  including [**relations**](/concepts/relations) to other assets

## The Asset Type Hierarchy

Asset types inherit from one another. The work-item corner of the hierarchy:

```mermaid
flowchart TD
    BaseAsset --> Workitem["Workitem (abstract)"]
    BaseAsset --> Issue
    BaseAsset --> Request
    BaseAsset --> More["… many more"]
    Workitem --> PrimaryWorkitem["PrimaryWorkitem (abstract)"]
    Workitem --> Task
    Workitem --> Test
    Workitem --> Epic["Epic (UI: Portfolio Item)"]
    Workitem --> Theme["Theme (UI: Backlog Group)"]
    PrimaryWorkitem --> Story
    PrimaryWorkitem --> Defect
    PrimaryWorkitem --> TestSet
```

Two things about this tree surprise people:

- **`Issue` is not a work item.** It descends directly from `BaseAsset`, so
  a `Workitem` query never returns Issues.
- **`Epic` and `Theme` are.** Both descend from `Workitem`, so a `Workitem`
  query returns portfolio items alongside stories and tasks.

Discover any type's parent with the Meta API: `GET /meta.v1/Story` reports
`Base: PrimaryWorkitem`.

:::tip Even the schema is assets
The model describes itself: asset types and their attribute definitions are
assets too. `AttributeDefinition` is a queryable type, and filtering it on
its `AssetType` relation returns a type's attribute definitions through
`query.v1`, `rest-1.v1` or `api/asset`. It works, but it is not the
recommended path — `meta.v1` serves the same information already shaped for
the purpose: inheritance resolved, operations included, no authentication
required.
:::

### Querying Abstract Types Is Allowed — and Useful

`Workitem` and `PrimaryWorkitem` are abstract: no asset is ever *just* a
Workitem, and you cannot create one. But you can absolutely **query** them,
and it is the natural way to work across subtypes:

```json
{
  "from": "PrimaryWorkitem",
  "select": ["Number", "Name"],
  "page": { "size": 50, "start": 0 }
}
```

That one query returns Stories, Defects and Test Sets together — the classic
"whole backlog" shape. Remember the tree when interpreting results: a
`Workitem` query includes Epics and Themes too.

## Object IDs (OIDs)

Every asset has a unique **Object ID**: the asset type and a numeric id,
joined by a colon.

```text
Story:1207
Defect:5678
Scope:0
Member:20
```

An OID with a **third** part (`Story:1207:5837`) identifies a past version —
a *moment* — of the asset. See
[Moments and history](/concepts/moments-and-history).

### OID Vs Number

Work items also have a human-readable `Number`:

| Property   | Example      | Description                       |
| ---------- | ------------ | --------------------------------- |
| **OID**    | `Story:1207` | Permanent identifier — use in API calls |
| **Number** | `S-01001`    | Display number — use with humans  |
| **Name**   | `Logon`      | Title                             |

Find an asset's OID from its Number (every result carries `_oid`):

```json
{
  "from": "Story",
  "where": { "Number": "S-01001" },
  "select": ["Number", "Name"]
}
```

Fetch a specific asset when you already hold its OID — filter on `ID`, the
attribute every asset has that refers to itself:

```json
{
  "from": "Story",
  "where": { "ID": "Story:1207" },
  "select": ["Number", "Name"]
}
```

## Asset State and Status — Two Different Things

Two mechanisms track "where an asset is", and conflating them causes real
bugs:

- **`AssetState`** is the system lifecycle: Future (0), Active (64),
  Closed/Inactive (128), and the dead states (192 and above, including
  Deleted at 255). Every asset has it, it is not customizable, and it is
  what `Inactivate`/`Reactivate` operations change. See the
  [state table in the quickstart](/getting-started/quickstart#understanding-asset-states).
- **`Status`** is the workflow position — a list value like *Future*,
  *In Progress*, *Done* — customizable per instance and per project. See
  [Workflows and status](/concepts/workflows).

A story with `Status` = *Done* is usually still `AssetState` = Active; it
becomes Closed when someone closes it.

Lifecycle transitions are performed with **operations**, invoked as a
query-string parameter with an empty POST body:

```bash
curl -X POST -H "Authorization: Bearer ${AGILITY_ACCESS_TOKEN}" \
  "${AGILITY_BASE_URL}/rest-1.v1/Data/Story/1207?op=Inactivate"
```

See [Operations](/concepts/operations) for the catalogue and their quirks.

## Relations Between Assets

Relations are attributes whose values are other assets. They come in two
shapes:

- **Single-value**: `Scope`, `Timebox`, `Status`, `Parent`, `Super`
- **Multi-value**: `Owners`, `Children`, `Dependencies`, `Dependants`

Frequently used work-item relations:

| Relation       | Cardinality | Description                                        |
| -------------- | ----------- | -------------------------------------------------- |
| `Scope`        | single      | The project that contains the asset                |
| `Timebox`      | single      | Sprint/iteration assignment                        |
| `Owners`       | multi       | Assigned members — note the plural                 |
| `Children`     | multi       | A primary workitem's Tasks and Tests               |
| `Parent`       | single      | A Task's or Test's primary workitem                |
| `Super`        | single      | A primary workitem's Epic (portfolio parent)       |
| `Dependencies` | multi       | Upstream work this item depends on                 |

Traverse relations in a selection with dotted attribute paths:

```json
{
  "from": "Story",
  "select": ["Name", "Scope.Name", "Timebox.Name", "Owners.Name"],
  "page": { "size": 50, "start": 0 }
}
```

See [Relations](/concepts/relations) for traversal depth, filtering through
relations, and downcasting.

## Querying Assets

The response to a `query.v1` request is an **array of result arrays** — one
per query in the request, each containing the matching assets:

```json
[
  [
    { "_oid": "Story:1207", "Number": "S-01001", "Name": "Logon" },
    { "_oid": "Story:1208", "Number": "S-01002", "Name": "Setup" }
  ]
]
```

Filter with `where` (exact match) and `filter` (full comparison grammar,
including OR-lists):

```json
{
  "from": "Story",
  "where": { "Scope": "Scope:0" },
  "filter": ["Status.Name='Future','Done'"],
  "select": ["Number", "Name", "Status.Name"],
  "page": { "size": 50, "start": 0 }
}
```

See the [Query API](/apis/query-v1) and the
[query grammar](/reference/query-grammar) for the full syntax.

## Best Practices

1. **Use OIDs in code, Numbers with people.**
2. **Query abstract types** (`PrimaryWorkitem`) to work across subtypes;
   remember you cannot create them.
3. **Always page list queries** — Agility has no default page size. See
   [query size and performance](/guides/query-size-and-performance).
4. **Select only what you need**; every relation you project costs a join.
5. **Check `meta.v1`** when an attribute or type surprises you — the schema,
   including custom attributes, differs per instance.

## Code Examples

### TypeScript: Fetch One Asset by OID

```typescript
async function fetchAsset(client: AgilityClient, oid: string) {
  const [results] = await client.query({
    from: oid.split(':')[0],
    where: { ID: oid },
    select: ['Number', 'Name'],
  });

  if (results.length === 0) {
    throw new Error(`Asset ${oid} not found`);
  }
  return results[0];
}

const story = await fetchAsset(client, 'Story:1207');
console.log(`${story.Number}: ${story.Name}`);
```

### Python: OID Helpers

```python
def parse_oid(oid: str) -> tuple[str, int]:
    """Split an OID into asset type and numeric id."""
    asset_type, _, num = oid.partition(':')
    return asset_type, int(num)

def build_oid(asset_type: str, asset_id: int) -> str:
    return f"{asset_type}:{asset_id}"

asset_type, asset_id = parse_oid("Story:1207")
```

## Related Topics

- [Attributes](/concepts/attributes) — attribute types and definitions
- [Relations](/concepts/relations) — traversal and filtering through the graph
- [Moments and history](/concepts/moments-and-history) — past versions and `asof`
- [Operations](/concepts/operations) — actions that change lifecycle state
