TD3 — Contracts
Decision Status Labels ✅ Decided — confirmed in a stakeholder session. Can be built against. 🔍 Under Analysis — options identified. Do not build against yet. 📅 Pending Session — requires a specific session or external input before a decision can be made.
1. What a Contract Is
A contract is a formal, versioned interface agreement. It defines exactly what gets sent, what comes back, and what behaviour both sides commit to. It is not a registration document — Core does not need to know a module exists in order for the module to call a Core API. Any module can call a published Core API endpoint. The contract is the published specification of what that call looks like.
Without written contracts, every team builds against their own assumption of how Core works. When Core changes something, teams discover the breakage in production. Contracts make the interface explicit, versioned, and stable. They are the document that module teams build against, and the document that Core is accountable to.
A contract is not:
- A registration of a module's existence with Core
- Permission from Core for a module to be built
- A description of what the module does internally
A contract is:
- The exact interface spec for one integration point between a module and Core
- A versioned commitment from Core that the interface will not change without notice
- The basis for contract testing — automated verification that both sides are honouring what they agreed
2. Contract Types
There are three types of contracts on this platform, each covering a different class of integration.
Type 1 — ITS Integration Contracts
Agreements between Core and ITS. These define how Core receives identity data and master data from ITS. They are not module-facing — module teams do not need to read these. They are the foundation that makes everything else work: Core cannot validate an itsId, sync Mumineen records, or resolve a Jamaat hierarchy without these contracts being in place.
Type 2 — Core API Contracts
Agreements between Core and any module that calls a Core engine via synchronous HTTP (Pattern A from TD2). These are public — any module team can read them and build against them without asking Core's permission. Each contract covers one Core engine API endpoint or a related group of endpoints.
Type 3 — Event Contracts
Agreements about what gets published to the event bus and in what shape (Pattern B from TD2). These come in two directions: Core publishes events that modules may subscribe to (Core → Module), and modules publish events that Core engines (and other modules) subscribe to (Module → Core/Module). Each event type has its own schema contract registered with the Domain Event Schema Registry.
3. What Every Contract Must Cover
Regardless of type, every contract on this platform includes the same standard sections. This consistency is what makes contract testing possible and what makes it safe for teams to build against them independently.
| Section | What it defines |
|---|---|
| Contract ID | Unique identifier (e.g., C-005). Referenced in audit logs, changelogs, and System Definition Documents. |
| Contract type | ITS Integration / Core API / Event |
| Version | Semantic version (e.g., 1.2.0). Breaking change = major version bump. |
| Status | Draft / Active / Deprecated / Retired |
| Owner | Which team is responsible for honouring this contract (Core team for Core APIs and ITS contracts; module team for their event publish contracts) |
| Interface definition | For API contracts: endpoint path, HTTP method, request schema, response schema, error response schema. For event contracts: event type name, payload schema (JSON Schema), schemaVersion value. |
| Authentication | Which call type this endpoint accepts: user-initiated (requires X-Its-Id), system-initiated (no itsId required), or both. |
| SLA | Expected response time (for API contracts). Expected event delivery latency (for event contracts). |
| Versioning rules | What constitutes a breaking change for this contract. What the deprecation notice period is. |
| Deprecation date | Populated when a version enters deprecation. Subscribers must migrate before this date. |
| Change log | History of changes per version with migration notes. |
4. Type 1 — ITS Integration Contracts
These are the two contracts between Core and ITS that everything else depends on. Neither can be finalised until the ITS technical discovery session is complete.
📅 C-001 — ITS SSO Token Contract
Defines the format and validation behaviour of ITS authentication tokens. Core's Identity Bridge must know exactly how to validate a token and extract an itsId from it. Without this contract, the Identity Bridge cannot be built.
Important clarification — Core does not take over ITS login. All 32 modules continue using ITS login directly, exactly as they do today. The Identity Bridge only validates ITS tokens on calls coming into Core's API Gateway — it is not a login system, not an auth broker, and not a replacement for each module's existing ITS session handling. C-001 is purely about Core knowing the token format well enough to extract a verified itsId from it.
What this contract must define once the session happens:
- Token format (JWT structure, claims, signing algorithm)
- Token validation endpoint (where Core calls to verify a token)
- Token expiry and refresh behaviour
- itsId format and uniqueness guarantee
- How the Identity Bridge handles expired or invalid tokens (error codes, response format)
- Session duration and single-session enforcement rules
📅 C-005 — ITS ↔ Core Data Integration Contract
Defines how ITS master data flows into Core. Core's Eligibility Engine, Allocation Engine, Zone Mapping, and HR Bank all depend on this data being current and correct.
✅ The principle — confirmed
Core is the single point of ITS data ingestion. No business module syncs from ITS independently.
Today, many of the 32 modules each maintain their own copy of ITS data — their own sync process, their own subset of Mumineen records, their own stale risk. That is the same duplication problem the platform exists to solve, applied to ITS data. Core doing its own sync alongside all of them would make it the 33rd instance of the same pattern — which defeats the purpose.
The confirmed principle: Core syncs from ITS once. Modules that need ITS data query Core for it. As modules migrate to Core, their individual ITS syncs are deprecated. This consolidates 32+ sync processes into one, eliminates 32 stale copies of the same data, and makes ITS the unambiguous single source of truth with Core as its only read replica.
The value of this goes beyond architecture. A single sync means a single place to monitor for failures, a single place to audit what data Core is working from, and a single upgrade path when ITS changes its schema.
🔍 The mechanism — under analysis, pending ITS session
While the principle is confirmed, the technical mechanism for how ITS data gets into Core is not. Three options need to be evaluated with the ITS team:
Option A — Pull sync (Core-initiated, scheduled) Core runs a scheduled process that pulls from ITS on a defined cadence. A full sync on a schedule (e.g., nightly), with a delta sync process that pulls only changed records more frequently. Simple to build, straightforward to operate. The downside: there is always a window between ITS updating a record and Core having it. During that window, Core may make decisions on slightly stale data.
Option B — ITS push (event-driven, ITS-initiated) ITS publishes a change event whenever a member record is created or updated. Core subscribes and updates its copy instantly. No scheduled job, no stale window, no polling. The cleanest long-term solution. The constraint: this requires ITS to adopt event publishing — a significant change to ITS's own architecture and a meaningful ask of the ITS team. Feasibility depends on ITS's current stack and team capacity, which is unknown until the technical session.
Option C — Hybrid Full pull sync at startup and after any gap in connectivity (to ensure completeness). ITS push events for incremental changes in steady state. Gets the benefits of both: reliability of the pull for completeness, freshness of the push for ongoing changes. More complex to build and operate, but the most resilient in practice.
The right choice depends entirely on what ITS can support. This must be the first question in the ITS technical session.
📅 What this contract must define once the session happens
- Which mechanism (A, B, or C) is agreed with the ITS team
- The 9 ITS-managed data sets Core requires (full list, field-by-field schema)
- Transform rules (where ITS field names or structures differ from Core's internal schema)
- How Core signals data freshness to its dependent engines (e.g., last-sync timestamp, staleness flag)
- Error handling — what Core does if the sync falls behind or fails entirely during a live miqaat
- Data ownership confirmation: ITS is the source of truth; Core's copy is read-only and never written to by Core or any module
- Migration plan for existing module ITS syncs — which modules are syncing today, and the sequenced plan to deprecate those as modules migrate to Core
Both C-001 and C-005 are on the critical path. Core's Identity Bridge and ITS Sync Engine cannot be fully specified until these sessions are complete. All module teams depending on itsId-based decisions or ITS master data should treat this as their first external dependency. Additionally, the deprecation of existing module-level ITS syncs cannot be planned until the mechanism is confirmed.
5. Type 2 — Core API Contracts
Core API contracts are the primary interface that module teams build against. They are public specifications — a module team reads the contract, builds to it, and calls the endpoint. No registration required. No approval from Core. Just call the API as documented.
✅ Contract framework — confirmed, individual contracts being written
The framework for Core API contracts is confirmed. Each contract follows the standard section structure from Section 3. Individual contracts per engine API are being drafted as part of Core's build process. The first contracts to be written are for the engines that existing modules need earliest: RBAC, Eligibility, and Config Cascade.
✅ Endpoint authorization — read/write split enforced at the gateway
Core API contracts are classified as read or write. This classification is stated explicitly in every contract and enforced at the API Gateway before the request reaches any engine:
- Read contracts — accessible to any module with a valid API key. No per-module endpoint restriction needed.
- Write contracts — restricted to Platform Admin and designated Core processes. A business module API key is rejected at the gateway if it attempts to call a write endpoint. No engine-level write contract is published for business modules.
This is the enforcement model for "who can call what" on the synchronous API side. Module teams only need to read read-endpoint contracts — write contracts are irrelevant to them.
✅ Standard request and response format
All Core API contracts share the same base request and response structure. This is the consistent error response format established in TD1 — regardless of which engine (FastAPI or Node.js) handles the request, the module receives the same shape.
Standard success response:
{
"status": "success",
"requestId": "uuid — for tracing, same as correlationId in events",
"data": { }
}
Standard error response:
{
"status": "error",
"requestId": "uuid",
"error": {
"code": "RBAC_DENIED / ELIGIBILITY_INELIGIBLE / CONFIG_NOT_FOUND / ...",
"message": "human-readable explanation",
"details": { }
}
}
No engine returns a Python traceback, a Node.js stack dump, or a framework-generated error page. Every error from every Core engine looks the same to the calling module.
✅ Core API contracts in scope — one per engine group
| Contract ID | Engine | Status |
|---|---|---|
| C-010 | RBAC / Permission Engine | 📝 Being drafted |
| C-011 | Eligibility Evaluation Engine | 📝 Being drafted |
| C-012 | Config Cascade Engine | 📝 Being drafted |
| C-013 | Miqaat Lifecycle — read endpoints | 📝 Being drafted |
| C-014 | Allocation Engine | 📝 Being drafted |
| C-015 | Zone ↔ Resident Mapping | 📝 Being drafted |
| C-016 | Vendor and Procurement Registry | 📝 Being drafted |
| C-017 | HR Bank — read endpoints | 📝 Being drafted |
| C-018 | Capacity Balance — read endpoints | 📝 Being drafted |
| C-019 | Task Engine — read endpoints | 📝 Being drafted |
Write endpoints (those that mutate Core state) are restricted — only Platform Admin and designated Core processes can call them. Read endpoints are the primary interface for business modules.
✅ Example — RBAC Permission Check Contract (C-010, draft structure)
Contract: C-010
Engine: RBAC / Permission Engine
Version: 1.0.0 (draft)
Auth type: User-initiated (X-Its-Id required)
Endpoint: POST /v1/rbac/check
Request:
{
"itsId": "ITS-1234567",
"action": "approve",
"resource": "volunteer",
"miqaatId": "MQ-1447-KHI"
}
Response (allowed):
{
"status": "success",
"requestId": "uuid",
"data": {
"allowed": true,
"roleMatched": "VMS_Coordinator",
"reasonCode": "ROLE_GRANTS_ACTION"
}
}
Response (denied):
{
"status": "success",
"requestId": "uuid",
"data": {
"allowed": false,
"roleMatched": null,
"reasonCode": "NO_MATCHING_ROLE"
}
}
Error (missing itsId — system-initiated call attempted on user-only endpoint):
{
"status": "error",
"requestId": "uuid",
"error": {
"code": "AUTH_ITS_ID_REQUIRED",
"message": "This endpoint requires a verified itsId. System-initiated calls are not permitted."
}
}
6. Type 3 — Event Contracts
Event contracts define what is published to the event bus and in what shape. Unlike API contracts (where Core is always the publisher/responder), event contracts have two directions.
✅ Core → Module event contracts (Core publishes, modules subscribe)
When a Core engine publishes a decision event, the event type and payload schema are defined in a Core-owned contract. Modules subscribe knowing exactly what they will receive.
| Event Type | Owner Engine | Contract ID |
|---|---|---|
miqaat.phase.changed | Miqaat Lifecycle | C-020 |
allocation.confirmed | Allocation Engine | C-021 |
allocation.waitlisted | Allocation Engine | C-022 |
capacity.imbalance.detected | Capacity Balance Engine | C-023 |
eligibility.status.changed | Eligibility Engine | C-024 |
role.assigned | RBAC Engine | C-025 |
role.revoked | RBAC Engine | C-026 |
🔍 Event publish enforcement — requirements confirmed, mechanism under analysis
Three authorization checks on every event publish are confirmed:
sourceModulemust match the publishing module's identity — prevents impersonationeventTypemust be in the module's registered event type list — prevents publishing under another module's contract- Envelope and payload must conform to the registered schema for that
eventTypeandschemaVersion
Where these checks are enforced — at a Core gateway HTTP endpoint or at the bus level using bus-level ACLs and schema validation — is under analysis. The decision is tied to the messaging technology choice (TD2 Section 6). See TD2 Section 3 for the full options and trade-offs, including the load concern with gateway-mediated publishing during peak events.
✅ Module → Platform event contracts (modules publish, Core engines and other modules subscribe)
When a module publishes events, it registers the event type and schema with the Domain Event Schema Registry. That registration becomes the contract. Other subscribers (Core engines and other modules) build against the registered schema. It also becomes the permitted publish scope enforced by the gateway above.
| Example event type | Publisher module | Primary Core subscriber |
|---|---|---|
registration.confirmed | AMS | Eligibility Engine, Audit Log |
khidmat.assigned | VMS | HR Bank, Audit Log |
capacity.configured | Accommodation | Capacity Balance Engine, Audit Log |
payment.received | Payment module | Eligibility Engine, Audit Log |
pass.printed | Scanning / Kiosk | Audit Log |
volunteer.assigned | VMS | HR Bank, Audit Log |
Module event contracts are the module team's responsibility to maintain. Breaking changes to their event payload (removing or renaming a field) require a new schemaVersion with a deprecation notice to all subscribers before the old schema is retired.
✅ Every event contract inherits the standard envelope
All event contracts build on the standard event envelope from TD2 (Section 4). The payload field is what varies per event type and is what each event contract specifically defines.
7. Contract Lifecycle
✅ Versioning — semantic versioning on all contracts
All contracts use semantic versioning (MAJOR.MINOR.PATCH).
- MAJOR — breaking change. New major version required. Old version continues to be supported through the deprecation period. Module teams must migrate before the deprecation date.
- MINOR — backward-compatible addition (new optional field, new optional endpoint). Existing modules are unaffected.
- PATCH — documentation fix, clarification, no behaviour change.
✅ Breaking changes — what counts
For API contracts: removing a field from a response, renaming a field, changing a field's data type, changing an endpoint path, removing an endpoint, changing error codes that modules may be handling.
For event contracts: removing a field from the payload, renaming a field, changing a field's data type, changing the event type name.
Adding new optional fields is never a breaking change. Adding new endpoints is never a breaking change.
✅ Deprecation process
- New major version contract is published and marked Active
- Old version is marked Deprecated with a stated deprecation date
- Module teams have the full deprecation window to migrate (minimum 8 weeks for API contracts, confirmed by the Core team per contract)
- On the deprecation date, the old version is marked Retired and the endpoint or schema version stops being served
- All steps are logged in the contract's change log and announced to module teams
🔍 Contract format and tooling
The format in which contracts are authored, stored, and published is under analysis. Options include OpenAPI Specification (for API contracts), AsyncAPI (for event contracts), or a simpler structured markdown format. The tooling decision affects whether contract testing can be automated. A decision will be confirmed before the first contracts are finalised.
🔍 Contract storage and discoverability
Where contracts live — a Git repository, a developer portal, Confluence, or a dedicated contract registry — is under analysis. The requirement is: any module team can find and read the latest version of any Core contract without asking the Core team for it. Discoverability and version history are non-negotiable.
8. Pre-Build Contract — System Definition Document
Before a module begins building any integration with Core, it completes the System Definition Document (the seven-question process from BusinessModulesAndPolicies.md, Part 2). This document functions as the pre-build contract between the module team and the Core team. It records:
- Which Core API contracts the module will call
- Which event types it will publish (and therefore register with the Domain Event Schema Registry)
- Which event types it will subscribe to
- Which migration type applies (greenfield / migrate / rewrite)
The Core team reviews and signs off on the System Definition Document before implementation begins. This is not an approval of the module's existence — it is a review to ensure the module is not recreating something Core already provides, and that its planned integrations are consistent with platform policies.
Decision Summary
| Decision | Status | Notes |
|---|---|---|
| Contracts are required for all Core integration points | ✅ Decided | Foundation of the federation model |
| Contracts are public — no approval needed to read or build against them | ✅ Decided | Core does not gate module builds |
| Three contract types: ITS Integration, Core API, Event | ✅ Decided | See Section 2 |
| Standard contract sections apply to all types | ✅ Decided | See Section 3 |
| Standard success and error response format for all API contracts | ✅ Decided | Consistent regardless of engine runtime |
| Semantic versioning on all contracts | ✅ Decided | MAJOR.MINOR.PATCH |
| Breaking change = new major version | ✅ Decided | API Versioning Policy |
| Minimum 8-week deprecation window for API contract versions | ✅ Decided | Module teams have time to migrate |
| Write endpoints restricted to Platform Admin and Core processes | ✅ Decided | Module teams access read endpoints only — enforced at gateway |
| Read/write classification stated explicitly in every contract | ✅ Decided | Module teams only need to read read-endpoint contracts |
| Event publish authorization requirements (sourceModule, eventType, schema) | ✅ Decided | Three checks required on every publish |
| Event publish enforcement mechanism | 🔍 Under Analysis | Core gateway vs direct bus with ACLs — see TD2 Section 3 |
| Event type registration doubles as permitted publish scope | ✅ Decided | Enforced at whichever mechanism is confirmed |
| System Definition Document as pre-build contract | ✅ Decided | Core team reviews before implementation |
| Event contracts inherit standard envelope from TD2 | ✅ Decided | Payload schema is what each event contract defines |
| Module team owns and maintains their event publish contracts | ✅ Decided | Breaking payload changes require new schemaVersion + notice |
| C-001 — ITS SSO Token Contract | 📅 Pending Session | ITS technical session — critical path |
| C-005 — ITS ↔ Core Sync Contract | 📅 Pending Session | ITS technical session — critical path |
| Individual Core API contracts (C-010 to C-019) | 📝 Being drafted | Written as each engine is built |
| Individual Core event contracts (C-020 to C-026) | 📝 Being drafted | Written as each engine is built |
| Contract format and tooling (OpenAPI / AsyncAPI / other) | 🔍 Under Analysis | Impacts contract testing automation |
| Contract storage and discoverability (developer portal / Git / registry) | 🔍 Under Analysis | Must be accessible without asking Core team |