Skip to main content

05 — Event Schemas

Status: ✅ Draft
Prerequisite: 04 — Event Catalog
Next: 06 — Authentication Flow

Formal payload schemas for every event type in the catalog. Each schema defines the fields inside the payload block of the standard envelope. The envelope itself is fixed — every event carries it unchanged.

Core-published event schemas (C-020 to C-026) are the authoritative contract. Module-published event schemas included here cover the events Core subscribes to — modules own the full definition of all other events they publish.


1. Standard Envelope Schema​

Every event on the platform carries this envelope. It does not vary by event type.

{
"eventId": "uuid-v4",
"eventType": "registration.confirmed",
"schemaVersion": "1.0",
"timestamp": "2026-07-15T10:30:00Z",
"idempotencyKey": "uuid-v4",
"sourceModule": "ams",
"miqaatId": "MQ-1447-KHI",
"itsId": "ITS-1234567",
"correlationId": "uuid-v4",
"payload": { }
}

Envelope field definitions​

FieldTypeRequiredDescription
eventIdstring (UUID v4)✅Unique identifier for this event instance. Never reused.
eventTypestring✅The registered event type name (domain.action notation).
schemaVersionstring✅Schema version of the payload block (e.g. "1.0", "2.0"). Consumers use this to select their parser.
timestampstring (ISO 8601)✅When the event was produced — UTC. Set by the publisher at the moment of publish.
idempotencyKeystring (UUID v4)✅Consumers use this to detect duplicate delivery. Two events with the same idempotencyKey carry the same logical event — the second delivery is a duplicate and must be discarded.
sourceModulestring✅Module identifier of the publisher (e.g. ams, vms, rbac-engine). Matches X-Module-Id in API calls.
miqaatIdstring✅The Miqaat scope this event belongs to. All events are scoped to a Miqaat.
itsIdstring❌The itsId of the Mumin this event concerns — present on person-specific events, absent on structural events (e.g. miqaat.phase.changed).
correlationIdstring (UUID v4)❌Carries the X-Correlation-Id from the triggering API call, if the event was produced as a result of a synchronous request. Enables tracing from API call → event.
payloadobject✅Event-specific data. Defined per event type below.

2. Schema Versioning​

Version format​

schemaVersion follows semantic versioning with two levels: MAJOR.MINOR.

Change typeVersion bumpConsumer impact
New optional field added to payloadMINOR (e.g. 1.0 → 1.1)Safe — existing consumers ignore unknown fields
Removing a field from payloadMAJOR (e.g. 1.x → 2.0)Breaking — consumers must migrate
Renaming or retyping a fieldMAJORBreaking
Changing the meaning of an existing fieldMAJORBreaking

Parallel version support​

When a MAJOR version bump occurs, the old schema version continues to be published in parallel until all declared subscribers have migrated. The transition window is agreed between the Core team and affected module teams before the new version is deployed.

During the parallel window, both 1.x and 2.0 events may be in the stream. Consumers check schemaVersion and route to the appropriate parser.

// Consumer version routing
if (event.schemaVersion.startsWith("1.")) {
parseV1(event.payload);
} else if (event.schemaVersion.startsWith("2.")) {
parseV2(event.payload);
}

Old version support is removed only after Platform Admin confirms all subscribers have migrated. Premature removal of a version is a breaking contract violation.


3. Core-Published Event Schemas (C-020 to C-026)​

Core engine event schemas are the authoritative contracts. These are the events modules consume from Core.


miqaat.created · C-020a​

Payload

{
"miqaatId": "MQ-1447-KHI",
"name": "Ashara Mubaraka 1447 — Karachi",
"type": "ashara_mubaraka",
"createdAt": "2026-05-01T08:00:00Z",
"createdBy": "ITS-9999999"
}
FieldTypeRequiredDescription
miqaatIdstring✅The newly assigned Miqaat identifier
namestring✅Display name of the Miqaat
typestring✅Miqaat type (ashara_mubaraka, misaaq, safar, etc.)
createdAtstring (ISO 8601)✅When Platform Admin created the Miqaat
createdBystring✅itsId of the Platform Admin user who created it

itsId is absent from the envelope for this event — this is a structural event, not a person-specific event.


miqaat.phase.changed · C-020​

Payload

{
"miqaatId": "MQ-1447-KHI",
"fromPhase": "setup",
"toPhase": "registration_open",
"changedAt": "2026-07-01T00:00:00Z",
"changedBy": "ITS-9999999"
}
FieldTypeRequiredDescription
miqaatIdstring✅The Miqaat whose phase changed
fromPhasestring✅The previous phase
toPhasestring✅The new active phase
changedAtstring (ISO 8601)✅When the transition occurred
changedBystring✅itsId of the Platform Admin user who triggered the transition

Valid phase values: setup · registration_open · registration_closed · operational · closed · archived

itsId is absent from the envelope — structural event.


miqaat.archived · C-020b​

Payload

{
"miqaatId": "MQ-1447-KHI",
"archivedAt": "2026-11-01T00:00:00Z",
"archivedBy": "ITS-9999999"
}
FieldTypeRequiredDescription
miqaatIdstring✅The archived Miqaat
archivedAtstring (ISO 8601)✅When the Miqaat was archived
archivedBystring✅itsId of the Platform Admin user who triggered archival

allocation.confirmed · C-021​

Payload

{
"allocationId": "ALO-20261001-00042",
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"category": "residential",
"zone": "KHI-NORTH",
"requestedBy": "ams",
"confirmedAt": "2026-07-15T10:30:00Z"
}
FieldTypeRequiredDescription
allocationIdstring✅The allocation record identifier
itsIdstring✅The Mumin who received the allocation
miqaatIdstring✅The Miqaat scope
categorystring✅Allocation category (e.g. residential, day_visitor, khidmatguzar)
zonestring❌Zone assigned, if zone-quota allocation mode is active
requestedBystring✅The module that submitted the allocation request
confirmedAtstring (ISO 8601)✅When the allocation was confirmed

allocation.waitlisted · C-022​

Payload

{
"allocationId": "ALO-20261001-00043",
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"category": "residential",
"waitlistPosition": 14,
"requestedBy": "ams",
"waitlistedAt": "2026-07-15T10:30:01Z"
}
FieldTypeRequiredDescription
allocationIdstring✅The allocation record identifier (waitlisted state)
itsIdstring✅The Mumin placed on the waitlist
miqaatIdstring✅The Miqaat scope
categorystring✅Allocation category
waitlistPositioninteger✅Current position in the waitlist at time of placement
requestedBystring✅The module that submitted the request
waitlistedAtstring (ISO 8601)✅When the Mumin was placed on the waitlist

allocation.released · C-022a​

Payload

{
"allocationId": "ALO-20261001-00042",
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"category": "residential",
"reason": "cancelled_by_mumin",
"releasedAt": "2026-08-01T09:00:00Z",
"releasedBy": "ITS-1234567",
"nextWaitlistId": "ITS-9876543"
}
FieldTypeRequiredDescription
allocationIdstring✅The allocation that was released
itsIdstring✅The Mumin whose allocation was released
miqaatIdstring✅The Miqaat scope
categorystring✅Allocation category
reasonstring✅Why the allocation was released. Values: cancelled_by_mumin · revoked_by_operator · capacity_change
releasedAtstring (ISO 8601)✅When the release occurred
releasedBystring✅itsId of the person who triggered the release
nextWaitlistIdstring❌itsId of the next person on the waitlist who will be promoted. Absent if waitlist is empty.

capacity.imbalance.detected · C-023​

Payload

{
"miqaatId": "MQ-1447-KHI",
"detectedAt": "2026-06-12T08:00:01Z",
"imbalances": [
{
"description": "Vaaz capacity (8,000) is more than double Mawaid capacity (3,200). Ratio exceeds threshold.",
"venueA": "vaaz",
"capacityA": 8000,
"venueB": "mawaid",
"capacityB": 3200,
"ratio": 2.5,
"threshold": 2.0
}
]
}
FieldTypeRequiredDescription
miqaatIdstring✅The Miqaat where the imbalance was detected
detectedAtstring (ISO 8601)✅When the imbalance was detected
imbalancesarray✅One object per detected imbalance
imbalances[].descriptionstring✅Human-readable description for Platform Admin display
imbalances[].venueAstring✅First venue in the comparison
imbalances[].capacityAinteger✅Capacity reported by venueA
imbalances[].venueBstring✅Second venue in the comparison
imbalances[].capacityBinteger✅Capacity reported by venueB
imbalances[].rationumber✅Actual ratio (capacityA / capacityB)
imbalances[].thresholdnumber✅The configured threshold that was exceeded

itsId is absent from the envelope — structural event, not person-specific.


eligibility.status.changed · C-024​

Payload

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"ruleSetId": "ISTEFADAH_2026_GENTS",
"previousStatus": "eligible",
"newStatus": "ineligible",
"changedAt": "2026-07-20T14:00:00Z",
"reason": "PRIOR_ATTENDANCE rule failed after ITS sync update"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin whose eligibility changed
miqaatIdstring✅The Miqaat scope
ruleSetIdstring✅The rule set whose evaluation changed
previousStatusstring✅The eligibility status before this change. Values: eligible · ineligible · unknown
newStatusstring✅The new eligibility status
changedAtstring (ISO 8601)✅When the status changed
reasonstring❌Human-readable description of what caused the status change — for logging and audit, not for display

role.assigned · C-025​

Payload

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"roleId": "VMS_Coordinator",
"assignedAt": "2026-06-01T08:00:00Z",
"assignedBy": "ITS-9999999"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin who was assigned the role
miqaatIdstring✅The Miqaat scope the role applies in
roleIdstring✅The role identifier assigned
assignedAtstring (ISO 8601)✅When the assignment occurred
assignedBystring✅itsId of the Platform Admin user who made the assignment

role.revoked · C-026​

Payload

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"roleId": "VMS_Coordinator",
"revokedAt": "2026-09-15T10:00:00Z",
"revokedBy": "ITS-9999999",
"reason": "Role reassigned to different coordinator"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin whose role was revoked
miqaatIdstring✅The Miqaat scope
roleIdstring✅The role that was revoked
revokedAtstring (ISO 8601)✅When the revocation occurred
revokedBystring✅itsId of the Platform Admin user who revoked it
reasonstring❌Optional reason for revocation

Cache invalidation: any module that receives role.revoked must immediately clear its local permission cache for the affected itsId + miqaatId. See 03 — API Contracts, C-010.


4. Module-Published Event Schemas​

Core subscribes to the events below. The schema here is the minimum Core requires from the payload. Modules may include additional fields — Core ignores unknown fields.

Module-published events that Core does not subscribe to (most of section 4 of the Event Catalog) are owned entirely by the module. Core does not define their payload schema.


registration.confirmed​

Core subscriber: Eligibility Engine, HR Bank, Audit Log

Minimum payload required by Core

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"registrationId": "REG-AMS-2026-00891",
"registrationType": "residential",
"confirmedAt": "2026-07-10T12:00:00Z"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin whose registration was confirmed
miqaatIdstring✅The Miqaat scope
registrationIdstring✅The module's own registration record identifier
registrationTypestring✅Type of registration (e.g. residential, day_visitor, istefadah)
confirmedAtstring (ISO 8601)✅When the registration was confirmed

attendance.recorded​

Core subscriber: Eligibility Engine (prior attendance rules), HR Bank, Audit Log

Minimum payload required by Core

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"sessionId": "SESSION-WAAZ-D3",
"sessionType": "waaz",
"recordedAt": "2026-10-03T09:30:00Z"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin whose attendance was recorded
miqaatIdstring✅The Miqaat scope
sessionIdstring✅The module's identifier for the session (day, vaaz, majlis, etc.)
sessionTypestring✅Type of session (e.g. waaz, majlis, khidmat_shift, general)
recordedAtstring (ISO 8601)✅When attendance was recorded

khidmat.assigned​

Core subscriber: HR Bank (builds cross-Miqaat history record), Audit Log

Minimum payload required by Core

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"khidmatRole": "VMS_Coordinator",
"isPrimary": true,
"assignedAt": "2026-06-15T10:00:00Z"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin assigned to the khidmat
miqaatIdstring✅The Miqaat scope
khidmatRolestring✅The khidmat role identifier — should align with the RBAC role name where applicable
isPrimaryboolean✅Whether this is the Mumin's primary khidmat for this Miqaat (governs the one-primary-khidmat rule)
assignedAtstring (ISO 8601)✅When the assignment was made

khidmat.completed​

Core subscriber: HR Bank (marks history record as completed), Audit Log

Minimum payload required by Core

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"khidmatRole": "VMS_Coordinator",
"completedAt": "2026-10-10T20:00:00Z",
"confirmedBy": "ITS-9999999"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin who completed the khidmat
miqaatIdstring✅The Miqaat scope
khidmatRolestring✅The khidmat role that was completed
completedAtstring (ISO 8601)✅When the khidmat was completed
confirmedBystring✅itsId of the coordinator who confirmed completion

capacity.configured​

Core subscriber: Capacity Balance Engine, Audit Log

Minimum payload required by Core

{
"miqaatId": "MQ-1447-KHI",
"venue": "vaaz",
"capacity": 8000,
"configuredAt": "2026-06-10T09:00:00Z",
"isFinal": true
}
FieldTypeRequiredDescription
miqaatIdstring✅The Miqaat scope
venuestring✅The venue or service type reporting capacity (e.g. vaaz, mawaid, kitchen, accommodation)
capacityinteger✅The capacity figure being reported
configuredAtstring (ISO 8601)✅When this figure was set
isFinalboolean✅Whether this is a final figure or a provisional estimate. Capacity Balance Engine only alerts on isFinal: true configurations.

If isFinal: false, the Capacity Balance Engine records the figure but does not trigger imbalance detection until a final figure is received from each expected venue.


payment.received​

Core subscriber: Eligibility Engine (payment-gate rule family), Audit Log

Minimum payload required by Core

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"paymentId": "PAY-2026-00312",
"paymentType": "vajebaat",
"amount": 1500,
"currency": "INR",
"receivedAt": "2026-07-12T14:00:00Z"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin for whom payment was received
miqaatIdstring✅The Miqaat scope
paymentIdstring✅The module's payment record identifier
paymentTypestring✅Type of payment (e.g. vajebaat, registration_fee, accommodation_fee)
amountnumber✅Amount received
currencystring✅ISO 4217 currency code (e.g. INR, PKR, USD)
receivedAtstring (ISO 8601)✅When payment was confirmed received and cleared

misaaq.recorded​

Core subscriber: Eligibility Engine (Misaaq rule family), Audit Log

Minimum payload required by Core

{
"itsId": "ITS-1234567",
"miqaatId": "MQ-1447-KHI",
"recordedAt": "2026-10-05T11:00:00Z",
"recordedBy": "ITS-9999999"
}
FieldTypeRequiredDescription
itsIdstring✅The Mumin for whom Misaaq was recorded
miqaatIdstring✅The Miqaat scope at which Misaaq was taken
recordedAtstring (ISO 8601)✅When the Misaaq was recorded
recordedBystring✅itsId of the authorised user who recorded it

indent.raised​

Core subscriber: Vendor Registry (budget tracking, conflict detection), Audit Log

Minimum payload required by Core

{
"miqaatId": "MQ-1447-KHI",
"indentId": "INDENT-AMS-2026-0041",
"category": "catering",
"vendorId": "VND-KHI-00142",
"amount": 50000,
"currency": "INR",
"raisedAt": "2026-08-01T10:00:00Z",
"raisedBy": "ITS-1234567"
}
FieldTypeRequiredDescription
miqaatIdstring✅The Miqaat scope
indentIdstring✅The module's indent record identifier
categorystring✅Procurement category (e.g. catering, seating, transport, printing)
vendorIdstring❌The vendor from Core's Vendor Registry, if a specific vendor is named
amountnumber✅Proposed procurement amount
currencystring✅ISO 4217 currency code
raisedAtstring (ISO 8601)✅When the indent was raised
raisedBystring✅itsId of the user who raised the indent

5. Schema Registry and Registration Process​

The Domain Event Schema Registry (see 01 — Service Decomposition) is the authoritative store for all registered event schemas.

Before a module can publish an event:

  1. Module team submits the proposed event type name and payload schema to Core team
  2. Core team reviews: naming convention compliance, no collision with existing event types, minimum required fields present
  3. Schema is registered in the Domain Event Schema Registry with schemaVersion: "1.0"
  4. Module team is notified — the registered event type name is now the permanent contract identifier

Schema updates:

  • MINOR additions — module team notifies Core team; schema is updated in the registry; no consumer impact
  • MAJOR changes — Core team coordinates the migration window with all declared subscribers before the new schema is registered

6. Open Questions​

QuestionBlocked On
Full payload schema for module-published events not consumed by Core (e.g. checkpoint.scanned, pass.generated)Module discovery sessions — modules own these schemas
schemaVersion format: should patch version be added (1.0.0)?Architecture decision — current MAJOR.MINOR may be sufficient
Whether Core validates incoming module event payloads against the registry schema, or accepts and logs errorsInfrastructure / event bus session
Misaaq rule set mapping — which specific rule sets does the Eligibility Engine react to on misaaq.recorded?Business session