# create-backup

Creates a **backup** of the database: a **full backup** of all of it, or an **increment** with what was written **after a previous backup**.

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

A backup holds the **state at its start**: the schemas registered and the events written up to then. **Writes go on while it runs**, and what they add is left for the next backup. Each backup names the event it **ended at**, which the next increment follows on, so a full backup and its increments form a sequence that **[`restore-backup`](/docs/eventsourcingdb/api/restore-backup)** takes in one request. **[Backup and Restore](/docs/eventsourcingdb/backup-and-restore)** explains how full backups and increments work together.

## Request

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

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `lastBackedUpEvent` | object or `null` | yes | The event the previous backup ended at, for an increment, or `null` for a full backup. It has no default: a body that leaves it out is refused rather than answered with a full backup, so that a client that has lost track of its previous backup notices. |
| `lastBackedUpEvent.id` | string | yes, unless `lastBackedUpEvent` is `null` | The ID of that event, a non-negative integer in a string, without a sign or a leading zero, such as `"41"`. |
| `lastBackedUpEvent.hash` | string | yes, unless `lastBackedUpEvent` is `null` | The hash of that event: 64 lowercase hexadecimal digits, prefixed with `esdb:hash:v2:` for a hash of version 2. |

For an increment, send the `id` and `hash` **exactly as the `lastBackedUpEvent` item of the previous backup** named them, whether that backup was a full backup or an increment itself.

To create a full backup and keep it in a file:

```shell
curl \
  -X POST \
  -H "authorization: Bearer secret" \
  -H "content-type: application/json" \
  -d '{"lastBackedUpEvent":null}' \
  http://localhost:3000/api/v2/management/create-backup \
  -o full-backup.ndjson
```

## Answer

The answer is a **stream of items**, one per line, and is itself the backup:

| Type | Data | When |
| --- | --- | --- |
| `backup` | `formatVersion`, the version of the format, which is `2`, and `lastBackedUpEvent`, the event the backup follows on as the request named it, or `null` for a full backup | Once, as the first item. |
| `eventSchema` | `type`, an event type, and `schema`, its schema | For every event type that has a schema at the start of the backup, before the events. An increment holds every schema again. |
| `event` | the event, as reading events returns it | For every event of the backup, in the order in which the events were written. |
| `lastBackedUpEvent` | `id` and `hash` of the event the backup ended at | Once, after the events: the last event of the backup, or, for an increment without new events, the event it follows on. A full backup of a database without events has none, and the next backup is a full backup again. |
| `error` | `code`, `description`, and `details` | When the backup cannot begin or cannot be completed, 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 restore skips the heartbeats, so you can **keep the stream exactly as you received it**. **Keep a backup only if it ends with the `end` item and holds no `error` item**: a restore refuses any other.

For a database with one schema and one event, the request above is answered with:

```json
{"type":"backup","data":{"formatVersion":2,"lastBackedUpEvent":null}}
{"type":"eventSchema","data":{"type":"io.eventsourcingdb.library.book-acquired","schema":{"additionalProperties":false,"properties":{"title":{"type":"string"}},"required":["title"],"type":"object"}}}
{"type":"event","data":{"specversion":"1.0","id":"0","source":"https://library.eventsourcingdb.io","subject":"/books/42","type":"io.eventsourcingdb.library.book-acquired","time":"2026-10-28T04:31:16.142581Z","predecessorhash":"0000000000000000000000000000000000000000000000000000000000000000","datacontenttype":"application/json","data":{"title":"2001 – A Space Odyssey"},"traceparent":null,"tracestate":null,"hash":"esdb:hash:v2:3fe219db06819ac40fd287a9cde02c071e9ee4b846bb262dd5b7c67569c2dc66","signature":null}}
{"type":"lastBackedUpEvent","data":{"id":"0","hash":"esdb:hash:v2:3fe219db06819ac40fd287a9cde02c071e9ee4b846bb262dd5b7c67569c2dc66"}}
{"type":"end","data":{}}
```

## Errors

The backup reports these errors as an `error` item, followed by the `end` item. An error that keeps the backup from beginning is the only item before the `end`; one that keeps it from being completed follows the items sent so far:

- **[`last-backed-up-event-missing`](/docs/eventsourcingdb/errors/last-backed-up-event-missing)**: The request leaves out `lastBackedUpEvent`.
- **[`event-id-missing`](/docs/eventsourcingdb/errors/event-id-missing)**: `lastBackedUpEvent` 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)**: `lastBackedUpEvent` 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-backed-up-event-mismatched`](/docs/eventsourcingdb/errors/last-backed-up-event-mismatched)**: The database holds an event with that `id`, but with another hash, for example because the request goes to another database than the one the previous backup came from.
- **[`last-backed-up-event-unknown`](/docs/eventsourcingdb/errors/last-backed-up-event-unknown)**: The database holds no event with that `id`.
- **[`restore-in-progress`](/docs/eventsourcingdb/errors/restore-in-progress)**: A restore is running when the backup is requested. A backup that began before the restore waits until it is over instead.
- **[`file-unreadable`](/docs/eventsourcingdb/errors/file-unreadable)**: A file of the store that the backup 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 while the backup ran. Before the stream begins, 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, `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.
