Events API
Events in Causet are emitted by rules, not dispatched through a REST API. An emit op inside an action’s core or side_effects block appends a ledger event when the rule fires. There is no endpoint for directly inserting events into the ledger.
Event Structure
Every ledger event has the following envelope:
{
"event_type": "ARTIST_FOLLOWED",
"entity_id": "user-1",
"entity_type": "user",
"fork_id": "main",
"platform_id": "my-platform",
"application_id": "concert-app",
"payload": {
"user_id": "user-1",
"artist_id": "artist-pearl-jam"
},
"ts": 1719360000000,
"cursor": 7,
"event_id": "evt_01j2x..."
}| Field | Type | Description |
|---|---|---|
event_type | string | DSL event name (e.g. ARTIST_FOLLOWED) |
entity_id | string | The entity this event belongs to |
entity_type | string | Entity type from the DSL state: block |
fork_id | string | Fork the event was written to |
platform_id | string | Platform identifier |
application_id | string | Application identifier |
payload | object | Event payload as defined in the DSL |
ts | int64 | Event timestamp in milliseconds since epoch |
cursor | int64 | Monotonically increasing cursor for this entity |
event_id | string | Unique event ID |
Warning: Do not include
type,ts, orentity_idas payload field names — these are reserved and will cause a compiler error (VERR_FIELD_NOT_DECLARED).
Accessing Events via gRPC
The EntityBrowserGrpcService on port 9090 provides access to the ledger event history for a specific entity.
grpcurl -plaintext \
-d '{
"platformId": "my-platform",
"applicationId": "concert-app",
"forkId": "main",
"entityType": "user",
"entityId": "user-1"
}' \
localhost:9090 \
causet.EntityBrowserService/GetEntityEventsThis returns all ledger events for the entity in order, including full payload.
Kafka Streams
External systems integrate with Causet by consuming Kafka topics directly. This is the primary integration pattern — not REST webhooks.
causet.ledger-events.v1
The authoritative stream of all committed ledger events across all tenants and forks.
- Format: JSON (event envelope as described above)
- Partitioning: by
entity_id - Ordering: per-partition (per-entity)
- Retention: configurable (default 7 days; increase for long replay windows)
External consumers should subscribe to this topic to build their own projections, trigger webhooks, or feed data pipelines.
kafka-console-consumer.sh \
--bootstrap-server localhost:9092 \
--topic causet.ledger-events.v1 \
--from-beginning \
--property print.key=truecauset.projection-events.v1
Internal topic consumed by the projection worker. Contains routing metadata telling the worker which projections to update for each event.
- External consumers can subscribe to this topic but the
causet.ledger-events.v1topic is the more stable integration surface.
causet.patches.v1
Row-level patch events emitted after the projection worker successfully writes a row. Useful for downstream systems that need to react to projection changes (e.g., invalidating application-layer caches, triggering notifications).
{
"projection_name": "user_following",
"table": "user_following",
"op": "upsert",
"primary_key": {
"user_id": "user-1",
"artist_id": "artist-pearl-jam"
},
"row": {
"user_id": "user-1",
"artist_id": "artist-pearl-jam",
"followed_at": 1719360000000
},
"ts": 1719360000150
}causet.projection-dlq.v1
Dead letter queue for events that could not be processed by the projection worker after all retries. Monitor this topic for failures.
External System Integration
Pattern 1 — Kafka consumer: Subscribe to causet.ledger-events.v1. Process events in your consumer. This is the most reliable integration pattern.
Pattern 2 — Webhook fan-out (Available now): A webhook delivery service sits in front of causet.ledger-events.v1 and delivers HTTP callbacks to your app. See Use Webhooks to Update Existing Flows.
Pattern 3 — Patch stream: Subscribe to causet.patches.v1 to react to projection changes without polling the query API.
Event Ordering Guarantees
- Events for a single entity are totally ordered by
cursorwithin a fork. - Kafka partitioning by
entity_idpreserves per-entity ordering within a partition. - Cross-entity ordering is not guaranteed — do not rely on global event ordering across different entities.
Emitting Events from DSL
Events are never emitted directly via API. They are always produced by emit or emit_each ops inside action rules:
actions:
FOLLOW_ARTIST:
...
side_effects:
rules:
- name: emit_followed
then:
- op: emit
event_type: ARTIST_FOLLOWED
payload:
user_id: intent.user_id
artist_id: intent.artist_idThe emit_each op emits one event per item in a list:
- op: emit_each
event_type: ITEM_ADDED
for_each: intent.items
payload:
item_id: item.id
name: item.name