# Formats

SynthEvents writes a data set in one of **two formats**: as **plain CloudEvents**, or as a **backup for EventSourcingDB**. Both carry **the same events** and differ only in their **envelope**. You choose one with the `format` parameter of a request, `cloudevents` or `eventsourcingdb`.

## What Both Formats Have in Common

Both formats are **[NDJSON](https://github.com/ndjson)**: one JSON object per line, each line ending with a newline. A data set is sent with the content type `application/x-ndjson`, and its file name ends with `.cloudevents.ndjson` or `.eventsourcingdb.ndjson`.

The events are written **the way EventSourcingDB writes them**, so that a data set looks like data that came out of it:

- **`time`** is in **UTC**, to the **nanosecond**, without trailing zeros, such as `2025-01-01T00:00:00Z` or `2025-01-01T00:00:00.009892423Z`. All events of one command share the same time.
- **`id`** counts from **`0`** in the order the events happened, and time never runs backward.
- **`source`** names SynthEvents and every parameter of the request except the format (see **[The Source as a Recipe](/docs/synthevents/generating-data-sets#the-source-as-a-recipe)**).
- **`type`** is **`io.synthevents.<domain>.<event>`**, such as `io.synthevents.library.book-borrowed`.
- **`data`** is in the **normal form** of EventSourcingDB: compact, with its keys in alphabetical order, and with `<`, `>`, and `&` escaped as `\u003c`, `\u003e`, and `\u0026`.

For the fields of a CloudEvent, see **[CloudEvents](/docs/eventsourcingdb/cloud-events)**.

## Plain CloudEvents

With the format **`cloudevents`**, every line is **one CloudEvent**, with its fields in the order in which CloudEvents lists them, which is also the order in which the API v2 of EventSourcingDB writes an event when you read it: `specversion`, `id`, `source`, `subject`, `type`, `time`, `datacontenttype`, and `data`. There are **no hash fields**.

```json
{"specversion":"1.0","id":"48","source":"tag:synthevents.io,2026:synthevents;version=golden;domain=library;scenario=everyday;from=2025-01-01T00:00:00Z;to=2026-01-01T00:00:00Z;eventCount=500;seed=golden","subject":"/books/dd9ceeee-febe-436b-9b66-17334b5a7abc","type":"io.synthevents.library.book-borrowed","time":"2025-01-01T17:12:14.331645957Z","datacontenttype":"application/json","data":{"borrowedAt":"2025-01-01T17:12:14.331Z","borrowedBy":"/readers/6e682d89-e9e9-4d55-8672-2da67f8bf38a","borrowedUntil":"2025-01-22"}}
```

*Note that this event comes from a test of SynthEvents, which generates its data sets with the version `golden` and the seed `golden`. In a data set of the public service, `version` names the version of SynthEvents that generated it.*

Use this format for anything other than EventSourcingDB: to read the events line by line, to import them into another system, or to process them with tools such as `jq`.

## Backup for EventSourcingDB

With the format **`eventsourcingdb`**, the data set is a **backup of EventSourcingDB**, ready to be restored into an **empty** instance. Like every backup, it has two kinds of lines, and its lines carry their content in `payload`:

- First, one **`schema`** line for **every event type of the domain**, in alphabetical order of their types, with the **JSON schema** of its data. After a restore, EventSourcingDB checks every event you write of one of these types against its schema.
- Then, one **`event`** line for every event, with the event and its **`hash`**. The fields of the event are in the order of a backup: `source`, `subject`, `type`, `specversion`, `id`, `time`, `datacontenttype`, `predecessorhash`, and `data`.

Shortened, a backup looks like this:

```json
{"type":"schema","payload":{"eventType":"io.synthevents.library.book-acquired","schema":{...}}}
...
{"type":"event","payload":{"event":{"source":"...","subject":"/books/...","type":"io.synthevents.library.book-imported","specversion":"1.0","id":"0","time":"...","datacontenttype":"application/json","predecessorhash":"0000000000000000000000000000000000000000000000000000000000000000","data":{...}},"hash":"esdb:hash:v2:..."}}
{"type":"event","payload":{"event":{"source":"...","subject":"/books/...","type":"io.synthevents.library.book-imported","specversion":"1.0","id":"1","time":"...","datacontenttype":"application/json","predecessorhash":"esdb:hash:v2:...","data":{...}},"hash":"esdb:hash:v2:..."}}
...
```

The events form a **hash chain**: the `predecessorhash` of every event is the `hash` of the event before it, and the first event has 64 zeros instead. The hashes are of **version 2**, which starts every hash with `esdb:hash:v2:`. On a restore, EventSourcingDB computes every hash again and checks it.

If you restore a data set and then back the instance up again, the backup is the data set again, **byte for byte**.

> **EventSourcingDB 1.3 or Later**
>
> The hash of version 2 comes with **EventSourcingDB 1.3**, so a data set in this format needs version 1.3 or later. Earlier versions are not supported. Until version 1.3 is released, use the image **`thenativeweb/eventsourcingdb:preview`**.

To restore a data set, send it to the `/api/v1/restore` endpoint of EventSourcingDB:

```shell
curl \
  -i \
  -X POST \
  -H "authorization: Bearer <API_TOKEN>" \
  --data-binary "@backup.ndjson" \
  http://localhost:3000/api/v1/restore
```

A restore only works on an **empty database**. For details, see **[Backup and Restore](/docs/eventsourcingdb/backup-and-restore)**.

## Choosing a Format

- To load a data set into **EventSourcingDB**, choose **`eventsourcingdb`**. A restore keeps the IDs, times, and hashes of the events, so the store holds them exactly as SynthEvents generated them, including the schemas of their event types.
- For **anything else**, choose **`cloudevents`**.
