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.
eventTypestringThe registered event type name (domain.action notation).
schemaVersionstringSchema 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.
sourceModulestringModule identifier of the publisher (e.g. ams, vms, rbac-engine). Matches X-Module-Id in API calls.
miqaatIdstringThe Miqaat scope this event belongs to. All events are scoped to a Miqaat.
itsIdstringThe 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.
payloadobjectEvent-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.01.1)Safe — existing consumers ignore unknown fields
Removing a field from payloadMAJOR (e.g. 1.x2.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
miqaatIdstringThe newly assigned Miqaat identifier
namestringDisplay name of the Miqaat
typestringMiqaat type (ashara_mubaraka, misaaq, safar, etc.)
createdAtstring (ISO 8601)When Platform Admin created the Miqaat
createdBystringitsId 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
miqaatIdstringThe Miqaat whose phase changed
fromPhasestringThe previous phase
toPhasestringThe new active phase
changedAtstring (ISO 8601)When the transition occurred
changedBystringitsId 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
miqaatIdstringThe archived Miqaat
archivedAtstring (ISO 8601)When the Miqaat was archived
archivedBystringitsId 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
allocationIdstringThe allocation record identifier
itsIdstringThe Mumin who received the allocation
miqaatIdstringThe Miqaat scope
categorystringAllocation category (e.g. residential, day_visitor, khidmatguzar)
zonestringZone assigned, if zone-quota allocation mode is active
requestedBystringThe 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
allocationIdstringThe allocation record identifier (waitlisted state)
itsIdstringThe Mumin placed on the waitlist
miqaatIdstringThe Miqaat scope
categorystringAllocation category
waitlistPositionintegerCurrent position in the waitlist at time of placement
requestedBystringThe 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
allocationIdstringThe allocation that was released
itsIdstringThe Mumin whose allocation was released
miqaatIdstringThe Miqaat scope
categorystringAllocation category
reasonstringWhy the allocation was released. Values: cancelled_by_mumin · revoked_by_operator · capacity_change
releasedAtstring (ISO 8601)When the release occurred
releasedBystringitsId of the person who triggered the release
nextWaitlistIdstringitsId 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
miqaatIdstringThe Miqaat where the imbalance was detected
detectedAtstring (ISO 8601)When the imbalance was detected
imbalancesarrayOne object per detected imbalance
imbalances[].descriptionstringHuman-readable description for Platform Admin display
imbalances[].venueAstringFirst venue in the comparison
imbalances[].capacityAintegerCapacity reported by venueA
imbalances[].venueBstringSecond venue in the comparison
imbalances[].capacityBintegerCapacity reported by venueB
imbalances[].rationumberActual ratio (capacityA / capacityB)
imbalances[].thresholdnumberThe 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
itsIdstringThe Mumin whose eligibility changed
miqaatIdstringThe Miqaat scope
ruleSetIdstringThe rule set whose evaluation changed
previousStatusstringThe eligibility status before this change. Values: eligible · ineligible · unknown
newStatusstringThe new eligibility status
changedAtstring (ISO 8601)When the status changed
reasonstringHuman-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
itsIdstringThe Mumin who was assigned the role
miqaatIdstringThe Miqaat scope the role applies in
roleIdstringThe role identifier assigned
assignedAtstring (ISO 8601)When the assignment occurred
assignedBystringitsId 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
itsIdstringThe Mumin whose role was revoked
miqaatIdstringThe Miqaat scope
roleIdstringThe role that was revoked
revokedAtstring (ISO 8601)When the revocation occurred
revokedBystringitsId of the Platform Admin user who revoked it
reasonstringOptional 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
itsIdstringThe Mumin whose registration was confirmed
miqaatIdstringThe Miqaat scope
registrationIdstringThe module's own registration record identifier
registrationTypestringType 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
itsIdstringThe Mumin whose attendance was recorded
miqaatIdstringThe Miqaat scope
sessionIdstringThe module's identifier for the session (day, vaaz, majlis, etc.)
sessionTypestringType 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
itsIdstringThe Mumin assigned to the khidmat
miqaatIdstringThe Miqaat scope
khidmatRolestringThe khidmat role identifier — should align with the RBAC role name where applicable
isPrimarybooleanWhether 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
itsIdstringThe Mumin who completed the khidmat
miqaatIdstringThe Miqaat scope
khidmatRolestringThe khidmat role that was completed
completedAtstring (ISO 8601)When the khidmat was completed
confirmedBystringitsId 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
miqaatIdstringThe Miqaat scope
venuestringThe venue or service type reporting capacity (e.g. vaaz, mawaid, kitchen, accommodation)
capacityintegerThe capacity figure being reported
configuredAtstring (ISO 8601)When this figure was set
isFinalbooleanWhether 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
itsIdstringThe Mumin for whom payment was received
miqaatIdstringThe Miqaat scope
paymentIdstringThe module's payment record identifier
paymentTypestringType of payment (e.g. vajebaat, registration_fee, accommodation_fee)
amountnumberAmount received
currencystringISO 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
itsIdstringThe Mumin for whom Misaaq was recorded
miqaatIdstringThe Miqaat scope at which Misaaq was taken
recordedAtstring (ISO 8601)When the Misaaq was recorded
recordedBystringitsId 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
miqaatIdstringThe Miqaat scope
indentIdstringThe module's indent record identifier
categorystringProcurement category (e.g. catering, seating, transport, printing)
vendorIdstringThe vendor from Core's Vendor Registry, if a specific vendor is named
amountnumberProposed procurement amount
currencystringISO 4217 currency code
raisedAtstring (ISO 8601)When the indent was raised
raisedBystringitsId 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