Event catalog
Event catalog
Section titled “Event catalog”Audience: backend
Status: specced
Owns: backend
Depends on: System architecture, Coding standards, Architecture handover
Living catalog of every RabbitMQ event: name, producer, consumers, payload schema, version. Payload JSON Schemas live under contracts/events/ and are browsable at Contracts — Events.
Envelope
Section titled “Envelope”Every outbox row and RabbitMQ message uses this envelope:
{ "eventId": "01hz...", "eventType": "payment.completed", "version": 1, "occurredAt": "2026-07-28T10:00:00Z", "producedBy": "orchestrator", "correlationId": "req_01hz...", "data": { }}| Field | Rule |
|---|---|
eventId |
Dedupe key for at-least-once delivery (ULID) |
version |
Lets payload shape evolve without breaking existing consumers |
correlationId |
Ties the event back to the originating request’s trace |
| Naming | resource.past_tense_verb |
Schema: event-envelope.v1.json.
Conventions
Section titled “Conventions”- Producers write via transactional outbox, then publish.
- Consumers are idempotent (dedupe on
eventId). - Version payloads; keep emitting the previous shape for at least one release when breaking.
- Per-event JSON Schemas describe the
dataobject only.
Catalog
Section titled “Catalog”| Event | Producer | Consumers | Trigger | Key data fields |
Schema |
|---|---|---|---|---|---|
user.created |
Orchestrator | CAP, LMS, WorkMasters | Registration / OTP-verify complete | userId, email, intents[] |
v1 |
user.deleted |
Orchestrator | CAP | POST /auth/delete-account |
userId |
v1 |
payment.completed |
Orchestrator | CAP, LMS | Webhook processed | referenceType, referenceId, paymentId, amount, paidAt, provider? |
v1 |
payment.failed |
Orchestrator | CAP, LMS | Webhook reports failure | referenceType, referenceId, paymentId, … |
v1 |
notification.requested |
Any business service | Orchestrator (Notification) | A service decides a user needs notifying | userId, channel, template, data, platform |
v1 |
application.stage_changed |
CAP | Audit service | Workflow engine advances a stage | applicationId, fromStage, toStage, outcome |
v1 |
application.evaluation_inconclusive |
CAP | LMS | Assessment outcome unsuccessful (recommendation flow) | applicationId, candidateId, failedUnitIds[] |
v1 |
application.certified |
CAP | WorkMasters, LMS | Certificate issued | applicationId, candidateId, certificateAssetId, issuedAt |
v1 |
lms.enrollment.activated |
LMS | Notify / analytics | Entitlement active | enrollmentId, courseId, learnerUserId, source |
v1 |
lms.course.completed |
LMS | CAP, WorkMasters later | Completion policy satisfied | enrollmentId, courseId, learnerUserId, certificateId?, capApplicationId? |
v1 |
lms.certificate.issued |
LMS | WorkMasters later | LMS certificate issued | certificateId, enrollmentId, assetId?, issuedAt |
v1 |
| (security audit events) | All services | Audit service | Login, PII access, admin action | actorId, action, resourceType, resourceId |
(separate audit service — not CAP/OL schemas) |
Design note — who requests notifications
Section titled “Design note — who requests notifications”notification.requested is emitted by the business service that decides a notification is needed. Orchestrator does not subscribe to CAP domain events to infer when to notify. Same principle as payment/identity: Orchestrator stays generic and does not need CAP’s domain event catalog. On dispatch, OL skips the requested channel if the user disabled it in GET/PUT /notifications/preferences, except auth-critical templates (auth.account_verify, auth.password_reset, ol.account_provisioned).
Account deletion is an event. POST /auth/delete-account deactivates the Orchestrator User and outbox-publishes user.deleted { userId }. CAP redacts onboarding/candidate PII, inactivates CentreStaff, suspends assessor profiles, and removes awarding-body staff membership. Applications, certificates, and wallet history stay. The last centre super_admin may still delete; UI should warn via CAP GET /me/deletion-eligibility.
Chat send is not an event. The client posts messages to Orchestrator REST. Optional email/in-app pings are in-process dispatch on OL (channel allow-list). CAP does not emit notification.requested for Send. A future system-line in a thread may use an event; that is out of v1.
Centre-staff invite secrets are not an event. CAP calls Orchestrator gRPC ProvisionAccount. New-account generated passwords (and that email) are in-process OL dispatch — they must never appear on notification.requested. Existing-member “added to centre” mail may be in-process on the same RPC or a no-secret notification.requested. user.created remains the provision signal for CAP (idempotent); there is no cap.staff.invited consumer on OL.
Payment initiation is gRPC InitiatePayment; completion is async payment.completed — see Payment architecture.
Identity: gRPC VerifyIdentity and ProvisionAccount — see Contracts — gRPC.
Review UI: Contracts hub · Events · gRPC.