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 withce-, 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
Thece-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.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), 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 ince-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 — 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 once-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.
