Platform referenceReference
Event schema reference
Full envelope, registration API, and naming rules for AssetEvents.
AssetEventEnvelope
interface AssetEventEnvelope<TPayload = unknown> {
id: string; // evt_<ULID>
tenantId: string;
spaceId: string;
eventType: string; // PascalCase past-tense
eventSchemaVersion: number; // integer per event type
subjectType: string; // PascalCase, must be registered
subjectId: string;
actorId: string;
actorType: string; // see ActorType enum
actionInvocationId?: string; // empty for platform-emitted events not tied to an action
payload: TPayload;
sequence: number; // monotonic per (subjectType, subjectId)
occurredAt: Date;
recordedAt: Date;
correlationId: string;
causationId?: string;
}Field semantics
| Field | Required | Notes |
|---|---|---|
id | yes | Sortable Crockford-ULID with evt_ prefix. |
tenantId, spaceId | yes | Replay scope dimensions. Filtered first. |
eventType | yes | Must be in the event type registry. |
eventSchemaVersion | yes | Bump when payload shape changes. Old events still replay against old shape. |
subjectType | yes | Must be in the subject type registry. |
subjectId | yes | The entity this event is about. |
actorId, actorType | yes | Who/what caused the event. |
actionInvocationId | no | Present for events caused by a handler. Absent for some platform events. |
sequence | yes | Per-subject. Use to detect gaps and order replay. |
occurredAt | yes | Domain time. May differ from recordedAt for backfills. |
recordedAt | yes | Platform-stamped time of append. |
correlationId | yes | Stable across all events of a related invocation chain. |
causationId | no | Direct parent invocation. Forms a tree for sagas. |
Registration API
From packages/platform/events/index.ts:
export function registerSubjectType(type: string): void;
export function isValidSubjectType(type: string): boolean;
export function getRegisteredSubjectTypes(): string[];
export function registerEventType(type: string): void;
export function isValidEventType(type: string): boolean;
export function getRegisteredEventTypes(): string[];
export interface EventTypeRegistry {
register(eventType: string): void;
has(eventType: string): boolean;
list(): string[];
}
export function createEventTypeRegistry(initial?: string[]): EventTypeRegistry;Verticals do not call these directly — they declare event types in their FabricModule.eventTypes, and registerFabricModule calls the registration functions for them.
Pre-registered subject types
| Subject type |
|---|
ActionInvocation |
PolicyEvaluation |
AdapterInvocation |
Pre-registered event types
| Event type | Description |
|---|---|
ComplianceBlocked | A policy returned block. |
StateTransitioned | A state-machine transition was applied during a handler. |
AdapterInvocationStarted | Adapter step beginning. |
AdapterInvocationSucceeded | Adapter step success. |
AdapterInvocationFailed | Adapter step terminal failure. |
WebhookReceived | Webhook captured by integration adapter. |
Naming rules
Enforced by convention; tooling rejects in code review:
- Event types — PascalCase, past tense fact (
OfferAccepted). Never imperative (AcceptOffer). - Subject types — PascalCase noun (
Offer,Vehicle). - Action IDs —
<namespace>.<snake_verb>(lending.accept_offer). - Policy IDs —
<id>.v<integer>(lending.credit_pull_consent.v1).
Schema versioning rules
When a payload shape changes:
- Bump
eventSchemaVersionfor the affected event type. - Add a new schema for the new version. Keep the old schema accessible.
- Projection reducers that consume both versions branch on
event.eventSchemaVersion. - Never edit past events. The platform replays them as-is.
Common safe changes that don't require a bump:
- Adding optional fields with sensible default behavior in reducers.
Changes that do require a bump:
- Removing a field.
- Renaming a field.
- Changing a field type.
- Adding a required field.
ID format
^[a-z][a-z0-9]{1,7}_[0-9A-Z]{26}$The 26-character suffix is a Crockford-base32 ULID (10 chars time + 16 chars random). The evt_ prefix is fixed for events.
See also
- Events as evidence — the design rationale.
- Event emission stage — how the runtime appends.
- Projection mechanics — how the log is consumed.