---
id: attributes
title: 'Attributes in Agility'
description: 'Understanding asset attributes, attribute definitions and data types in Digital.ai Agility'
sidebar_label: 'Attributes'
sidebar_position: 2
tags: ['concepts', 'attributes', 'data-model']
---

Attributes are the named properties that hold an [asset's](/concepts/assets)
data — names, estimates, dates, statuses, and [relations](/concepts/relations)
to other assets. Everything you select, filter, or update in the API is an
attribute.

## The Attribute Definition

Every attribute is described by an **attribute definition** in the
[Meta API](/apis/meta-v1). This is what `GET /meta.v1/Story` returns for
`Story.Owners`:

```json
{
  "_type": "AttributeDefinition",
  "Name": "Owners",
  "Token": "Story.Owners",
  "AttributeType": "Relation",
  "IsReadOnly": false,
  "IsRequired": false,
  "IsMultivalue": true,
  "IsCanned": true,
  "IsCustom": false,
  "Base": { "tokenref": "Workitem.Owners" },
  "RelatedAsset": { "nameref": "Member" },
  "ReciprocalRelation": { "tokenref": "Member.OwnedWorkitems" }
}
```

The fields that drive integration decisions:

| Field                | Tells you                                                          |
| -------------------- | ------------------------------------------------------------------ |
| `Token`              | The full name (`AssetType.Attribute`) — unambiguous in any context  |
| `AttributeType`      | The data type — see [attribute types](/reference/attribute-types)   |
| `IsMultivalue`       | Whether values are collections                                      |
| `IsRequired`         | Must be supplied when creating the asset                            |
| `IsReadOnly`         | Maintained by the system; writes are rejected or ignored            |
| `IsCustom`           | Defined by an administrator rather than shipped with the product    |
| `Base`               | The ancestor type that actually declares the attribute              |
| `RelatedAsset`       | For relations: the asset type on the other end                      |
| `ReciprocalRelation` | For relations: the same link seen from the other side               |

## Attribute Types, Briefly

The [attribute-types reference](/reference/attribute-types) is the complete
list; the ones you will meet constantly:

| Type       | Example on `Story`          | Notes                                                             |
| ---------- | --------------------------- | ------------------------------------------------------------------ |
| `Text`     | `Name`, `Number`            | Single-line strings                                                |
| `LongText` | `Description`               | Rich/long content                                                  |
| `Numeric`  | `Estimate`                  | Integer or decimal                                                 |
| `Date`     | `CreateDateUTC`, `ChangeDateUTC` | Prefer the `…UTC` twins for filtering — no timezone/DST surprises |
| `Boolean`  | `IsDeleted`, `IsClosed`     | True/false                                                         |
| `State`    | `AssetState`                | System lifecycle; read-only — change it with [operations](/concepts/operations) |
| `Rank`     | `Order`                     | An **opaque ordering token** — sort by it, never read or compare it |
| `Relation` | `Scope`, `Owners`, `Status` | Single- or multi-value links to other assets                       |

Two type-specific traps worth knowing early:

- **`Rank` values are not numbers you can reason about.** Sort by `Order` to
  reproduce the UI's ordering; do not store, compare or do arithmetic on the
  values.
- **Date attributes come in local and UTC pairs** (`ChangeDate` /
  `ChangeDateUTC`). Filter and project the UTC ones in integrations.

## Selecting Attributes

Name attributes directly, traverse relations with dots, and project multiple
attributes of related assets with a nested subquery. Multi-value paths come
back as arrays. This request and its real response:

```json
{
  "from": "Story",
  "where": { "ID": "Story:1207" },
  "select": [
    "Name",
    "Scope.Name",
    "Owners.Name",
    { "from": "Children:Task", "select": ["Name", "ToDo"] }
  ]
}
```

```json
[
  [
    {
      "_oid": "Story:1207",
      "Name": "Logon",
      "Scope.Name": "Release 1.0",
      "Owners.Name": ["Alfred Smith"],
      "Children:Task": [
        { "_oid": "Task:1469", "Name": "Design Login Page", "ToDo": "0" },
        { "_oid": "Task:1470", "Name": "Code Login", "ToDo": "0" }
      ]
    }
  ]
]
```

Note the response keys: a dotted selection arrives **flattened under its
path string** (`"Scope.Name"`), and a nested subquery arrives as an array of
objects under its `from` expression (`"Children:Task"` — the `:Task` is a
[downcast](/reference/query-grammar/attribute-paths) selecting only the Task
children).

## Filtering by Attributes

`where` matches exact values; `filter` takes the
[full comparison grammar](/reference/query-grammar/filter-expressions), with
values quoted:

```json
{
  "from": "Story",
  "where": { "Scope": "Scope:0" },
  "filter": ["Estimate>'2'", "ChangeDateUTC>='2026-01-01'"],
  "select": ["Number", "Name", "Estimate"],
  "page": { "size": 50, "start": 0 }
}
```

Filter on OIDs rather than names where you can — names are editable and
localized; OIDs are stable.

## Setting Attribute Values

Writes happen on two surfaces, each with its own shape — don't mix them:

**`rest-1.v1`** takes XML, marking each attribute with an action:

```xml
<Asset>
  <Attribute name="Name" act="set">Updated story name</Attribute>
  <Relation name="Status" act="set">
    <Asset idref="StoryStatus:134" />
  </Relation>
</Asset>
```

See [update examples](/apis/rest-1/update-examples) for scalars, single
relations, and multi-relation add/remove.

**`api/asset`** takes JSON or YAML update commands:

```json
{
  "from": "Story:1207",
  "update": {
    "Name": "Updated story name",
    "Owners": { "add": ["Member:20"] }
  }
}
```

See [updating assets](/apis/asset-bulk/updating-assets) for the full command
shape, including multi-relation `add`/`remove` semantics.

## Required and Read-Only Attributes

Read `IsRequired` and `IsReadOnly` from the Meta API rather than memorizing
lists. Verified examples from `Story`:

- `Name` — `IsRequired: true`; `Scope` — `IsRequired: true`. A Story create
  without both fails.
- `Number`, `CreateDateUTC`, `ChangeDateUTC` — `IsReadOnly: true`, generated
  and maintained by the system.
- `AssetState` — read-only **and** only changeable through
  [operations](/concepts/operations) (`Inactivate`, `Reactivate`, …), never
  by direct update.

One timing subtlety: requiredness is enforced **when values are written,
not retroactively** — changing an attribute definition to required does not
fill it in on existing assets of that type. Those assets are not locked by
the missing value: updates to their other attributes succeed, and
`Inactivate` closes them with the required attribute still empty.

## Custom Attributes

Administrators can add attributes to most types. They appear in the Meta API
like any other definition, flagged `IsCustom: true`; admin-created fields
have tokens beginning `Custom_`. Because they differ per instance:

1. **Discover them from `meta.v1`** rather than hard-coding names
2. **Use the exact token**, including the `Custom_` prefix
3. **Check `AttributeType` and `IsRequired`** before writing
4. **Ask for them explicitly.** Custom fields are never part of the default
   selection, so a query that omits `sel` — or one that filters with `where`
   but does not name them in `sel` — returns none of them. List each
   `Custom_…` token you want (`sel=Name,Custom_Region`).

## Best Practices

1. **Select only what you need** — every attribute costs something and every
   relation costs a join; see
   [query size and performance](/guides/query-size-and-performance).
2. **Read the definition before you write the value** — `IsReadOnly`,
   `IsRequired` and `AttributeType` answer most "why did my update fail?"
   questions before they happen.
3. **Use `Token` names in logs and errors** (`Story.Name`, not `Name`) — the
   same attribute name means different things on different types.
4. **Prefer UTC date attributes** in filters and projections.

## Related Topics

- [Attribute types](/reference/attribute-types) — the complete type list
- [Attribute paths](/reference/query-grammar/attribute-paths) — dots, downcasts and path filters
- [Meta API](/apis/meta-v1) — discovering definitions
- [Relations](/concepts/relations) — the relation side of the model in depth
