> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Event streaming overview

> The shared event-streaming contract for Lerian products: the CloudEvents envelope, topic and type conventions, tenancy, schema versioning, and delivery guarantees.

Lerian products emit domain events — past-tense business facts such as an account being created or a credential being issued — onto a shared streaming backbone. Any service or downstream subscriber consumes them without coupling to the producer's internal APIs. This page describes the wire contract every event on the platform follows. The per-product pages list the concrete events each system emits and consumes.

## Transport

Events travel as **CloudEvents 1.0** messages in **binary content mode** over **Kafka**. Binary mode puts the CloudEvents context attributes in Kafka record headers, each prefixed with `ce-`, and the event body in the record value as JSON. A consumer reads routing and identity from the headers without deserializing the payload.

## The envelope

Every record carries these CloudEvents headers.

| Header               | Present  | Carries                                              |
| -------------------- | -------- | ---------------------------------------------------- |
| `ce-specversion`     | Always   | CloudEvents spec version — `1.0`.                    |
| `ce-id`              | Always   | Unique event id (UUIDv7). Deduplicate on this value. |
| `ce-source`          | Always   | The producing service.                               |
| `ce-type`            | Always   | Event type — `studio.lerian.<resource>.<event>`.     |
| `ce-time`            | Always   | Emission timestamp (RFC 3339).                       |
| `ce-resourcetype`    | Always   | The resource — for example `account`.                |
| `ce-eventtype`       | Always   | The event — for example `created`.                   |
| `ce-schemaversion`   | Always   | Payload schema version.                              |
| `ce-subject`         | When set | The aggregate id the event concerns.                 |
| `ce-tenantid`        | When set | The owning tenant; omitted for single-tenant scope.  |
| `ce-datacontenttype` | When set | Body media type — `application/json`.                |

## Event type

The `ce-type` header names the event as `studio.lerian.<resource>.<event>`. An account creation in the ledger is `studio.lerian.account.created`. The two segments also appear on their own in `ce-resourcetype` and `ce-eventtype`, so a consumer filters on either the full type or its parts.

## Topic naming

Every event lands on a Kafka topic named for the resource and event it carries, under a shared platform prefix:

```
lerian.streaming.<resource>.<event>
```

An account creation in the ledger lands on `lerian.streaming.account.created`; a holder creation in the CRM on `lerian.streaming.holder.created`. The prefix is identical across every Lerian producer, so a consumer subscribes by resource and event without needing to know which service emitted the record. The topic name carries no version suffix — the payload schema version travels in the `ce-schemaversion` header (see [Schema versioning](#schema-versioning)), so a schema change never renames a topic.

## Source

`ce-source` identifies the producing service and is **deployment-configured** through the `STREAMING_CLOUDEVENTS_SOURCE` environment variable; it is not hard-coded in the product. It records where a record originated, for auditing and consumer-side routing. The topic name does not derive from it — topics use the shared `lerian.streaming.` prefix regardless of source — so a deployment is free to set any stable, descriptive source value.

| Producer     | Default source        |
| ------------ | --------------------- |
| Midaz ledger | `lerian.midaz.ledger` |
| Midaz CRM    | `lerian.midaz.crm`    |

## Subject and tenant

`ce-subject` carries the id of the aggregate the event is about — the account, transaction, or credential the fact concerns. `ce-tenantid` carries the owning tenant in multi-tenant deployments; it is omitted for single-tenant business events, so a consumer treats an absent tenant id as valid single-tenant scope rather than an error.

## Schema versioning

Each event declares its own payload schema version in `ce-schemaversion`, independent of other events on the same source. The default is `1.0.0`. A minor bump is additive and backward compatible; a major bump is a breaking change. The version lives in the header, never in the topic name, so a consumer that reads payloads as a [tolerant reader](/en/reference/tolerant-reader) — ignoring unknown fields — is unaffected by an additive change.

## Delivery guarantees

Delivery is **at-least-once**. A consumer commits its position only after it finishes handling a record, so a crash mid-handling replays the record rather than dropping it — which means the same event can arrive more than once. Deduplicate on `ce-id` and keep handlers idempotent.

On the producer side, durability comes from a **transactional outbox**. A product writes the event to its outbox in the same database transaction as the state change that produced it, so the event and the fact it reports commit or roll back together. A relay then publishes committed outbox rows to Kafka and retries through broker outages, so no committed business fact is lost before it reaches the stream.
