Query versions
Draft/publish lifecycle for saved queries: version history, revert, fork, and publish.
GET/api/queries/{id}/version
Full version history, oldest first, each entry annotated with its 1-indexed version number.
Response
[
{
"version": 1,
"change_id": 901,
"user_id": 12,
"change": { "query": "select 1" },
"created_at": "2026-06-01T00:00:00Z"
}
]POST/api/queries/{id}/publish
Publish the current draft — the first publish, or "publish changes" on an already-live query. Requires the row's current working_version as a concurrency guard.
Request
{ "expected_version": 4 }Response
{ "id": 42, "published": true, "has_unpublished_changes": false, "working_version": 5, "...": "..." }POST/api/queries/{id}/revert
Apply a historical snapshot back onto the live query row. Supply exactly one of change_id (a changes.id) or version (the 1-indexed number from GET .../version) — supplying both is a 400.
Request
{ "version": 3 }Response
{ "id": 42, "query": "select 1 -- reverted", "working_version": 6, "...": "..." }POST/api/queries/{id}/fork
Create a new query owned by the caller, copying data_source_id/query/options from the source. Name becomes "Copy of <name>"; the fork always starts as an unpublished draft. Only View access to the source is required.
Response
{ "id": 88, "name": "Copy of Weekly signups", "published": false, "...": "..." }