# ECOLE ECOIN Event Catalog

Date: 2026-05-21

Status: Phase 3 bounded EventBus foundation.

## Scope

This catalog separates lifecycle contracts that exist today from emitters that are safe to wire today. The Phase 3 rule is safety over coverage:

- Event classes are available as automation-safe contracts.
- Current wiring is limited to extracted Actions and clear seams already covered by tests.
- Legacy enrollment, payment, attendance, and Corporate workflows remain planned emitters until they are extracted or protected by focused regression tests.

## Payload Contract

Every Phase 3 event is a readonly business fact:

- Use IDs and safe scalar metadata only.
- Include `occurredAt`.
- Do not expose Livewire state, provider payload dumps, secrets, or whole Eloquent models.
- Dispatch transactional business events after commit.

## Event Registry

| Event | Owner | Safe payload | Current emitter | Planned emitter | Current listeners | Automation safe | Listener idempotency |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `EnrollmentCreated` | Enrollments | enrollment, cohort, user, actor, source IDs | Contract only | Enrollment creation Action after registration flow extraction | `CreateCrmLeadFromEnrollmentCreated` via current messaging seam | Yes | Implemented with `ListenerIdempotency` |
| `EnrollmentConfirmed` | Enrollments | enrollment, user, actor IDs | `SyncEnrollmentAfterPaymentConfirmed::handle()` | Confirm enrollment Action after payment transition extraction | `SyncCrmLeadOnEnrollmentConfirmed` | Yes | Implemented with `ListenerIdempotency` |
| `EnrollmentCancelled` | Enrollments | enrollment, actor IDs, reason | Contract only | Cancel enrollment Action | None | Yes | Required for compensation listeners |
| `DepositPaymentStarted` | Finance | payment, enrollment, actor IDs, source | Contract only | Payment initiation Action | None | Yes | Required for checkout tracking |
| `DepositPaymentApproved` | Enrollments | payment, enrollment, actor IDs | `PaymentObserver::updated()` for deposit confirmations only | Deposit approval Action when finance/enrollment boundary is extracted | `SyncCrmLeadOnDepositPaymentApproved` | Yes | Implemented with `ListenerIdempotency` |
| `PaymentReceiptUploaded` | Finance | payment, actor IDs | Contract only | Receipt upload Action/controller seam | None | Yes | Required for review notifications |
| `BalancePaymentCompleted` | Finance | payment, enrollment, actor IDs | Contract only | Balance completion Action | None | Yes | Required for certificate/finance projections |
| `AttendanceSessionCreated` | Scheduling | attendance session, cohort, actor IDs | `CreateAttendanceSessionAction` | Generated schedule Action when unified | None | Yes | Required before reminders/projections |
| `AttendanceMarked` | Attendance | attendance record, attendance session, actor IDs | Contract only | Attendance mark Action after old surfaces are extracted | None | Yes | Required before analytics/no-show listeners |
| `StudentNoShowDetected` | Attendance | enrollment, attendance session IDs | `AttendanceWorkflowService::save()` when the first session is completed as absent | No-show detection Action/job | `SyncCrmLeadOnStudentNoShowDetected` | Yes | Implemented with `ListenerIdempotency` |
| `B2bLeadCreated` | Corporate B2B | lead, actor IDs, source | Contract only | Public or WhatsApp B2B lead Action after lifecycle unification | None | Yes | Required for alerts/tracking |
| `B2bLeadConverted` | Corporate B2B | lead, organization, actor IDs | `ConvertB2bLeadToOrganizationAction` | Existing Action remains canonical | `TrackB2bLeadConverted`, `SyncCrmLeadOnB2bLeadConverted` | Yes | Implemented with `ListenerIdempotency` |
| `ProposalSent` | Corporate Proposals | proposal, organization, actor IDs | Contract only | Proposal send Action | None | Yes | Required before messaging/tracking |
| `ProposalAccepted` | Corporate Proposals | proposal, organization, actor IDs | Contract only | Proposal acceptance Action | None | Yes | Required before agreement automation |
| `AgreementActivated` | Corporate Agreements | agreement, organization, actor IDs | Contract only | Agreement activation Action | None | Yes | Required before delivery automation |
| `CorporateCohortStarted` | Corporate Cohorts | cohort, organization, actor IDs | Contract only | Corporate cohort lifecycle Action | None | Yes | Required before report/reminder listeners |
| `CorporateReportGenerated` | Corporate Reports | report, organization, actor IDs | Contract only | Report PDF generation Action | None | Yes | Required before organization messaging |
| `CorporateInvoiceIssued` | Corporate Finance | invoice, organization, actor IDs | Contract only | Invoice issue Action after finance slice is covered | None | Yes | Required before receivables notifications |
| `CorporatePaymentAllocated` | Corporate Finance | allocation, payment, invoice, organization, actor IDs | Contract only | Allocation Action after finance slice is covered | None | Yes | Required before revenue projections |
| `WhatsAppLeadCreated` | WhatsApp | conversation, lead, contact IDs, source | `CreateB2bLeadFromConversationAction` | WhatsApp journey creation Actions as they are extracted | `CreateCrmLeadFromWhatsAppLeadCreated` | Yes | Implemented with `ListenerIdempotency` |
| `WhatsAppConversationAssigned` | WhatsApp | conversation, assignee, actor IDs | Contract only | Conversation assignment Action | None | Yes | Required before agent notification |
| `WhatsAppReplySent` | WhatsApp | conversation, message, actor IDs, mode | `SendWhatsAppReplyAction` | Existing Action remains canonical | `LogCrmActivityForWhatsAppReplySent` | Yes | Implemented with `ListenerIdempotency` |
| `EventRegistrationCreated` | Events | event, registration, actor IDs | Legacy event funnel seam | Event registration Action | `CreateCrmLeadFromEventRegistrationCreated` | Yes | Implemented with `ListenerIdempotency` |
| `WebinarReminderTriggered` | Events | event, reminder IDs | Contract only | Reminder planner/job Action | None | Yes | Required before provider dispatch |
| `WebinarAttended` | Events | event, registration IDs | `EventFunnelService::confirmAttendance()` | Webinar attendance Action | `SyncCrmLeadOnWebinarAttended` | Yes | Implemented with `ListenerIdempotency` |
| `WebinarNoShowDetected` | Events | event, registration IDs | `EventFunnelService::markNoShow()` | Webinar no-show Action | `SyncCrmLeadOnWebinarNoShowDetected` | Yes | Implemented with `ListenerIdempotency` |
| `ConversionTracked` | Tracking | event name, source, related reference | Contract only | Tracking owned dispatcher/action | None | Yes | Tracking projections must deduplicate |
| `FunnelStepCompleted` | Tracking | funnel, step, related reference | Contract only | Tracking funnel Action | None | Yes | Tracking projections must deduplicate |

## Transitional Legacy-to-CRM Bridges

These are intentionally narrow integrations from already-existing safe seams. They are not broad lifecycle refactors.

| Existing event seam | CRM listener | Purpose |
| --- | --- | --- |
| `App\\Events\\Messaging\\EnrollmentCreated` | `CreateCrmLeadFromEnrollmentCreated` | Create one student CRM lead when an enrollment is created on the current safe seam |
| `App\\Events\\Messaging\\PaymentConfirmed` | `SyncCrmLeadOnPaymentConfirmed` | Move the linked student CRM lead to `confirmed` and append CRM payment-confirmed activity |
| `App\\Events\\Messaging\\EventRegistrationCreated` | `CreateCrmLeadFromEventRegistrationCreated` | Create one webinar/event CRM lead from the current safe event funnel seam |

## Wired Emitters In Phase 3

| Action | Event | Dispatch rule |
| --- | --- | --- |
| `ConvertB2bLeadToOrganizationAction` | `B2bLeadConverted` | Explicit `DB::afterCommit()` after the conversion transaction |
| `CreateB2bLeadFromConversationAction` | `WhatsAppLeadCreated` | Explicit `DB::afterCommit()` only when a new lead is created |
| `SendWhatsAppReplyAction` | `WhatsAppReplySent` | Explicit `DB::afterCommit()` after message state is persisted |
| `CreateAttendanceSessionAction` | `AttendanceSessionCreated` | Explicit `DB::afterCommit()` after session/audit transaction |

## Planned Listener Families

| Listener family | Allowed behavior | Boundary |
| --- | --- | --- |
| Notification | Email, WhatsApp, Telegram, in-app notices, reminder scheduling | Must call Messaging or WhatsApp public APIs |
| Tracking | Conversion records, pixels, funnel projections | Must never mutate workflow state |
| CRM | Future tasks, tags, follow-up queues | Must call domain Actions for state writes |
| Projections | Read models, operational counters | Must be idempotent and tenant-safe |
