# DESCRIBE HISTORY: the table's versions, newest first — `version`, `timestamp`, `operation`

`POST /datanotes/operations/describe_history/` (operation `describe_history`)

DESCRIBE HISTORY: the table's versions, newest first — `version`, `timestamp`, `operation`
(CREATE TABLE, WRITE, UPDATE, DELETE, MERGE, ALTER TABLE, ADD COLUMNS, RESTORE, UNDO, …;
`EXTERNAL_EDIT` = changed outside the engine: an editor, git, sync), `operation_metrics` and `migration_id` (undo).
An EXTERNAL_EDIT's `operation_metrics.violations` counts the rules this table's rows break because of the hand edits it holds (each once): the edited rows whose front matter changed and the rows of the table linking to them, a removed or renamed row included (rows of other tables count in their table; a body-only edit is not checked); every rule its rows break when its definition was edited in metastore.yaml. Absent when nothing was checked, or when the edits were logged right before an engine write (the change feed has them); check_table lists the rows invalid now.
Works for dropped tables too (full `catalog.schema.table`). Use a version with `select_rows`
(time travel), `table_changes` or `restore_table`.
Without `table`: the history of the whole database — the commits of every table with a log (dropped
ones included), newest first, each with its `table` (the result's `tables` lists the logged tables).
An operation that changed several tables (a cascade, inverse links, restore_database) commits to each
with the same `migration_id`, and the operations of a `batch` carry its id as `batch`: their commits list
in `tables` every table that operation (or batch) committed to. Pick a moment for `restore_database`
from it (`timestamp`, or `before_migration` with a `migration_id` or a `batch`).
`limit` (default 50) counts commits; `since` (ISO date or date-time): only commits at or after it.

## Request body

`application/json`

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `table` | `string \| null` | no | `null` | A table; omitted, the whole database. |
| `limit` | `integer \| null` | no | `null` |  |
| `since` | `string \| null` | no | `null` |  |

## Responses

| Status | Description | Body |
| --- | --- | --- |
| 200 | Done. | `object` (application/json) |
| 400 | Bad input: the body is not a JSON object, or an argument is unknown, missing or of the wrong type. | `Error` (application/json) |
| 401 | Missing or wrong bearer token. | `Error` (application/json) |
| 404 | Unknown table, view, migration, note or folder. | `Error` (application/json) |
| 409 | Ambiguous table name: it matches several tables (`paths` or `tables` lists them). | `AmbiguousName` (application/json) |
| 500 | Unexpected error. | `Error` (application/json) |

The schemas named here are in [openapi.json](https://datanotes.dev/openapi.json) (`components.schemas`).
