---
id: workflows
title: 'Workflows and Status'
description: 'How status lists, RollupState and asset lifecycle interact in Digital.ai Agility - and what the API does and does not enforce'
sidebar_label: 'Workflows & Status'
sidebar_position: 6
tags: ['concepts', 'workflows', 'status']
---

In Agility's API, a "workflow" is a **list of status values** that work items
move through — not a state machine. The API places no restrictions on which
status you set next; boards and team process impose order, the server does
not. What the server *does* maintain is each status's **rollup category**,
which quietly drives every report.

## Which Types Use Which Status List

Status lists are shared across related types — verified from the schema:

| Work item type            | Status list type |
| ------------------------- | ---------------- |
| Story, Defect, TestSet    | `StoryStatus` — one shared list for all primary workitems |
| Task                      | `TaskStatus`     |
| Test                      | `TestStatus`     |
| Epic                      | `EpicStatus`     |

There is no `DefectStatus`: a Defect's *Status* dropdown draws from the same
`StoryStatus` list as Stories. Rename or reorder a value and every primary
workitem type sees the change.

## Anatomy of a Status Value

Status values are assets. In the schema their types descend from an
abstract asset type named `List` (`StoryStatus` → `Status` → `List`) —
a name that reads backwards at first: each individual **value** is-a
`List`, so the type really means *list entry*. The list as a whole has
no asset of its own — "the StoryStatus list" is simply every asset of
type `StoryStatus`, optionally narrowed by a
[team's process or a scheme](#per-project-and-per-team-availability).
When [`meta.v1`](/apis/meta-v1) shows a relation whose target descends
from `List`, read it as "picks one entry from a value list".

The attributes that matter:

| Attribute     | Type     | Notes                                                                 |
| ------------- | -------- | ---------------------------------------------------------------------- |
| `Name`        | Text     | Instance data — editable, so **code should match on OID, not name**    |
| `Order`       | Rank     | Opaque ordering token: **sort by it, never read it**                    |
| `Description` | LongText |                                                                         |
| `RollupState` | State    | The status's lifecycle category: `Future`, `Active` or `Closed`         |

Discover a list, in display order:

```json
{
  "from": "StoryStatus",
  "select": ["Name", "RollupState"],
  "sort": ["Order"],
  "page": { "size": 50, "start": 0 }
}
```

## RollupState

`RollupState` exists on **`StoryStatus` and `EpicStatus`** — Task and Test
statuses have no rollup, so effective-state reasoning applies to primary
workitems and Epics, not to their breakdown items. Where it exists, every
status value carries one — and it does not always match
what the name suggests. A live 26.2 sample:

| Status name    | RollupState |
| -------------- | ----------- |
| Future         | Future      |
| In Progress    | Active      |
| Testing        | Active      |
| **In UAT**     | **Closed**  |
| Done           | Closed      |
| Accepted       | Closed      |

That *In UAT → Closed* row is the kind of configuration that makes an
integration's numbers disagree with the product's: reporting treats an
Active asset as effectively closed once its status rolls up as Closed. If
your counts don't match a dashboard, this is the first place to look — see
[reproducing reported numbers](/guides/reproducing-reported-numbers).

RollupState also drives the **`DoneDate` / `DoneDateUTC`** synthetic
attributes (read-only): they are stamped the moment a workitem's *effective*
state moves to Closed — verified live: setting Status to a Closed-rolling
value populated `DoneDateUTC` immediately, while `AssetState` remained
Active. If you report cycle time, this is your completion timestamp — not
the moment someone ran `Inactivate`.

There is no `StartedDate` — but you can reconstruct it (and who caused it)
with a **historical projection** over the asset's history, filtering for the
moment the effective state moved from Future to Active. It works, verified
live, but it is costly — use it for targeted assets, not instance-wide
sweeps:

```json
{
  "from": "Story",
  "where": { "ID": "Story:1207" },
  "select": [
    "Name",
    "History[Prior.EffectiveAssetState='Future';EffectiveAssetState='Active'].ChangeDateUTC",
    "History[Prior.EffectiveAssetState='Future';EffectiveAssetState='Active'].ChangedBy.Name"
  ]
}
```

(The semicolon inside the bracket filter is AND; `History` and `Prior` reach
across [moments](/concepts/moments-and-history).)

**It is often less costly to skip the filter** and pull the workitem's whole
Status/AssetState history in one go, finding the transitions client-side.
`History` works as a subquery, returning one object per moment:

```json
{
  "from": "Story",
  "where": { "ID": "Story:1207" },
  "select": [
    "Name",
    { "from": "History", "select": ["ChangeDateUTC", "Status.Name", "AssetState"] }
  ]
}
```

```json
"History": [
  { "_oid": "Story:1207:1482", "ChangeDateUTC": "2026-05-25T…", "Status.Name": "Future", "AssetState": "Active" },
  { "_oid": "Story:1207:1605", "ChangeDateUTC": "2026-05-26T…", "Status.Name": "Future", "AssetState": "Active" }
]
```

Each entry's `_oid` carries the moment (`Story:1207:1482`) — see
[Moments and history](/concepts/moments-and-history).

`RollupState` is also directly filterable, which gives you
process-independent queries that survive status renames:

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

## Changing Status

A status change is an ordinary attribute update — see
[Operations](/concepts/operations#status-changes-are-not-operations) for the
payload shapes on each write surface. Three facts to build on:

1. **No transition order** is enforced — Future to Accepted in one write
   is accepted. But **value membership is**: a team using a Team Process
   has its own set of `StoryStatus` values its primary workitems may be
   in, and that set is enforced on status change — verified live. Writing
   a status outside the team's process fails with
   `Violation'PrimaryWorkitemCannotHaveInvalidStatus`. (Status values
   carry a `Team` relation — a team-scoped value belongs to that team's
   process.)

   The process itself is queryable, though the attribute names are not
   guessable: **`Team.HasProcess`** says whether the team enforces one,
   and **`Team.SelectedValues`** is the allowed set.

   ```json
   { "from": "Team", "select": ["Name", "HasProcess", { "from": "SelectedValues", "select": ["Name"] }] }
   ```

   A team with `HasProcess: "False"` has an empty `SelectedValues` and
   enforces nothing. A status value belongs to a team's process **or** to
   a project scheme, never both — creating one with both a `Team` and
   `SelectedInSchemes` fails with
   `Violation'ListValueCannotBelongToBothSchemeAndTeam`. To ask the same question for one specific workitem —
   with every narrowing already applied — use
   [`api/ValidValues`](/apis/valid-values) instead.
2. **Use OIDs.** Status names are instance data and freely editable.
3. **Status is not lifecycle.** Setting a Closed-rolling status does not
   close the asset; `AssetState` changes only through operations like
   `Inactivate`. An asset can be `Status` = *Done* and `AssetState` =
   Active indefinitely — see
   [Assets](/concepts/assets#asset-state-and-status--two-different-things).

## Per-Project and per-Team Availability

Two mechanisms narrow which status values actually apply to a given work
item — discover per context rather than assuming one universal set:

- **Scheme** — shown in the UI as the **Project Workspace** — controls which
  values a *project* offers (status values carry a `SelectedInSchemes`
  relation).
- **Team Process** — a team can have its own set of `StoryStatus` values for
  its primary workitems, enforced on status change. Two related concepts
  ride along but are **advisory only** — the UI calls them out, the API
  does not enforce them: the team's **WIP cycle** (the subset of its
  statuses whose `RollupState` is Active) and its **WIP limit** (a cap on
  workitems in progress). Epics are not subject to team processes.

## What Agility Offers Instead of Enforcement

The product covers the workflow-enforcement need with softer mechanisms an
integration should know about:

- **Important fields (soft requirements).** A Scheme can flag attributes as
  important (`Scheme.ImportantFields`): the UI calls them out, and with
  `Scheme.IsRequiredAtClose` they become mandatory **when the asset is
  closed** — so a close can fail validation even though ordinary updates
  never did. The [`api/canbeimportant`](/apis/field-visibility/can-be-important)
  endpoint reports which attributes are eligible.
- **Closed assets accept backfill — and only backfill.** A Closed asset
  takes an attribute update without being reopened **only if the attribute
  had no value**; changing an existing value fails with
  `Violation'ClosedAssetCannotEditExistingValues` (both verified live —
  including that a value backfilled after close immediately becomes
  uneditable itself). The design intent is pushing missing values, not
  editing history.
- **Quick Transitions** — follow-up actions configured in the UI that run
  when an asset reaches a new Status or AssetState. They are product
  configuration, not part of the API surface.

## Useful Queries

```json
// everything still genuinely open, by rollup rather than name
{ "from": "PrimaryWorkitem",
  "filter": ["Status.RollupState!='Closed'", "AssetState='Active'"],
  "select": ["Number", "Name", "Status.Name"],
  "page": { "size": 100, "start": 0 } }
```

```json
// stale in-flight work: active rollup, untouched for 30 days
{ "from": "Story",
  "filter": ["Status.RollupState='Active'", "ChangeDateUTC<'2026-07-27'"],
  "select": ["Number", "Name", "Status.Name", "ChangeDateUTC"],
  "sort": ["ChangeDateUTC"],
  "page": { "size": 50, "start": 0 } }
```

## Related Topics

- Product feature: [Team Process management](https://docs.digital.ai/agility/docs/agility/team-process-management)
  and [What impact does defining Team Process have on my
  instance?](https://docs.digital.ai/agility/docs/agility/what-impact-does-defining-team-process-have-on-my-instance)
  — configuring the processes whose enforcement this page describes.

- [Assets](/concepts/assets) — AssetState vs Status
- [Operations](/concepts/operations) — how lifecycle actually changes
- [Reproducing reported numbers](/guides/reproducing-reported-numbers) — RollupState in reporting
- [Attribute types](/reference/attribute-types) — Rank and State types
