Made Open

Event Catalog

All events share the envelope from event-driven-spine.md. This catalog defines the eventType values and their data payloads.

Audit status (2026-04-12, followup): This file is split into three sections. Published Events entries are verified against a publish() call in apps/hub/src/**/*.ts; payload shapes are taken from the actual publish call. Subscribed-Only Events are subjects the hub listens on but does not publish itself (typically emitted by plugins via PluginContext). Planned Events are documented schemas with no publisher or subscriber yet — they describe intended future behavior and are not wired. Only a subset of the hub's published events are cataloged here; domains such as coordination.*, governance.*, marketplace.*, presence.*, dispute.*, wiki.*, and per-entity domain events (health.*, fitness.*, etc.) are published by the hub but documented in their own service references.


Published Events

connector.*

connector.DataReceived

Emitted by the Connector Service after a plugin returns raw items ready for ETL. Publisher: apps/hub/src/services/connectors/ConnectorService.ts

{
  connectorId: string,
  entityType: string,
  rawItems: unknown[],
  syncId: string
}

connector.SyncCompleted

Emitted after a full sync cycle finishes. Publisher: apps/hub/src/services/connectors/ConnectorService.ts

{
  connectorId: string,
  itemsProcessed: number,
  syncId: string,
  nextSyncAt: string
}

connector.BridgeProcessed

Emitted by the Connector Bridge after routing a plugin result into observations. Publisher: apps/hub/src/services/connector/ConnectorBridgeService.ts

{
  connectorId: string,
  observationCount: number,
  correlationId: string
}

communication.*

communication.MessageSent

Outbound message accepted by the provider. Publisher: apps/hub/src/services/channels/ChannelService.ts

{
  messageId: string,
  conversationId: string,
  channelId: string,
  recipientPhone: string,
  providerMessageId: string,
  timestamp: string
}

communication.CallStarted

Inbound or outbound call began. Publishers: apps/hub/src/services/calling/CallingService.ts, apps/hub/src/services/channels/ChannelService.ts

{
  callId: string,
  conversationId: string,
  channelId: string,
  direction: 'inbound' | 'outbound',
  callerPhone: string,
  calleePhone: string,
  timestamp: string,
  providerCallId: string
}

communication.VoicemailReceived

New voicemail recorded. Publisher: apps/hub/src/services/calling/VoicemailService.ts

{
  voicemailId: string,
  ownerId: string,
  fromNumber: string,
  contactId: string | null,
  durationSeconds: number,
  phoneLineId: string | null
}

communication.NoteReceived

ActivityPub Note received from a federated peer. Publisher: apps/hub/src/services/federation/ActivityPubService.ts

{
  actorId: string,
  noteId: string,
  content: string,
  receivedAt: string
}

data.*

data.EntityCreated

New entity persisted. Publishers: apps/hub/src/services/calling/PhoneLineService.ts, apps/hub/src/services/contacts/ContactTimelineService.ts, apps/hub/src/services/entity-registry/EntityRegistryService.ts

{
  entityType: string,
  entityId: string,
  sourceConnectorId: string | null
}

data.EntityEmbedded

Embedding pipeline produced a vector for an entity. Publisher: apps/hub/src/services/embedding/EmbeddingPipelineService.ts

{
  entityType: string,
  entityId: string,
  model: string,
  dimensions: number
}

data.EntityMerged

Two records merged by the lineage service. Publisher: apps/hub/src/services/lineage/EntityMergeService.ts

{
  primaryId: string,
  mergedId: string,
  entityType: string
}

data.RuleMatched

A rule evaluated to true during Rules Service processing. Publisher: apps/hub/src/services/rules/RulesService.ts

{
  ruleId: string,
  triggerEventId: string,
  matchedAt: string
}

inbox.*

inbox.ItemCreated

New item landed in the unified inbox. Publisher: apps/hub/src/services/inbox/InboxService.ts

{
  inboxItemId: string,
  ownerId: string,
  type: 'call' | 'sms' | 'email' | 'voicemail' | 'video' | 'calendar_event',
  channelType: string,
  direction: 'inbound' | 'outbound',
  contactId: string | null
}

inbox.ItemUpdated

Inbox item read/starred/archived state changed. Publisher: apps/hub/src/services/inbox/InboxService.ts

{
  inboxItemId: string,
  ownerId: string,
  changedFields: string[]
}

contact.*

contact.Resolved

Identifier matched to an existing contact. Publisher: apps/hub/src/services/inbox/InboxService.ts

{
  identifier: string,
  identifierType: 'phone' | 'email',
  contactId: string,
  ownerId: string
}

contact.SuggestionCreated

Unrecognized identifier created a contact suggestion. Publisher: apps/hub/src/services/inbox/InboxService.ts

{
  identifier: string,
  identifierType: 'phone' | 'email',
  ownerId: string,
  sourceEventType: string
}

contact.Merged

Two contact records merged. Publisher: apps/hub/src/services/contacts/ContactTimelineService.ts

{
  primaryId: string,
  mergedId: string,
  ownerId: string
}

email.*

email.Sent

Outbound email accepted by Microsoft Graph. Publisher: apps/hub/src/services/email/EmailService.ts

{
  messageId: string,
  threadId: string,
  to: Array<{ name: string, email: string }>,
  subject: string,
  sentAt: string
}

email.DraftSaved

Draft created or updated. Publisher: apps/hub/src/services/email/EmailService.ts

{
  messageId: string,
  subject: string,
  to: Array<{ name: string, email: string }>,
  savedAt: string
}

email.SendRequested

Rule action requested that an outbound email be composed and sent. Publisher: apps/hub/src/services/rules/RulesService.ts

{
  ruleId: string,
  to: string[],
  subject: string,
  body: string
}

video.*

video.MeetingCreated

New video meeting room provisioned. Publisher: apps/hub/src/services/meeting/MeetingService.ts

{
  meetingId: string,
  ownerId: string,
  title: string,
  roomName: string,
  scheduledStart: string | null,
  scheduledEnd: string | null
}

video.MeetingStarted

First participant joined a meeting. Publisher: apps/hub/src/services/meeting/MeetingService.ts

{
  meetingId: string,
  ownerId: string,
  roomName: string,
  startedAt: string
}

video.MeetingEnded

Last participant left. Publisher: apps/hub/src/services/meeting/MeetingService.ts

{
  meetingId: string,
  ownerId: string,
  roomName: string,
  endedAt: string,
  durationSeconds: number,
  participantCount: number
}

audio.*

audio.SessionStarted

Windows audio sidecar began a recording session. Publisher: apps/hub/src/api/routes/audio.ts

{
  sessionId: string,
  ownerId: string,
  source: 'mic' | 'system' | 'both',
  deviceId: string
}

audio.SessionEnded

Recording session completed. Publisher: apps/hub/src/api/routes/audio.ts

{
  sessionId: string,
  ownerId: string,
  durationSeconds: number,
  segmentCount: number
}

audio.TranscriptChunk

A transcript chunk was persisted. Publisher: apps/hub/src/api/routes/audio.ts

{
  sessionId: string,
  ownerId: string,
  transcriptId: string,
  pass: number,
  segmentCount: number,
  fullText: string
}

audio.CommandDetected

Wake-word command parsed from the transcript. Publisher: apps/hub/src/api/routes/audio.ts

{
  commandId: string,
  sessionId: string,
  ownerId: string,
  rawTranscript: string,
  commandText: string,
  intent: string,
  confidence: number
}

audio.CommandExecuted

Voice Command Service processed a command. Publisher: apps/hub/src/services/voice-command/VoiceCommandService.ts

{
  commandId: string,
  sessionId: string,
  ownerId: string,
  intent: string,
  jobId: string,
  status: 'completed' | 'failed'
}

audio.IntelligenceExtracted

LLM extraction pass complete. Publisher: apps/hub/src/services/audio-etl/AudioETLService.ts

{
  sessionId: string,
  ownerId: string,
  transcriptId: string,
  actionItemCount: number,
  decisionCount: number,
  commitmentCount: number,
  entityMentionCount: number
}

audio.PassiveIntentDetected

Regex/LLM detected a passive user intent in an audio transcript. Publisher: apps/hub/src/services/audio-etl/AudioETLService.ts

{
  sessionId: string,
  ownerId: string,
  text: string,
  intentType: string,
  confidence: number,
  context?: unknown
}

ai.*

ai.CallAnalyzed

AI analysis of a call transcript completed. Publisher: apps/hub/src/services/calling/TranscriptionService.ts

{
  analysisId: string,
  callSessionId: string,
  ownerId: string,
  sentiment: 'positive' | 'neutral' | 'negative' | null,
  actionItemCount: number,
  keyTopics: string[]
}

ai.EmailSuggested

Email AI detected priority / follow-up opportunities. Publisher: apps/hub/src/services/email/EmailAIService.ts

{
  threadId: string,
  priority: {
    threadId: string,
    score: number,
    factors: string[],
    generatedAt: string
  },
  followUps: Array<{
    threadId: string,
    messageId: string,
    detectedPhrase: string,
    suggestedDueDate: string,
    description: string
  }>
}

ai.QueryReceived

Published by AgentService.query before any work happens. One event per inbound agent query. Useful for tracking inbound query rate, length distribution, abandonment (queries received without a matching ai.ResponseGenerated), and per-user activity. Publisher: apps/hub/src/services/agent/AgentService.ts

{
  queryId: string,                 // correlates with the ResponseGenerated event
  userId: string,
  conversationId: string,
  queryLength: number,             // character length of the raw query
  hasExistingConversation: boolean // true when the caller passed a conversationId
}

ai.ResponseGenerated

Published by AgentService.query after the LLM returns, the audit entry is written, and the conversation row is inserted. Correlates with ai.QueryReceived via queryId. Useful for dashboards that track provider mix, latency percentiles, response-length distribution, and routing-reason breakdown without reading the audit log. Publisher: apps/hub/src/services/agent/AgentService.ts

{
  queryId: string,                 // same as ai.QueryReceived.queryId
  userId: string,
  conversationId: string,
  provider: string,                // e.g. 'openrouter' | 'ollama'
  model: string,                   // e.g. 'anthropic/claude-3.5-sonnet' | 'llama3.2'
  routingReason: string,           // LlmRouter decision rationale
  contextSnippetCount: number,     // how many Meilisearch hits were included in the prompt
  responseLength: number,          // character length of the LLM's response
  latencyMs: number                // wall time from QueryReceived to now
}

system.*

system.WorkflowCompleted

Workflow run reached a terminal state. Publisher: apps/hub/src/services/workflow/WorkflowService.ts

{
  workflowId: string,
  runId: string,
  status: 'completed' | 'failed' | 'cancelled'
}

system.ScheduledTrigger

Scheduler fired a schedule. Dual-published alongside scheduler.ScheduleFired so Rules and Workflow services can subscribe to a stable subject. Publisher: apps/hub/src/services/scheduler/SchedulerService.ts

{
  scheduleId: string,
  scheduleName: string,
  ownerId: string,
  sourceType: string,
  sourceId: string,
  payload: unknown,
  runId: string,
  firedAt: string
}

system.IntentionTriggered

An active intention reached its trigger condition. Publisher: apps/hub/src/services/intentions/IntentionsService.ts

{
  intentionId: string,
  ownerId: string,
  title: string,
  triggeredAt: string
}

system.plugin

Plugin lifecycle event (install/uninstall/update). The envelope's inner eventType distinguishes the specific action (e.g. plugin.Installed). Publisher: apps/hub/src/services/plugins/PluginRegistryService.ts

{
  pluginId: string,
  ownerId: string
}

system.HealthCheckFailed

Published when CircuitBreakerService transitions a named circuit from CLOSED → OPEN (failure threshold exceeded) or HALF_OPEN → OPEN (probe failed). One event per state transition — not one per failure. willRetry is always true because the breaker will attempt a HALF_OPEN probe after retryAfterMs. Publisher: apps/hub/src/services/circuit-breaker/CircuitBreakerService.ts

{
  target: string,                                          // circuit name, e.g. 'nats', 'meilisearch', 'openrouter'
  reason: string,                                          // message of the error that tripped the breaker
  trigger: 'failure_threshold_exceeded' | 'half_open_probe_failed',
  failureCount: number,                                    // total failures recorded on this circuit
  willRetry: boolean,                                      // always true — breaker auto-probes after retryAfterMs
  retryAfterMs: number                                     // config.timeoutMs for this circuit
}

system.ServiceStarted

Published once at the tail of hub boot, after all services have started and the HTTP server is listening. One event per process — not per service — so subscribers (dashboards, alerting, audit log) have a durable record of every hub start without being drowned at startup. Publisher: apps/hub/src/main.ts

{
  serviceName: 'hub',
  version: string,         // from npm_package_version, defaults to 'dev'
  nodeVersion: string,     // process.version
  pid: number,
  port: number,            // HTTP API port
  startedAt: string        // ISO-8601
}

system.PluginLoaded

Published by PluginManager.loadPlugin after a hub-side plugin (connector, channel, or operator) is successfully loaded from disk and its init() has returned. Distinct from the user-scoped system.plugin lifecycle event emitted by PluginRegistryService when a user installs a plugin. Publisher: apps/hub/src/services/plugins/PluginManager.ts

{
  pluginId: string,                              // manifest id, e.g. 'com.microsoft.graph'
  pluginName: string,
  pluginVersion: string,
  pluginType: 'connector' | 'channel' | 'operator',
  pluginPath: string                             // relative path passed to loadPlugin
}

credential.*

credential.CredentialAdded

Published by POST /api/credentials (apps/hub/src/api/routes/credentials.ts) after a credential is upserted into user_credentials and secrets are stored in Supabase Vault. Consumed by audit and capability-activation subscribers.

{
  userId: string,
  credentialId: string,
  credentialType: string, // e.g. 'twilio' | 'microsoft365' | 'openrouter' | 'ollama'
  displayInfo: string     // human-readable, never a secret
}

credential.CredentialRemoved

Published by DELETE /api/credentials/:type after the vault secrets and metadata row are deleted. Consumed by audit and capability-deactivation subscribers.

{
  userId: string,
  credentialId: string,
  credentialType: string
}

rule.*

rule.ConditionMatched

Published once per rule whose condition AST evaluates to true for an incoming event, immediately before actions fire. Useful for rule-engine observability and for subscribers that need to react to rule matches without running actions. Publisher: apps/hub/src/services/rules/RulesService.ts

{
  ruleId: string,
  ruleName: string,
  ownerId: string,
  triggerEventType: string,       // e.g. 'communication.MessageReceived'
  triggerCorrelationId: string,   // correlationId of the envelope that matched
  actionCount: number             // how many actions the rule declared
}

rule.ActionTriggered

Published once per action inside a matched rule, after the action handler returns. Emits with outcome: 'executed' on success or outcome: 'failed' on any thrown error. An error field is included on failure. Publisher: apps/hub/src/services/rules/RulesService.ts

{
  ruleId: string,
  ruleName: string,
  ownerId: string,
  actionType: string,             // e.g. 'send.Sms' | 'tag.Person' | 'route.Call'
  triggerEventType: string,
  outcome: 'executed' | 'failed',
  error?: string                  // present only on failure
}

Subscribed-Only Events

Subjects the hub listens on but does not publish itself. These are expected to be emitted by plugins (via PluginContext.publish) or external webhooks.

connector.LocationUpdated

Device location fix. ContextEngine and StreamProcessingService subscribe. Expected emitter: device connector plugin.

{
  deviceId: string,
  lat: number,
  lon: number,
  accuracyMeters: number,
  altitudeMeters?: number,
  wifiSsid?: string,
  recordedAt: string
}

connector.DeviceEventReceived

Generic device event (call log, SMS, active app, clipboard, notification). StreamProcessingService subscribes. Expected emitter: device connector plugin.

{
  deviceId: string,
  eventKind: 'call_log_entry' | 'sms' | 'active_app' | 'clipboard' | 'notification',
  payload: unknown
}

communication.MessageReceived

Normalized inbound message. ActivityFeedService and others subscribe. Expected emitter: Twilio / WhatsApp / MS Graph channel plugins.

{
  messageId: string,
  conversationId: string,
  channelId: string,
  channelType: string,
  senderPhone: string,
  senderPersonId: string | null,
  body: string,
  timestamp: string,
  providerMessageId: string
}

communication.CallEnded

Call finished. TranscriptionService, InboxService, ContextEngine, StreamProcessingService subscribe. Expected emitter: Twilio channel plugin via status webhook.

{
  callId: string,
  conversationId: string,
  duration_seconds: number,
  outcome: 'completed' | 'no-answer' | 'busy' | 'failed' | 'voicemail',
  recordingUrl: string | null,
  transcriptJobId: string | null,
  timestamp: string
}

data.EntityUpdated

Existing entity updated. WikiService subscribes. Expected emitter: ETL service or entity-specific domain services (currently publish domain-specific events instead).

{
  entityType: string,
  entityId: string,
  changedFields: string[],
  sourceConnectorId: string | null
}

email.Received

New email arrival. EmailAIService and PushNotificationService subscribe. Expected emitter: MS Graph connector plugin.

{
  messageId: string,
  threadId: string,
  folderId: string,
  from: { name: string, email: string },
  subject: string,
  snippet: string,
  hasAttachments: boolean,
  receivedAt: string
}

Planned Events

These schemas are documented as roadmap intent. They are not yet emitted by the hub and have no active subscribers. Do not rely on them.

connector.AuthFailed

A connector could not authenticate.

{
  connectorId: string,
  reason: string,
  requiresUserAction: boolean
}

communication.VideoRoomCreated

Superseded in practice by video.MeetingCreated.

{
  roomId: string,
  providerRoomId: string,
  participants: string[],
  createdAt: string
}

communication.CallTransferred

{
  callSid: string,
  ownerId: string,
  transferType: 'warm' | 'cold',
  targetNumber: string,
  fromNumber: string
}

communication.CallParked

{
  callSid: string,
  ownerId: string,
  slot: number,
  fromNumber: string
}

communication.CallRetrieved

{
  callSid: string,
  ownerId: string,
  slot: number,
  retrievedBy: string
}

data.EntityDeleted

{
  entityType: string,
  entityId: string
}

job.Enqueued

{
  jobId: string,
  jobType: string,
  queue: 'realtime' | 'interactive' | 'background',
  triggeredByEventId: string
}

job.Completed

{
  jobId: string,
  jobType: string,
  durationMs: number
}

job.Failed

{
  jobId: string,
  jobType: string,
  attempts: number,
  lastError: string
}

ai.MeetingAnalyzed

{
  analysisId: string,
  meetingId: string,
  ownerId: string,
  sentiment: 'positive' | 'neutral' | 'negative' | null,
  actionItemCount: number,
  keyTopics: string[]
}

audio.DailyBriefGenerated

{
  briefId: string,
  ownerId: string,
  date: string,
  sessionCount: number
}

video.RecordingReady

{
  recordingId: string,
  meetingId: string,
  ownerId: string,
  url: string,
  durationSeconds: number
}