Docs / EventSourcingDB / API / create-backup

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 takes in one request. 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:

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.
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. 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:

{"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: The request leaves out lastBackedUpEvent.
  • event-id-missing: lastBackedUpEvent leaves out its id, or gives an empty one.
  • 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: lastBackedUpEvent leaves out its hash, or gives an empty one.
  • event-hash-malformed: The hash is not a hash, such as one that was cut short.
  • 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: The database holds no event with that id.
  • 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: A file of the store that the backup needs is damaged, such as an events file whose checksum no longer matches.
  • store-failed: The store refuses reads for good, after a restore that could not empty it again.
  • 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:

  • 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.
  • 429: Too many requests were sent, or the client already holds 256 open streams, see 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.