Event Catalog
The Loop comms platform uses a typed, versioned event system for transactional messaging. Events are the bridge between your application logic and the communication pipeline — when something happens in your product, fire an event and let the platform handle delivery.
@loop/contracts
All event schemas are defined in the @loop/contracts package. Install it to get TypeScript types, Zod validators, and the validateEvent() helper:
pnpm add @loop/contractsNaming Convention
Events follow the <domain>.<action>.v<N> convention:
| Part | Description | Examples |
|---|---|---|
domain | Business domain | lead, message, journey, order |
action | What happened | captured, sent, completed |
v<N> | Schema version | v1, v2 |
When a schema changes in a breaking way, bump the version number. The platform routes events by full name, so lead.captured.v1 and lead.captured.v2 are independent event types.
Validation
Use validateEvent() to validate payloads at runtime before firing:
import { validateEvent } from '@loop/contracts';
const result = validateEvent('lead.captured.v1', payload);
if (result.success) {
await fireEvent(result.data);
} else {
console.error('Invalid event:', result.error.issues);
}The function returns a Zod SafeParseResult — success: true with parsed data, or success: false with detailed error information.
Common Fields
All events share these base fields:
| Field | Type | Required | Description |
|---|---|---|---|
eventId | uuid | Yes | Unique event identifier (used for deduplication) |
occurredAt | datetime | Yes | ISO 8601 timestamp of when the event occurred |
brandId | uuid | Yes | Brand that owns this event |
Events
lead.captured.v1
Fired when a new lead is captured from any source — VSL forms, quiz completions, inbound leads, or other channels.
message.sent.v1
Fired when a message is sent through any channel — email, SMS, push, or in-app.
journey.step.completed.v1
Fired when a person completes (or skips/fails) a step in a journey sequence.