Export stage history
An export can include the full workflow history of each item: every stage the item went through, in order, and its annotations as they were when it left each stage. Use it to see what was delivered at a given stage, measure how much review changed, or put an item back the way it was at an earlier stage.
Turn it on
- Export dialog: tick Include full stage history.
- API: set
include_stage_history: trueon Create an export. - darwin-py:
darwin dataset export <dataset> <release> --version 2.0 --include-stage-history darwin dataset pull <dataset>:<release> --only-annotations
Stage history needs Darwin JSON 2.0. Turn on worker metadata (include_authorship in the API) to also get names, emails, annotators and reviewers.
What you get
Next to the usual annotation files, the export has one folder per item:
stage_history/
<item id>/
visits.json # every stage visit of the item, in order
<stage instance id>.json # annotations at the end of that visit (Darwin JSON 2.0)
After darwin dataset pull, it's under <dataset>/releases/<release>/stage_history/.
visits.json
{
"version": "1.1",
"item_id": "0191...",
"visits": [
{
"stage_instance_id": "8f2c...",
"parent_stage_id": null,
"stage": {"id": "...", "name": "Annotate", "type": "annotate"},
"status": "completed",
"entered_at": "2026-11-02T09:14:03Z",
"exited_at": "2026-11-02T10:02:41Z",
"exited_by": {"type": "user", "id": 123},
"exit_snapshot": {"status": "exported", "file": "8f2c....json"}
},
{
"stage_instance_id": "a71d...",
"parent_stage_id": null,
"stage": {"id": "...", "name": "Consensus", "type": "consensus_entrypoint"},
"status": "completed",
"entered_at": "2026-11-02T10:02:41Z",
"exited_at": "2026-11-02T10:02:41Z",
"exited_by": {"type": "system", "id": null},
"exit_snapshot": {"status": "none", "file": null, "reason": "pass_through"}
}
]
}| Field | Meaning |
|---|---|
parent_stage_id | For an annotator's visit inside a consensus stage, the consensus stage it belongs to; otherwise null. |
status | completed (left through the workflow), overriden (moved with set stage) or current (the item is still there) |
exited_by | Who ended the visit: user, model or system, with its id. With include_authorship, users also have email and full_name. |
exit_snapshot.status | exported: file holds the annotations at exit. none: no snapshot; see reason. |
exit_snapshot.reason | current: the visit is still open. pass_through: a stage that never changes annotations (consensus entry, AI review). not_recorded: no snapshot was stored, typically older visits. |
Snapshot files
Each <stage instance id>.json is a normal Darwin JSON 2.0 file with the item's annotations at the end of that visit: all frames, classes, properties and item properties, including classes and properties deleted since. With include_authorship, each annotation lists the annotators and reviewers it had at that point.
Restore an item to an earlier stage
- Export and pull with stage history (above).
- Open
stage_history/<item id>/visits.jsonand pick the visit to go back to. - Import its snapshot over the item:
darwin dataset import <dataset> darwin stage_history/<item id>/<stage instance id>.json --overwrite --yes
The item's annotations, annotation properties and item properties now match that snapshot. Annotations are recreated with new IDs.
In Python:
from darwin.dataset.stage_history import read_stage_history
visits = read_stage_history(release_path / "stage_history" / item_id)
for visit in visits:
print(visit.stage["name"], visit.exited_at, visit.exit_snapshot["status"], visit.reason)
annotations = visit.snapshot() # parsed Darwin JSON, or NoneGood to know
- History starts when snapshots were recorded. Moves made with set stage (UI or API) have snapshots from 19 October 2026. Earlier visits moved that way show
not_recorded. Annotators and reviewers in snapshots start with snapshots taken after the November 2026 release. - Size. A clip with a long history can add tens of MB to the export. Filter the export to the items you need.
- IDs across visits. An import with
--overwritegives every annotation a new ID, so matching annotations between visits by ID only works between overwrite imports.
Updated 26 minutes ago
