HIP Platform - OpenAPI 3.1 Support Decision
Date: 2026-02-16
Status: Analysis Complete
Version: 1.0
Primary Question: What do we want to support from OAS 3.1?
Executive Summary
Answer: Support OAS 3.1 selectively - implement JSON Schema 2020-12, hold on webhooks pending blocker validation, discourage complex features.
Key Decisions:
- JSON Schema 2020-12: ✅ SUPPORT
- Webhooks: ⏸️ HOLD (pending blocker validation)
- Discriminators & Union Types: ❌ DISCOURAGE
Examples have been included of the sort of thing that can be expressed with JSON schema 2020-12. These are intended only to illustrate what the schema can do and are not a judgement of any API seen at this point. We merely need to be deliberate about what we want to support, and discourage things we do no not.
Feature Decision Matrix
| Feature | OAS 3.1 Capability | Business Value | Decision | Rationale |
|---|---|---|---|---|
| JSON Schema 2020-12 | Full JSON Schema standard | HIGH | ✅ SUPPORT | Better validation, improved tooling interop |
| Webhooks | Native async callbacks | HIGH | ⏸️ HOLD | Complex, depends on blockers (see OAS-3.1-WEBHOOKS.md) |
info.license.identifier | SPDX license identifiers | MEDIUM | ✅ SUPPORT | Metadata only, low effort |
unevaluatedProperties | Advanced schema composition | MEDIUM | ⚠️ OPTIONAL | Use sparingly for complex inheritance |
$ref Siblings | Allow alongside other properties | LOW | ✅ WORKS | Already compatible with 3.0 |
| Improved Discriminators | Clearer polymorphism | LOW | ❌ DISCOURAGE | Use RESTful patterns instead |
| Union Types | type: [T, "null"] patterns | LOW | ❌ DISCOURAGE | Code smell - indicates design issues |
1. JSON Schema 2020-12 (SUPPORT)
Decision: ✅ Support OAS 3.1’s full JSON Schema 2020-12 standard
What’s New: OAS 3.0 used JSON Schema Draft 5 (from 2017). OAS 3.1 upgrades to the full JSON Schema 2020-12 standard, removing OAS-specific keywords like nullable and enabling richer validation capabilities.
Why: Enables better validation capabilities and improved tooling interoperability.
Constraint: With domain APIs we intend to support simple REST aligned APIs (no deep nesting, complex conditionals, or advanced patterns). Some of the features of JSON schema 2020-12, whilst technically correct & can add value for an API producer, reduce the ease for the consumer, and especially for ‘re-usable’ APIs that may not be the right tradeoff.
Encouraged: Simple & Clear
Example 1: Basic types with validation
Pattern matching and min/max constraints are standard JSON Schema features now fully supported in 3.1 (OAS 3.0 had these via Draft 5 subset, but 3.1 enables the full 2020-12 standard).
{
"type": "object",
"required": ["periodKey", "dutyAmount"],
"properties": {
"periodKey": {
"type": "string",
"pattern": "^[0-9]{4}[AB]$"
},
"dutyAmount": {
"type": "number",
"minimum": 0,
"maximum": 999999999.99
}
}
}Example 2: Enums for fixed values
Enums are a standard JSON Schema feature to restrict values to a fixed set. OAS 3.1 enables richer enum validation with the full 2020-12 standard.
{
"complianceStatus": {
"type": "string",
"enum": ["PENDING", "COMPLIANT", "NON_COMPLIANT"]
}
}Why this works:
- ✅ Clear types (no ambiguity)
- ✅ Simple validation rules
- ✅ Easy to code-generate
Discouraged: unevaluatedProperties (Complex Inheritance)
What’s New: OAS 3.1 introduces unevaluatedProperties (from JSON Schema 2020-12), which validates that no unexpected properties are present after evaluating all schema constraints. This is powerful but can lead to overly complex schemas.
Key Issue: When using unevaluatedProperties: false with multiple allOf references, it becomes hard for humans (and code generators) to understand what fields are actually valid/required. The field definitions are scattered across referenced schemas. If this pattern is deemed necessary, consider tooling to generate a combined view of all referenced schemas merged together.
Problem: Using it with multiple allOf schemas scatters field definitions, making them unclear.
❌ DON’T:
{
"unevaluatedProperties": false,
"allOf": [
{ "$ref": "#/components/schemas/BaseSubmission" },
{ "$ref": "#/components/schemas/AuditFields" },
{ "$ref": "#/components/schemas/ComplianceFields" }
]
}✅ DO: Flat structure
{
"type": "object",
"required": ["submissionId", "periodKey", "dutyAmount"],
"properties": {
"submissionId": { "type": "string" },
"periodKey": { "type": "string" },
"dutyAmount": { "type": "number" },
"auditedBy": { "type": ["string", "null"] },
"complianceStatus": { "enum": ["PENDING", "COMPLIANT", "NON_COMPLIANT"] }
}
}Why: All fields visible in one place, clear what’s required.
2. Webhooks (HOLD)
Decision: ⏸️ HOLD pending validation of three critical blockers
Why: Webhooks have high business value (enterprise async pattern 5a) but implementation is complex and depends on factors outside platform team control.
Critical Blockers
| Blocker | Impact | Owner |
|---|---|---|
| HOD backends can publish to event bus | Webhooks require event bus for delivery - if HOD teams cannot publish events, webhooks are not viable | HOD teams |
| Consumer interest >30% | Validates market demand for webhooks vs polling - if interest is low, investment not justified | Consumer survey |
| Platform team capacity | Requires 6-8 weeks for MVP service build plus ongoing maintenance - if unavailable, defer | Platform leadership |
Go/No-Go: Proceed to implementation ONLY if ALL three blockers are cleared. If any single blocker fails, defer webhooks.
See detailed analysis: OAS-3.1-WEBHOOKS.md for complete evaluation and blockers framework.
3. Discriminators & Union Types (DISCOURAGE)
Decision: ❌ Discourage both features
Improved Discriminators (DISCOURAGE)
What’s New: OAS 3.1 improved the discriminator feature to better handle polymorphic requests/responses by using the value of a specific property to determine which schema applies.
Example with discriminator (showing why it’s problematic):
The two variant schemas:
// OriginalVpdReturn schema
{
"type": "object",
"required": ["returnType", "vpdApprovalNumber", "dutyAmount"],
"properties": {
"returnType": { "enum": ["ORIGINAL"] },
"vpdApprovalNumber": { "type": "string" },
"dutyAmount": { "type": "number" }
}
}
// AmendedVpdReturn schema
{
"type": "object",
"required": ["returnType", "vpdApprovalNumber", "originalAcknowledgementReference", "revisedDutyAmount"],
"properties": {
"returnType": { "enum": ["AMENDED"] },
"vpdApprovalNumber": { "type": "string" },
"originalAcknowledgementReference": { "type": "string" },
"revisedDutyAmount": { "type": "number" }
}
}The discriminator schema:
oneOf:
- $ref: '#/components/schemas/OriginalVpdReturn'
- $ref: '#/components/schemas/AmendedVpdReturn'
discriminator:
propertyName: returnType
mapping:
ORIGINAL: '#/components/schemas/OriginalVpdReturn'
AMENDED: '#/components/schemas/AmendedVpdReturn'Consumer requests (must choose which to send):
// Option 1: Original
POST /vpd/submission-returns/v1
{ "returnType": "ORIGINAL", "vpdApprovalNumber": "VPD123456", "dutyAmount": 5000 }
// Option 2: Amended
POST /vpd/submission-returns/v1
{ "returnType": "AMENDED", "vpdApprovalNumber": "VPD123456", "originalAcknowledgementReference": "ACK-123", "revisedDutyAmount": 5500 }Problem: Consumer must choose which variant to send by setting returnType. This adds complexity without clear benefit.
✅ DO: Use HTTP methods instead
// Create original return
POST /vpd/submission-returns/v1
{ "vpdApprovalNumber": "VPD123456", "dutyAmount": 5000 }
// Amend existing return
PUT /vpd/submission-returns/v1/{acknowledgementReference}
{ "vpdApprovalNumber": "VPD123456", "dutyAmount": 5500 }Why: HTTP method (POST vs PUT) is the discriminator. No ambiguous fields in payload. Standard REST.
Union Types (DISCOURAGE)
What’s New: OAS 3.1 allows JSON Schema’s array syntax for type unions (e.g., type: [string, number]), replacing OAS 3.0’s workarounds with oneOf. (Note: OAS 3.0 also had nullable: true for nullable fields, which 3.1 replaces with type: ["string", "null"])
Problem: type: [string, number, integer] allows multiple incompatible types, indicating unclear design.
❌ DON’T:
"dutyReference": { "type": [string, number, integer] }✅ DO: Pick one type or use separate fields
Option 1 - Single type: Capture as a string and enforce format with a pattern. This handles alphanumeric references and is flexible enough for future changes without breaking consumers.
"dutyApprovalRef": { "type": "string", "pattern": "^[A-Z0-9]+$" }Option 2 - Separate fields:
"dutyReferenceAlpha": { "type": "string" },
"dutyAmountPence": { "type": "integer" }Option 3 - Nullable fields are GOOD (explicitly marked):
Use string "null" (not unquoted null) in the type array. This makes optionality explicit and clear.
"approverComments": { "type": ["string", "null"] },
"rejectionReason": { "type": ["string", "null"], "enum": ["INVALID_DUTY", null] }Note: type: [T, "null"] is encouraged for optional fields. The problem is incompatible types like [string, number].
4. Features Already Working
$ref Siblings
Status: Works in both 3.0 and 3.1 - no action needed. Keep using allOf pattern (reliable in both versions).
info.license.identifier
Status: Add SPDX license identifiers to platform spec template (metadata only, low effort).
Recommendations
Immediate (enable JSON Schema 2020-12):
- Integrate OAS 3.1-compatible validator library
- Update Integration Hub to accept OAS 3.1 specs
- Publish this decision to producers
Webhooks - Before any investment:
- Survey consumer community (>30% threshold)
- Engage HOD teams (validate event bus publishing capability)
- Assess platform team capacity (6-8 weeks available?)
If all webhook blockers cleared → Proceed with implementation If any blocker fails → Document learnings and defer
FAQ
Q: Do I need to migrate from OAS 3.0 to OAS 3.1? A: No. Both versions are fully supported. Choose based on your API needs.
Q: Will my OAS 3.0 API work if other APIs use OAS 3.1? A: Yes, completely. From a consumer perspective, OAS version is transparent at runtime.
Q: What about webhooks - when are they available? A: Under evaluation. See OAS-3.1-WEBHOOKS.md for blocker framework and timeline.
Related Documents
- OAS-3.1-WEBHOOKS.md - Complete webhook decision analysis with blocker framework