Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grantmaster.dev/llms.txt

Use this file to discover all available pages before exploring further.

Event Catalog

StatusUpdatedCovered Files
🟢 Stable2026-02-21packages/shared/src/events/*, core/eventBus.ts

Overview

GrantMaster uses a centralized event system (SystemEventType enum) for cross-feature coordination, notification triggering, and audit trail persistence. This catalog lists every event type, its payload schema, emitting service, persistence behavior, and typical subscribers. Source file: packages/shared/src/events/index.ts For the canonical cross-feature contract used by status projections (Grants/Projects/Compliance/Billing/Agents/Extensions), see docs/engineering/architecture/canonical-event-contract.md.

EventBus Topology

The EventBus follows a fan-out pattern: many domain services emit events into a single centralized bus, which dispatches them to a small set of cross-cutting consumers. Each consumer independently decides which event types it cares about.

Base Event Envelope

Every event shares the SystemEvent<T> envelope:
interface SystemEvent<T> {
  type: SystemEventType;
  organizationId: string;
  userId: string;
  severity: EventSeverity;   // INFO | WARNING | ERROR | CRITICAL
  timestamp: Date;
  payload: T;
  metadata?: {
    source?: string;
    version?: string;
    correlationId?: string;
  };
}

Severity Levels

LevelUsage
INFONormal operations (approvals, creations, completions)
WARNINGApproaching limits, non-critical issues
ERRORFailures, rejections, errors
CRITICALBudget depletion, compliance violations, credit exhaustion

Persistence Rules

Events are persisted to Firestore when requiresPersistence(eventType) returns true. The following categories are always persisted:
  • Audit events (AUDIT_LOG_CREATED)
  • Compliance events (all 3)
  • Approval/rejection events (journals, expenses, time off)
  • Budget events (threshold exceeded, depleted)
  • User lifecycle events (onboarded, offboarded)
  • Project lifecycle events (all 5)
  • Template events (created, used)
  • Quota events (exceeded, warning)
  • Intelligence events (all 3)
  • Portal events (all 4)
  • Agent events (all 8)
  • Credit events (all 3)
  • Impact/M&E events (all 25)
  • Module marketplace events (all 7 + 2 extension health)
Events NOT in these categories are ephemeral (notification-only, not saved to Firestore).

Type Guards

Helper functions for grouping events by domain:
FunctionReturns true for
isJournalEvent()JOURNAL_SUBMITTED, JOURNAL_APPROVED, JOURNAL_REJECTED
isExpenseEvent()EXPENSE_SUBMITTED, EXPENSE_APPROVED, EXPENSE_REJECTED
isComplianceEvent()COMPLIANCE_ALERT_RAISED, COMPLIANCE_ALERT_RESOLVED, COMPLIANCE_RULE_VIOLATED
isBudgetEvent()BUDGET_THRESHOLD_EXCEEDED, BUDGET_THRESHOLD_WARNING, BUDGET_DEPLETED
isProjectLifecycleEvent()PROJECT_CREATED, PROJECT_PHASE_ADVANCED, PROJECT_ARCHIVED, PROJECT_UNARCHIVED, PROJECT_CHECKLIST_COMPLETED
isProjectTemplateEvent()PROJECT_TEMPLATE_CREATED, PROJECT_TEMPLATE_USED
isTimeOffRequestEvent()TIME_OFF_REQUEST_SUBMITTED, TIME_OFF_REQUEST_APPROVED, TIME_OFF_REQUEST_REJECTED
isQuotaEvent()QUOTA_EXCEEDED, QUOTA_WARNING
isIntelligenceEvent()GRANT_APPLICATION_OUTCOME, INTELLIGENCE_CONSENT_CHANGED, INTELLIGENCE_SNAPSHOT_GENERATED
isGrantEvent()All 9 grant pipeline events
isPortalEvent()PORTAL_REPORT_ACKNOWLEDGED, PORTAL_REVISION_REQUESTED, PORTAL_COMMENT_POSTED, PORTAL_REPORT_SUBMITTED
isAgentEvent()All 8 agent lifecycle events
isCreditEvent()CREDITS_CONSUMED, CREDITS_EXHAUSTED, CREDITS_PURCHASED
isMEEvent()All 25 M&E events
isModuleEvent()All 7 module marketplace + 2 extension health events

Event Reference by Domain

Journal Events

EventSeverityPersistedPayload Interface
JOURNAL_SUBMITTEDINFONoJournalSubmittedPayload
JOURNAL_APPROVEDINFOYesJournalApprovedPayload
JOURNAL_REJECTEDWARNINGYesJournalRejectedPayload
JournalSubmittedPayload: journalId, userId, userName, projectId, projectName, hours, date, managerId? JournalApprovedPayload: journalId, userId, userName, projectId, projectName, hours, approvedBy, approvedByName, approvedAt JournalRejectedPayload: Same as approved + rejectedBy, rejectedByName, rejectedAt, reason?

Expense Events

EventSeverityPersistedPayload Interface
EXPENSE_SUBMITTEDINFONoExpenseSubmittedPayload
EXPENSE_APPROVEDINFOYesExpenseApprovedPayload
EXPENSE_REJECTEDWARNINGYesExpenseRejectedPayload
ExpenseSubmittedPayload: expenseId, userId, userName, projectId, projectName, amount, category, description, submittedAt ExpenseApprovedPayload: Same core fields + approvedBy, approvedByName, approvedAt ExpenseRejectedPayload: Same core fields + rejectedBy, rejectedByName, rejectedAt, reason?

Compliance Events

EventSeverityPersistedPayload Interface
COMPLIANCE_ALERT_RAISEDWARNING/ERRORYesComplianceAlertPayload
COMPLIANCE_ALERT_RESOLVEDINFOYesComplianceAlertResolvedPayload
COMPLIANCE_RULE_VIOLATEDERRORYesComplianceAlertPayload
ComplianceAlertPayload: alertId, projectId, projectName, ruleId, ruleName, violationType, severity (low/medium/high/critical), description, detectedBy?, detectedAt ComplianceAlertResolvedPayload: alertId, projectId, projectName, ruleId, resolvedBy, resolvedByName, resolvedAt, resolution

Budget Events

EventSeverityPersistedPayload Interface
BUDGET_THRESHOLD_WARNINGWARNINGNoBudgetThresholdPayload
BUDGET_THRESHOLD_EXCEEDEDWARNINGYesBudgetThresholdPayload
BUDGET_DEPLETEDCRITICALYesBudgetDepletedPayload
BudgetThresholdPayload: projectId, projectName, budgetLineId?, budgetLineName?, threshold (e.g., 80), currentUsage (percentage), totalBudget, spentAmount, remainingAmount, managerId? BudgetDepletedPayload: projectId, projectName, budgetLineId?, budgetLineName?, totalBudget, overspentAmount, managerId?

Project Events

EventSeverityPersistedPayload Interface
PROJECT_DEADLINE_APPROACHINGWARNINGNoProjectDeadlinePayload
PROJECT_STATUS_CHANGEDINFONoProjectStatusChangedPayload
PROJECT_MILESTONE_COMPLETEDINFONoProjectMilestonePayload
ProjectDeadlinePayload: projectId, projectName, deadline, daysRemaining, phase, managerId?, teamMemberIds ProjectStatusChangedPayload: projectId, projectName, oldStatus, newStatus, changedBy, changedByName, changedAt ProjectMilestonePayload: projectId, projectName, milestoneId, milestoneName, completedBy, completedByName, completedAt

Project Lifecycle Events

EventSeverityPersistedPayload Interface
PROJECT_CREATEDINFOYesProjectCreatedPayload
PROJECT_PHASE_ADVANCEDINFOYesProjectPhaseAdvancedPayload
PROJECT_ARCHIVEDINFOYesProjectArchivedPayload
PROJECT_UNARCHIVEDINFOYesProjectUnarchivedPayload
PROJECT_CHECKLIST_COMPLETEDINFOYesProjectChecklistCompletedPayload
ProjectCreatedPayload: projectId, projectName, projectCode, phase, fundingSource, createdBy, createdByName, templateId?, templateName?, createdAt ProjectPhaseAdvancedPayload: projectId, projectName, fromPhase, toPhase, advancedBy, advancedByName, advancedAt, notes?, blockerCount, warningCount ProjectArchivedPayload: projectId, projectName, archivedBy, archivedByName, archivedAt, finalPhase, totalHoursLogged, totalBudget ProjectUnarchivedPayload: projectId, projectName, unarchivedBy, unarchivedByName, unarchivedAt, currentPhase ProjectChecklistCompletedPayload: projectId, projectName, phase, completedBy, completedByName, completedAt, itemsCompleted, requiredItemsCompleted

Project Template Events

EventSeverityPersistedPayload Interface
PROJECT_TEMPLATE_CREATEDINFOYesProjectTemplateCreatedPayload
PROJECT_TEMPLATE_USEDINFOYesProjectTemplateUsedPayload
ProjectTemplateCreatedPayload: templateId, templateName, createdBy, createdByName, sourceProjectId?, sourceProjectName?, createdAt, isPublic ProjectTemplateUsedPayload: templateId, templateName, projectId, projectName, usedBy, usedByName, usedAt, usageCount

User Events

EventSeverityPersistedPayload Interface
USER_ONBOARDEDINFOYesUserOnboardedPayload
USER_OFFBOARDEDWARNINGYesUserOffboardedPayload
UserOnboardedPayload: userId, userName, email, role, department?, managerId?, onboardedAt UserOffboardedPayload: userId, userName, email, offboardedBy, offboardedByName, offboardedAt, reason?

Time Off Events

EventSeverityPersistedPayload Interface
TIME_OFF_REQUEST_SUBMITTEDINFONoTimeOffRequestSubmittedPayload
TIME_OFF_REQUEST_APPROVEDINFOYesTimeOffRequestApprovedPayload
TIME_OFF_REQUEST_REJECTEDWARNINGYesTimeOffRequestRejectedPayload
TimeOffRequestSubmittedPayload: leaveRequestId, userId, userName, leaveType, startDate, endDate, daysRequested, managerId?, notes?, submittedAt TimeOffRequestApprovedPayload: Same core fields + approvedBy, approvedByName, approvedAt TimeOffRequestRejectedPayload: Same core fields + rejectedBy, rejectedByName, rejectedAt, reason?

Quota Events

EventSeverityPersistedPayload Interface
QUOTA_WARNINGWARNINGYesQuotaWarningPayload
QUOTA_EXCEEDEDERRORYesQuotaExceededPayload
QuotaWarningPayload: resourceType (users/projects/storage/ai_generations/agent_credits/agent_runs), limit, current, available, percentageUsed, subscriptionTier, threshold QuotaExceededPayload: Same resource types + limit, current, available, attempted, subscriptionTier, suggestedTier?, requiresUpgrade
Note: organizationId goes in the event envelope, not the payload.

Audit Events

EventSeverityPersistedPayload Interface
AUDIT_LOG_CREATEDINFO/ERRORYes (always)AuditLogPayload
AuditLogPayload: action, resourceType, resourceId, resourceName?, result (success/failure), errorMessage?, userName?, userEmail?, changes?, ipAddress?, userAgent?

Grant Pipeline Events

EventSeverityPersistedPayload Interface
GRANT_ADDED_TO_PIPELINEINFONoGrantAddedToPipelinePayload
GRANT_PIPELINE_STAGE_CHANGEDINFONoGrantPipelineStageChangedPayload
GRANT_APPLICATION_SUBMITTEDINFONoGrantApplicationSubmittedPayload
GRANT_WONINFONoGrantWonPayload
GRANT_LOSTWARNINGNoGrantLostPayload
GRANT_REPORT_DUEWARNINGNoGrantReportDuePayload
GRANT_REPORT_SUBMITTEDINFONoGrantReportSubmittedPayload
GRANT_DISBURSEMENT_RECEIVEDINFONoGrantDisbursementReceivedPayload
GrantAddedToPipelinePayload: pipelineId, opportunityId, grantTitle, grantorName, requestedAmount, currency, addedBy, addedByName, addedAt GrantPipelineStageChangedPayload: pipelineId, opportunityId, grantTitle, previousStage, newStage, changedBy, changedByName, reason?, changedAt GrantApplicationSubmittedPayload: applicationId, pipelineId, opportunityId, grantTitle, requestedAmount, currency, confirmationNumber?, submittedBy, submittedByName, submittedAt GrantWonPayload: grantId, pipelineId, opportunityId, grantTitle, grantorName, awardedAmount, currency, startDate, endDate, recordedAt, projectId (2026-04-14: server-side convert now auto-creates a linked project and includes its id; see ADR #17) GrantLostPayload: pipelineId, opportunityId, grantTitle, grantorName, requestedAmount, currency, reason?, recordedAt GrantReportDuePayload: grantId, grantTitle, reportId, reportType, dueDate, daysUntilDue, assignedTo? GrantReportSubmittedPayload: grantId, grantTitle, reportId, reportType, submittedBy, submittedByName, submittedAt GrantDisbursementReceivedPayload: grantId, grantTitle, disbursementId, amount, currency, receivedAt, totalReceived, remainingAmount

Intelligence Events

EventSeverityPersistedPayload Interface
GRANT_APPLICATION_OUTCOMEINFOYesGrantApplicationOutcomePayload
INTELLIGENCE_CONSENT_CHANGEDINFOYesIntelligenceConsentChangedPayload
INTELLIGENCE_SNAPSHOT_GENERATEDINFOYesIntelligenceSnapshotGeneratedPayload
GrantApplicationOutcomePayload: pipelineId, opportunityId, outcome (won/lost/dead), requestedAmount, awardedAmount?, currency, grantorType, decisionDays, projectName?, recordedAt IntelligenceConsentChangedPayload: action (granted/withdrawn), dataCategories, changedBy, changedByName, changedAt, withdrawalReason? IntelligenceSnapshotGeneratedPayload: snapshotId, snapshotDate, metricsCount, qualityScore (0-100), generatedAt

Document Brain Events

EventSeverityPersistedPayload Interface
DOCUMENT_UPLOADEDINFONoRecord<string, unknown> (TBD)
DOCUMENT_APPROVEDINFONoRecord<string, unknown> (TBD)
DOCUMENT_REJECTEDWARNINGNoRecord<string, unknown> (TBD)
DOCUMENT_REVIEW_REQUESTEDINFONoRecord<string, unknown> (TBD)
DOCUMENT_VERSION_CREATEDINFONoRecord<string, unknown> (TBD)
DOCUMENT_CLASSIFIEDINFONoRecord<string, unknown> (TBD)
DOCUMENT_DEADLINE_EXTRACTEDINFONoRecord<string, unknown> (TBD)
Payload interfaces for document events are not yet defined. They use Record<string, unknown> in the EventPayloadMap.

Portal Events (Stakeholder Portal)

EventSeverityPersistedPayload Interface
PORTAL_REPORT_ACKNOWLEDGEDINFOYesPortalReportAcknowledgedPayload
PORTAL_REVISION_REQUESTEDWARNINGYesPortalRevisionRequestedPayload
PORTAL_COMMENT_POSTEDINFOYesPortalCommentPostedPayload
PORTAL_REPORT_SUBMITTEDINFOYesPortalReportSubmittedPayload
PortalReportAcknowledgedPayload: reportId, reportName, projectId, projectName, status (received/approved), stakeholderName, stakeholderType (funder/partner/board/auditor), tokenId, acknowledgedAt PortalRevisionRequestedPayload: reportId, reportName, projectId, projectName, stakeholderName, stakeholderType, revisionNotes, tokenId, requestedAt PortalCommentPostedPayload: commentId, projectId, projectName, stakeholderName, stakeholderType, contentPreview, tokenId, postedAt PortalReportSubmittedPayload: submissionId, submissionTitle, projectId, projectName, stakeholderName, stakeholderType, attachmentCount, tokenId, submittedAt

Agent Lifecycle Events

EventSeverityPersistedPayload Interface
AGENT_TASK_STARTEDINFOYesAgentTaskStartedPayload
AGENT_TASK_COMPLETEDINFOYesAgentTaskCompletedPayload
AGENT_TASK_FAILEDERRORYesAgentTaskFailedPayload
AGENT_TASK_CANCELLEDWARNINGYesAgentTaskCancelledPayload
AGENT_STEP_COMPLETEDINFOYesAgentStepCompletedPayload
AGENT_ESCALATION_REQUIREDWARNINGYesAgentEscalationRequiredPayload
AGENT_ESCALATION_RESOLVEDINFOYesAgentEscalationResolvedPayload
AGENT_BUDGET_CONSUMEDWARNINGYesAgentBudgetConsumedPayload
AgentTaskStartedPayload: runId, agentType, triggeredBy, triggeredByName, input, creditsReserved, startedAt AgentTaskCompletedPayload: runId, agentType, stepsCompleted, creditsConsumed, tokensUsed, durationMs, output, completedAt AgentTaskFailedPayload: runId, agentType, error, failedAtStep, creditsConsumed, failedAt AgentTaskCancelledPayload: runId, agentType, cancelledBy, cancelledByName, stepsCompleted, creditsConsumed, cancelledAt AgentStepCompletedPayload: runId, agentType, stepId, stepIndex, toolName, creditsUsed, tokensUsed, durationMs, completedAt AgentEscalationRequiredPayload: runId, agentType, stepId, reason, options (string[]), requiredPermission?, escalatedAt AgentEscalationResolvedPayload: runId, agentType, stepId, resolvedBy, resolvedByName, resolution, resolvedAt AgentBudgetConsumedPayload: runId, creditsConsumed, creditsRemaining, percentageUsed

Credit Events

EventSeverityPersistedPayload Interface
CREDITS_CONSUMEDINFOYesRecord<string, unknown> (TBD)
CREDITS_EXHAUSTEDCRITICALYesCreditsExhaustedPayload
CREDITS_PURCHASEDINFOYesCreditsPurchasedPayload
CreditsExhaustedPayload: organizationId, totalCredits, creditsUsed, lastAgentRunId?, suggestedTier? CreditsPurchasedPayload: packSize, amount, currency, newBalance, stripePaymentIntentId?

Impact (M&E) Events

Core Indicator Events

EventSeverityPersistedPayload Interface
ME_INDICATOR_CREATEDINFOYesMEIndicatorCreatedPayload
ME_INDICATOR_UPDATEDINFOYesMEIndicatorUpdatedPayload
ME_INDICATOR_ARCHIVEDINFOYesMEIndicatorArchivedPayload
ME_TARGET_SETINFOYesMETargetSetPayload
ME_DATA_POINT_RECORDEDINFOYesMEDataPointRecordedPayload
ME_INDICATOR_STATUS_CHANGEDINFOYesMEIndicatorStatusChangedPayload

Approval Workflow Events

EventSeverityPersistedPayload Interface
ME_DATA_POINT_SUBMITTEDINFOYesMEDataPointSubmittedPayload
ME_DATA_POINT_APPROVEDINFOYesMEDataPointApprovedPayload
ME_DATA_POINT_REJECTEDWARNINGYesMEDataPointRejectedPayload

KoboToolbox Sync Events

EventSeverityPersistedPayload Interface
ME_KOBO_SYNC_COMPLETEDINFOYesMEKoboSyncCompletedPayload
ME_KOBO_SYNC_FAILEDERRORYesMEKoboSyncFailedPayload

Grant-Impact Events (Phase 3)

EventSeverityPersistedPayload Interface
ME_GRANT_INDICATOR_LINKEDINFOYesMEGrantIndicatorLinkedPayload
ME_GRANT_INDICATOR_UNLINKEDINFOYesMEGrantIndicatorUnlinkedPayload
ME_DONOR_EXPORT_GENERATEDINFOYesMEDonorExportGeneratedPayload
ME_PORTFOLIO_VIEWEDINFOYesMEPortfolioViewedPayload

AI Assistant & Workflow Engine Events (Phase 4)

EventSeverityPersistedPayload Interface
ME_AI_INDICATORS_SUGGESTEDINFOYesMEAIIndicatorsSuggestedPayload
ME_AI_SUGGESTION_ACCEPTEDINFOYesMEAISuggestionAcceptedPayload
ME_AI_SUGGESTION_DISMISSEDINFOYesMEAISuggestionDismissedPayload
ME_AI_NARRATIVE_GENERATEDINFOYesMEAINarrativeGeneratedPayload
ME_AI_ANOMALY_DETECTEDWARNINGYesMEAIAnomalyDetectedPayload
ME_WORKFLOW_RULE_CREATEDINFOYesMEWorkflowRuleCreatedPayload
ME_WORKFLOW_RULE_UPDATEDINFOYesMEWorkflowRuleUpdatedPayload
ME_WORKFLOW_RULE_DELETEDINFOYesMEWorkflowRuleDeletedPayload
ME_WORKFLOW_RULE_TRIGGEREDINFOYesMEWorkflowRuleTriggeredPayload
ME_WORKFLOW_RULE_FAILEDERRORYesMEWorkflowRuleFailedPayload

Module Marketplace Events

EventSeverityPersistedPayload Interface
MODULE_INSTALLEDINFOYesModuleInstalledPayload
MODULE_ACTIVATEDINFOYesModuleActivatedPayload
MODULE_DEACTIVATEDINFOYesModuleDeactivatedPayload
MODULE_UNINSTALLEDWARNINGYesModuleUninstalledPayload
MODULE_TRIAL_STARTEDINFOYesModuleTrialStartedPayload
MODULE_TRIAL_EXPIREDWARNINGYesModuleTrialExpiredPayload
MODULE_PAYMENT_FAILEDERRORYesModulePaymentFailedPayload
ModuleInstalledPayload: moduleId, moduleName, installedBy, trialDays? ModuleActivatedPayload: moduleId, moduleName, activatedBy, previousStatus, stripeSubscriptionItemId? ModuleDeactivatedPayload: moduleId, moduleName, deactivatedBy, reason? ModuleUninstalledPayload: moduleId, moduleName, uninstalledBy, dataRetained ModuleTrialStartedPayload: moduleId, moduleName, trialDays, trialEndsAt, startedBy ModuleTrialExpiredPayload: moduleId, moduleName, trialStartedAt, trialEndedAt ModulePaymentFailedPayload: moduleId, moduleName, stripeSubscriptionItemId?, failureReason?, retryCount?

Extension Health Events

EventSeverityPersistedPayload Interface
EXTENSION_AUTO_DISABLEDERRORYesExtensionAutoDisabledPayload
EXTENSION_HEALTH_CHECK_FAILEDWARNINGYesExtensionHealthCheckFailedPayload
EXTENSION_TENANT_SWITCHEDINFONoExtensionTenantSwitchedPayload
ExtensionAutoDisabledPayload: extensionId, extensionName, errorCount, windowMinutes, lastError, disabledBy (always 'health_monitor') ExtensionHealthCheckFailedPayload: extensionId, extensionName, errorCount, lastError, threshold ExtensionTenantSwitchedPayload: previousOrganizationId, newOrganizationId, activeExtensionIds

Tenant Classification Events

EventSeverityPersistedPayload Interface
TENANT_CLASSIFICATION_UPDATEDINFONoTenantClassificationUpdatedPayload
TenantClassificationUpdatedPayload: fieldsChanged, completenessScore, previousScore, source (ONBOARDING/SETTINGS/MIGRATION/AI_SUGGESTION), isMigration

EventPayloadMap

The EventPayloadMap interface in events.ts provides compile-time type safety for the IEventBus.on() and emit() methods. When subscribing to a specific SystemEventType, the handler’s event.payload is automatically typed to the correct payload interface.
// Type-safe subscription — payload is automatically typed
eventBus.on(SystemEventType.EXPENSE_APPROVED, (event) => {
  // event.payload is ExpenseApprovedPayload
  console.log(event.payload.expenseId, event.payload.amount);
});
The utility type TypedSystemEvent<E> narrows a SystemEvent to a specific event type with its payload resolved from the map.

Summary Statistics

CategoryEvent Count
Journal3
Expense3
Compliance3
Budget3
Project3
Project Lifecycle5
Project Template2
User2
Time Off3
Quota2
Audit1
Grant Pipeline8
Intelligence3
Document Brain7
Portal4
Agent Lifecycle8
Credit3
Impact (M&E)25
Module Marketplace7
Extension Health3
Tenant1
Total100

Maintenance

Update this document when:
  • Adding new SystemEventType values
  • Creating new payload interfaces
  • Changing persistence rules in requiresPersistence()
  • Adding new type guard functions