# audit-history

Audits the **chain of events**: checks that every event **matches its hash** and **follows on the one before it**, anchored at the event a previous audit ended at.

- **Route:** `POST /api/v2/management/audit-history`
- **Category:** `management`
- **Authentication:** required, with the API token
- **Answer:** a stream of NDJSON

Every event names the hash of the event before it, so the events form a **chain of hashes**. An audit checks that chain from the first event up to the **latest one at its start**, and checks that the event the **previous audit ended at still has the hash** it had then, so a successful audit confirms that **nothing has changed up to the previous one**. Writes go on while it runs, and the events they add are left for the next audit. A restore does not end an audit: the audit waits until the restore is over. **[Auditing the Event Store](/docs/eventsourcingdb/auditing-the-event-store)** explains how to audit regularly and what to do when an audit fails.

## Request

The body is a JSON object, with the content type `application/json`:

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `lastAuditedEvent` | object or `null` | yes | The event the previous audit ended at, or `null` for a first audit. It has no default: a body that leaves it out is refused rather than taken for a first audit, so that an audit that has lost its anchor does not skip checking it. |
| `lastAuditedEvent.id` | string | yes, unless `lastAuditedEvent` is `null` | The ID of that event, a non-negative integer in a string, without a sign or a leading zero, such as `"41"`. |
| `lastAuditedEvent.hash` | string | yes, unless `lastAuditedEvent` is `null` | The hash of that event: 64 lowercase hexadecimal digits, prefixed with `esdb:hash:v2:` for a hash of version 2. |

Send the `id` and `hash` **exactly as the `lastAuditedEvent` item of the previous audit** named them. To audit the store, anchored at the event `0` that a previous audit ended at:

```shell
curl \
  -X POST \
  -H "authorization: Bearer secret" \
  -H "content-type: application/json" \
  -d '{
        "lastAuditedEvent": {
          "id": "0",
          "hash": "esdb:hash:v2:3fe219db06819ac40fd287a9cde02c071e9ee4b846bb262dd5b7c67569c2dc66"
        }
      }' \
  http://localhost:3000/api/v2/management/audit-history
```

## Answer

The answer is a **stream of items**, one per line. A successful audit ends with a `lastAuditedEvent` item:

| Type | Data | When |
| --- | --- | --- |
| `lastAuditedEvent` | `id` and `hash` of the latest event audited | Once, before the `end` item, when the audit succeeded and the database holds events. Keep it as the anchor of the next audit. |
| `error` | `code`, `description`, and `details` | When the audit fails, or cannot run, see **[Errors](#errors)**. |
| `heartbeat` | empty | While nothing else is sent for a while, to keep the connection alive. |
| `end` | empty | Once, as the last item, when the stream ends cleanly. |

**Heartbeats and the `end` item** work as on every stream of the API, see **[Streams](/docs/eventsourcingdb/api#streams)**. A first audit of a database without events is answered with the `end` item alone, and the next audit sends `null` again. **The audit succeeded only if the stream ends with the `end` item and holds no `error` item.**

For a database that holds the events `0` and `1`, the request above is answered with:

```json
{"type":"lastAuditedEvent","data":{"id":"1","hash":"esdb:hash:v2:32f739af34b563c90aa33fc47d39a061db72091aa0fd83c70a322b51bea5f22a"}}
{"type":"end","data":{}}
```

## Errors

The audit reports these errors as an `error` item, followed by the `end` item:

- **[`last-audited-event-missing`](/docs/eventsourcingdb/errors/last-audited-event-missing)**: The request leaves out `lastAuditedEvent`.
- **[`event-id-missing`](/docs/eventsourcingdb/errors/event-id-missing)**: `lastAuditedEvent` leaves out its `id`, or gives an empty one.
- **[`event-id-malformed`](/docs/eventsourcingdb/errors/event-id-malformed)**: The `id` is not a non-negative integer in a string, without a sign or a leading zero, such as `forty-one` or `041`.
- **[`event-hash-missing`](/docs/eventsourcingdb/errors/event-hash-missing)**: `lastAuditedEvent` leaves out its `hash`, or gives an empty one.
- **[`event-hash-malformed`](/docs/eventsourcingdb/errors/event-hash-malformed)**: The `hash` is not a hash, such as one that was cut short.
- **[`last-audited-event-mismatched`](/docs/eventsourcingdb/errors/last-audited-event-mismatched)**: The event with that `id` has another hash in the database, so the events changed since the previous audit, or the anchor does not come unchanged from this database.
- **[`last-audited-event-unknown`](/docs/eventsourcingdb/errors/last-audited-event-unknown)**: The database holds no event with that `id`. This is reported only once the whole chain was checked.
- **[`event-chain-broken`](/docs/eventsourcingdb/errors/event-chain-broken)**: An event does not follow on the one before it, or does not match its hash, so the stored events were changed or damaged.
- **[`event-hash-version-unknown`](/docs/eventsourcingdb/errors/event-hash-version-unknown)**: An event has a hash of a version that this version does not know.
- **[`file-unreadable`](/docs/eventsourcingdb/errors/file-unreadable)**: A file of the store that the audit needs is damaged, such as an events file whose checksum no longer matches.
- **[`store-failed`](/docs/eventsourcingdb/errors/store-failed)**: The store refuses reads for good, after a restore that could not empty it again.
- **[`server-shutting-down`](/docs/eventsourcingdb/errors/server-shutting-down)**: The server began to shut down after the audit had sent a heartbeat. Before that, the server answers with `503` instead.

## Status Codes

Before the stream begins, the server answers with a status code other than `200` where it cannot take the request at all, as described in **[Errors](/docs/eventsourcingdb/api#errors)**:

- **`400`**: The content type of the body is not `application/json`, or the body is no JSON object of the shape above, for example an empty body, a field the route does not know, such as `latestEventId` of the v1 API, `null` as the `id` or `hash`, or an `id` given as a number rather than a string.
- **`401`**: The API token is missing or wrong.
- **`408`**: The body did not arrive in time.
- **`413`**: The body is larger than its **[limit](/docs/eventsourcingdb/limits)**.
- **`429`**: Too many requests were sent, or the client already holds 256 open streams, see **[Rate Limiting](/docs/eventsourcingdb/rate-limiting)**; the answer says when to try again.
- **`500`**: The server failed unexpectedly before the stream began. Once it has begun, the stream breaks off without its `end` item instead.
- **`503`**: The server is shutting down, or began to shut down before the stream began.
