# register-event-schema

Registers a **schema** for an **event type**, which every event of the type has to match.

- **Route:** `POST /api/v2/data/register-event-schema`
- **Category:** `data`
- **Authentication:** required, with the API token
- **Answer:** a stream of NDJSON

An **[event type](/docs/eventsourcingdb/event-types)** has **at most one schema**, which applies to **all of its events, past and future**, and which can be **neither replaced nor removed** once it is registered. So before it registers a schema, the database **checks every event of the type that is stored already**, and every one that is written while the check runs, and refuses the schema if one of them does not match; reads and writes go on meanwhile. For data of another shape, introduce a **[new event type](/docs/eventsourcingdb/event-types#versioning)**. The tutorial **[Registering Event Schemas](/learn/tutorials/first-steps-with-eventsourcingdb/registering-event-schemas)** walks through it step by step.

## Request

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

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `type` | string | yes | The event type to register the schema for, a reverse domain name such as `io.eventsourcingdb.library.book-acquired`. |
| `schema` | object | yes | The **[JSON Schema](https://json-schema.org/)** that the data of the events of the type has to match, with `"type": "object"` at its top level. It may refer to its own parts with `$ref`, such as `#/$defs/address`, but not to other documents, nor back to a part that the reference lies within, as a schema that describes a tree does, nor to an anchor with `$dynamicRef` or `$recursiveRef`. It may reach at most 10,000 subschemas, counting a referenced one each time it is referenced, and each of its numbers may have at most 1,000 digits before and after its decimal point. |

To register a schema for `io.eventsourcingdb.library.book-acquired` that requires a title and an author:

```shell
curl \
  -X POST \
  -H "authorization: Bearer secret" \
  -H "content-type: application/json" \
  -d '{"type":"io.eventsourcingdb.library.book-acquired","schema":{"type":"object","properties":{"title":{"type":"string"},"author":{"type":"string"}},"required":["title","author"]}}' \
  http://localhost:3000/api/v2/data/register-event-schema
```

## Answer

The answer is a **stream of items**, one per line. A registered schema is an `event` item:

| Type | Data | When |
| --- | --- | --- |
| `event` | A **[CloudEvent](/docs/eventsourcingdb/cloud-events)** of the type `io.eventsourcingdb.api.event-schema-registered`, whose `data` holds the `type` and the `schema` that were registered | Once, when the schema is registered. |
| `error` | `code`, `description`, and `details` | When the schema cannot be registered, see **[Errors](#errors)**. |
| `heartbeat` | empty | While the registration takes a while, such as while it checks the stored events of the type, 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)**. Either the `event` item or an `error` item comes, never both.

For an event type that has no schema yet, and no events, the request above is answered with:

```json
{"type":"event","data":{"specversion":"1.0","id":"01a180c5-c8f9-7966-9f93-a68d5968243b","source":"https://www.eventsourcingdb.io","type":"io.eventsourcingdb.api.event-schema-registered","subject":"/api/v2/data/register-event-schema","time":"2026-10-28T04:30:08.121617Z","datacontenttype":"application/json","data":{"type":"io.eventsourcingdb.library.book-acquired","schema":{"properties":{"author":{"type":"string"},"title":{"type":"string"}},"required":["title","author"],"type":"object"}}}}
{"type":"end","data":{}}
```

## Errors

The registration reports these errors as an `error` item, followed by the `end` item. The event type and the schema are checked **before the database is asked**, so the first six are reported whatever the database holds, even in read-only mode:

- **[`event-type-missing`](/docs/eventsourcingdb/errors/event-type-missing)**: The request leaves out the event type, or sends it as an empty string.
- **[`event-type-malformed`](/docs/eventsourcingdb/errors/event-type-malformed)**: The event type is not a reverse domain name, such as `BookAcquired`.
- **[`event-schema-missing`](/docs/eventsourcingdb/errors/event-schema-missing)**: The request leaves out the schema.
- **[`event-schema-malformed`](/docs/eventsourcingdb/errors/event-schema-malformed)**: The schema is not a valid JSON Schema, for example because it refers to another document, or holds a number with more than 1,000 digits before or after its decimal point.
- **[`event-schema-not-object`](/docs/eventsourcingdb/errors/event-schema-not-object)**: The schema does not have `"type": "object"` at its top level.
- **[`event-schema-too-complex`](/docs/eventsourcingdb/errors/event-schema-too-complex)**: The schema refers back to a part of itself, such as one that describes a tree, uses `$dynamicRef` or `$recursiveRef` to refer to an anchor, or reaches more than 10,000 subschemas, counting a referenced one each time it is referenced.
- **[`event-schema-already-registered`](/docs/eventsourcingdb/errors/event-schema-already-registered)**: The event type has a schema already, even if it is the same one.
- **[`event-schema-violated`](/docs/eventsourcingdb/errors/event-schema-violated)**: An event of the type, stored already or written during the check, does not match the schema.
- **[`store-read-only`](/docs/eventsourcingdb/errors/store-read-only)**: The database is in **[read-only mode](/docs/eventsourcingdb/read-only-mode)**, which refuses registrations, since they are writes.
- **[`disk-full`](/docs/eventsourcingdb/errors/disk-full)**: The disk of the data directory is almost full, so the database refuses writes until there is room again.
- **[`restore-in-progress`](/docs/eventsourcingdb/errors/restore-in-progress)**: A restore is running, which refuses writes until it has finished.
- **[`file-unreadable`](/docs/eventsourcingdb/errors/file-unreadable)**: A file of the store that the registration needs is damaged, such as an events file whose checksum no longer matches.
- **[`store-failed`](/docs/eventsourcingdb/errors/store-failed)**: The database has failed, after storing an earlier write or a restore failed, and refuses writes.
- **[`server-shutting-down`](/docs/eventsourcingdb/errors/server-shutting-down)**: The server began to shut down after the stream had sent its first 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 missing or no JSON object of the shape above, for example with a field the route does not know, such as `eventType`, which the v1 API took instead of `type`, with `null` as a value, or with a `schema` that is not a JSON object.
- **`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; 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.
