Read an Entity's Edit History
Every change in Geo is published as an edit, recorded as one or more EditVersion rows. Values and relations track which edit they came from. That means you can answer questions like:
- Who last changed this entity?
- When was this value first published?
- What did this entity look like a month ago?
This recipe walks the read patterns for all three.
Concept — versioned data
In GRC-20 (Req #10), the 9 op types are: CreateEntity, UpdateEntity, DeleteEntity, RestoreEntity, CreateRelation, UpdateRelation, DeleteRelation, RestoreRelation, CreateValueRef. Each one writes a row to:
EditVersion— one row per published edit (the "commit")ValueVersion— historical state of every value, withvalidFromKey/validToKeydefining when it was currentRelationVersion— same idea for relations
A row is current when its validToKey is null. When superseded by a later edit, validToKey gets set and a new row is inserted with a fresh validFromKey.
1. List recent edits across the graph
query RecentEdits {
editVersions(first: 10, orderBy: BLOCK_NUMBER_DESC) {
editId
name
blockNumber
sequence
createdAt
createdById
}
}
{
"data": {
"editVersions": [
{
"editId": "5e736eea...",
"name": "Hospitals (10 entities)",
"blockNumber": "128935",
"sequence": "1",
"createdAt": "2026-05-05T16:57:09",
"createdById": "..."
},
{
"editId": "aa6c9086...",
"name": "Bounty links for: Hospitals (10 entities)",
"blockNumber": "128934",
"sequence": "0",
"createdAt": "2026-05-05T16:56:54"
}
]
}
}
2. History of a specific entity's values
valueVersions filtered by entityId returns every version of every value ever attached to that entity:
query EntityValueHistory($id: UUID!) {
valueVersions(
first: 50
filter: { entityId: { is: $id } }
) {
id
propertyId
text
integer
date
datetime
validFromKey
validToKey
}
}
{ "id": "9e49e5a95d2a41a6ba90cd84944080b5" }
{
"data": {
"valueVersions": [
{
"id": "805736af...",
"propertyId": "41aa3d98...",
"date": "2022-08-22Z",
"validFromKey": "457237923364864",
"validToKey": null
},
{
"id": "9860640572...",
"propertyId": "eed38e74...",
"text": "https://stability.ai/stable-image",
"validFromKey": "457237923364864",
"validToKey": null
}
]
}
}
Reading the response:
validToKey: null→ this version is current.validToKey: "..."→ this version was superseded by a later edit. The newer version is somewhere else in the result set with a matchingvalidFromKey.
To find the "current value of property P on entity E":
const current = data.valueVersions.find(
v => v.propertyId === P && v.validToKey === null
);
To trace history of one property over time:
const history = data.valueVersions
.filter(v => v.propertyId === P)
.sort((a, b) => a.validFromKey.localeCompare(b.validFromKey));
// First entry was the original; later entries are subsequent edits
3. History of a specific entity's relations
Same shape, different table:
query EntityRelationHistory($id: UUID!) {
relationVersions(
first: 50
filter: { fromEntityId: { is: $id } }
) {
id
typeId
fromEntityId
toEntityId
validFromKey
validToKey
}
}
Useful to answer: "When did this entity stop being tagged as a Project?" (Look for a validToKey set on the Types relation.)
4. What did this edit do? Find all rows from one edit
Every value/relation row carries an implicit edit reference via validFromKey. To list everything written by edit X, filter on its validFromKey:
query EditChangelog($validFromKey: String!) {
valueVersions(first: 100 filter: { validFromKey: { is: $validFromKey } }) {
entityId
propertyId
text
date
}
relationVersions(first: 100 filter: { validFromKey: { is: $validFromKey } }) {
fromEntityId
typeId
toEntityId
}
}
This is how the editVersions field links to its actual content — by shared validFromKey.
Notes
validFromKey/validToKeyare opaque string keys, not human timestamps. Use them for ordering and equality, not parsing. To get a human timestamp, join througheditVersionson the matching block number.createdByIdis the personal space ID of the publisher (the wallet's identity), not their wallet address. To resolve to a human name, look up the entity by ID.- Deletion is a versioning event:
DeleteEntityops setvalidToKeyon existing values/relations of the entity. The rows aren't physically removed — you can still read them withvalidToKey: { isNull: false }. - Restore = a new version:
RestoreEntitycreates freshvalidFromKeyrows pointing back to the previous data. Trace viavalueVersionsordering. - Performance:
valueVersionsandrelationVersionsare large tables. Always filter byentityId(or another narrow predicate) — never paginate the entire table.