Event envelope
Kafka and webhook destinations deliver a customer export body (specVersion 1.0) built by causet-realtime. That shape is stable for consumers. It is derived from the internal CommittedEventEnvelope on causet.committed-events.v1 (or a transitional PatchEvent conversion) — you do not receive the internal wire format on your topic/URL.
Dedupe on body field id (Kafka header causet-event-id, webhook header X-Causet-Event-Id). Delivery is at-least-once.
Export body (export_v1 / specVersion 1.0)
This is the default JSON value for Kafka records and the default webhook POST body (payloadFormat omitted or export_v1).
{
"specVersion": "1.0",
"id": "evt_1",
"type": "jamlet.feed-item-published",
"source": "causet://deployment/dep_1",
"subject": "feed/feed_1",
"time": "2026-07-30T15:35:12Z",
"dataContentType": "application/json",
"dataSchema": "causet://schemas/FeedItemPublished/1",
"tenantId": "tenant_1",
"projectId": "project_1",
"environmentId": "main",
"deploymentId": "dep_1",
"streamId": "feed",
"streamSequence": 42,
"globalSequence": 99,
"correlationId": "corr",
"causationId": "cause",
"data": { "title": "x" },
"causet": {
"destinationId": "dest_1",
"subscriptionId": "sub_1",
"deliveryId": "del_1",
"replayId": null,
"replay": false,
"attempt": 1
}
}| Field | Type | Notes |
|---|---|---|
specVersion | string | Always "1.0" today |
id | string | Stable Causet event id — dedupe key |
type | string | Business event type (subscription patterns match this) |
source | string | causet://deployment/{deploymentId}, or causet://deployment/unknown |
subject | string | Typically {streamId}/{entityId} |
time | string (RFC3339) | occurredAt, else committedAt, else send time (UTC) |
dataContentType | string | Always application/json |
dataSchema | string | causet://schemas/{schema.name}/{schema.version}, or causet://schemas/{type}/1 if schema name empty |
tenantId | string | Platform / tenant |
projectId | string | Application |
environmentId | string | Fork (e.g. main) — omitted when empty |
deploymentId | string | Omitted when empty |
streamId | string | Entity stream |
streamSequence | number | Per-stream sequence (maps from internal cursor) |
globalSequence | number | Omitted when 0 / unavailable |
correlationId / causationId | string | Omitted when empty |
data | object | Event payload ({} if none) |
causet.destinationId | string | Destination that delivered this record |
causet.subscriptionId | string | Matching subscription |
causet.deliveryId | string | This delivery job id |
causet.replayId | string | null | Set on replay deliveries |
causet.replay | boolean | true when replayId is non-empty |
causet.attempt | number | 1-based delivery attempt |
There is no top-level eventType, payload, entityId, intentId, platformId, applicationId, or forkId in export_v1. Entity identity is in subject (and usually streamId). Tenancy uses tenantId / projectId / environmentId.
Kafka headers
Set on every Kafka destination record:
| Header | When |
|---|---|
content-type | always → application/json |
causet-spec-version | always → 1.0 |
causet-event-id | always |
causet-event-type | always |
causet-delivery-id | always |
causet-destination-id | always |
causet-subscription-id | always |
causet-replay | always → true / false |
causet-global-sequence | when globalSequence > 0 |
causet-stream-sequence | when streamSequence > 0 |
causet-correlation-id | when present |
traceparent | when a trace id is present (00-{traceId}-0000000000000000-01) |
There are no causet-entity-id, causet-fork-id, or causet-intent-id headers on the Kafka export path.
Record key
Partition key strategy defaults to stream_id (fallback event_id). Other strategies: event_id, subject, correlation_id, constant. Ordering is only reliable for the same key within one partition — not a global total order.
Webhook headers
| Header | When |
|---|---|
Content-Type | always → application/json |
X-Causet-Event-Id | always |
X-Causet-Event-Type | always |
X-Causet-Delivery-Id | always |
X-Causet-Destination-Id | always |
X-Causet-Subscription-Id | always |
X-Causet-Delivery-Attempt | always (attempt number) |
X-Causet-Trace-Id | when present |
X-Causet-Signature | when HMAC signing is enabled |
X-Verdant-Signature | same value as X-Causet-Signature (compat) |
Plus any static headers configured on the destination.
Webhook flat_v1 (optional)
If the webhook destination sets payloadFormat: flat_v1, the body is a flatter map (not export_v1):
| Field | Notes |
|---|---|
eventId / eventType | Same ids as id / type above |
platformId / applicationId / forkId | Tenant aliases |
streamId / entityId / intentId | Stream coordinates |
cursor | Stream sequence |
timestamp | Unix millis |
payload | Event payload object |
deliveryId | Delivery job id |
Prefer export_v1 for new integrations so Kafka and webhooks share one schema.
Internal vs export (for operators)
| Layer | Shape | Audience |
|---|---|---|
| Internal bus | CommittedEventEnvelope (eventId, eventType, payload, …) | causet-realtime only |
| Customer export | export_v1 above (id, type, data, causet, …) | Your Kafka / webhook consumers |