Skip to main content

Moments & History

Overview​

Agility does not overwrite data. Changes to assets are journaled, and each change is stamped with a moment — a chronologically increasing integer.

A few attributes are exceptions: rank order (Order) changes, for example, are not journaled at all — an asset's history never shows a rank move. There is no way to discover through the API which attributes are tracked.

A moment is the unit of time in the Agility data model. It is what makes the Hist namespace, versioned asset addresses and asof queries possible, and it is the single concept you must understand before you can read history correctly.

Moments Are Global, Not per-Asset​

This is the detail that trips up most integrations.

Moment numbers are sequential for the entire Agility instance, not for an individual asset. Any change to any asset, by any member, consumes the next moment number in the system.

Consider three changes made in this order:

OrderChangeMoment
1A Story is created203
2That Story's ToDo is updated204
3An unrelated Member's Name is updated205

The Story now has two entries in its own history — moment 203 and moment 204 — and the next change anywhere in the system will be moment 206.

Do not assume a contiguous sequence

Because moments are handed out globally, an individual asset's moments are almost always sparse. A Story might have moments 203, 204, 818, 874 and 903. The gaps are other people's changes to other assets.

Never compute "the previous version" by subtracting 1 from a moment, and never treat the count of moments between two versions as a measure of elapsed time or of how much changed. To move through an asset's versions, read its history and use the moments it actually reports.

Addressing a Version​

An asset is identified by its asset type and integer ID:

Member:20

A past version of an asset is identified by asset type, ID and moment:

Member:20:563

That reads as "the Member asset with ID 20, as it was at moment 563". The same three parts appear in the URL of a versioned asset, in the Moment path segment:

/VersionOne/rest-1.v1/Data/Member/20/563

Omit the moment and you address the asset as it is now:

/VersionOne/rest-1.v1/Data/Member/20

Relations follow the same rule. A relation attribute on a past version refers to the past version of its target asset, so a historical query gives you an internally consistent snapshot rather than old attributes stitched onto current related assets.

Reading History: The Hist Namespace​

rest-1.v1 exposes two query namespaces, and they differ only in whether you get one record or all of them:

NamespaceReturnsRoot element
DataThe current version of each matching assetAssets
HistEvery historical version of each matching assetHistory

So this returns one Member:

GET /VersionOne/rest-1.v1/Data/Member/20

and this returns every version that Member has ever had:

GET /VersionOne/rest-1.v1/Hist/Member/20

Each Asset element in a History response carries an href that includes the moment, giving you a direct, permanent address for that exact version.

History works at attribute granularity too. Asking for the history of a single attribute returns one record per change to that attribute, each with an href pointing at the attribute and an of pointing at the owning asset version:

GET /VersionOne/rest-1.v1/Hist/Member/20/Email

All of the ordinary query parameters — attribute selection, filtering, sorting, paging and text search — work against Hist in the same way they do against Data, subject to the same rule that list-oriented parameters only take effect on list-shaped requests. See the query grammar reference for the applicability matrix.

tip
ChangeDate is maintained by history

ChangeDate is populated by the history subsystem and records the wall-clock time at which a version was written. Select it whenever you need to correlate moments with real time, because a moment number on its own tells you ordering but not when.

Reading History as a Relation​

The Hist namespace is not the only way to reach history, and often not the most convenient. Every asset type also exposes history through ordinary relations, which means you can traverse it with the attribute-path syntax from any surface — including query.v1 and the Bulk API, neither of which has a Hist namespace of its own.

AttributeTypeFromGives you
HistoryRelation, multi-valuea current assetevery version of it
NowRelationa versionthe current asset — the reciprocal of History
PriorRelationa versionthe version immediately before it
ChangedByRelationa versionthe Member who made the change
ChangeDateDatea versionwhen the change happened
ChangeCommentTexta versionthe comment supplied with the change
MomentOpaquea versionthe moment itself

All seven are read-only as history attributes — you read them, you do not select them to write. ChangeComment is the exception worth knowing: you cannot set it by selecting it, but you can supply it at write time — ?comment= on rest-1.v1 or a ChangeComment key on api/asset — and it then appears here.

Prior is what makes before-and-after comparison possible in a single query: within one history row, Description is the value at that moment and Prior.Description is the value immediately before it. Now.Description reaches past both to the current value.

tip
Now. is the query.v1 equivalent of the # operator

On rest-1.v1 you write Parent#.Name to mean "the current value, not the value at this moment" — and you must percent-encode the # or lose it.

Now. does the same job as a plain relation traversal, so it needs no escaping and works anywhere attribute paths work.

Finding What Changed​

Query an asset's history as a nested result set, selecting both the value at each moment and the value before it:

{
"from": "Defect",
"select": [
{
"from": "History",
"select": ["Now.Name", "Prior.Description", "Description", "ChangedBy.Name"]
}
],
"filter": "Number=$Numbers",
"with": { "$Numbers": ["D-14519", "D-16384"] },
"page": { "start": 0, "size": 2 }
}

Each row where Prior.Description differs from Description is a change to that attribute, attributed to ChangedBy.Name.

Restoring a Previous Value​

There is no "revert" operation. Reading history and writing the old value back is the supported approach: find the moment where the attribute changed, take Prior.<Attribute>, and update the asset with it.

Strip the moment before you write

OID tokens in history results carry a moment suffix — Defect:1234:5678, not Defect:1234. That third segment identifies a version, and passing it as the from of an update targets a historical row rather than the live asset.

Take the first two segments only:

{ "from": "Defect:1234", "update": { "Description": "<the prior value>" } }

See addressing a version.

Point-in-Time Queries: asof​

Moments are precise but opaque — you rarely know the moment number you want. The asof query parameter lets you ask the question in terms of wall-clock time instead:

GET /VersionOne/rest-1.v1/Hist/Story/1364?asof=2005-08-03T17:30:00.00&sel=Name,ToDo,ChangeDate

Two rules govern the result:

  1. asof returns the last historical record on or before the given time. It does not require a change to have happened at that instant. In the example above, the Story was last changed at 13:09:05.983 on 2005-08-03; that is the version returned for an asof of 17:30, because nothing changed in between.
  2. asof overrides an explicit moment. If you supply both a Moment path segment and an asof parameter, the moment is ignored. Use one or the other, not both.

asof applies to single-attribute, single-asset and multi-asset historical queries alike, so you can reconstruct a whole project as it stood on a given day.

note
What asof accepts

asof takes either an integer moment or a datetime in invariant culture, for example 2005-08-03T17:30:00.00 — the same format ChangeDate returns. The full syntax of asof and of the other query string parameters is covered in the query grammar reference.

Practical Consequences​

  • Audit trails come for free. You do not need to build change tracking on top of Agility; ask Hist for the asset and select ChangeDate alongside the attributes you care about.
  • Trend and burndown data is a series of asof queries. To chart remaining effort over a sprint, issue one asof query per point on the x-axis rather than trying to reduce the raw history yourself.
  • Cache versioned URLs, not current ones. A URL containing a moment is immutable and safe to cache indefinitely. A URL without one is not.
  • Moments are not timestamps. Two changes made in the same second get different moments; moments from different Agility instances are unrelated and must never be compared.
  • History is voluminous. A Hist query with no filter returns every version of every matching asset. Page it, and select only the attributes you need.
  • Information model — the authoritative definition of asset type, attribute definition, asset, attribute and moment.
  • Query examples — worked Hist and asof queries with real response payloads.
  • REST API (rest-1.v1) — the endpoint surface that exposes all of this.
  • Query grammar — the syntax of asof, sel, where, sort and page.