Review: Proposed Domain API Specifications
Reviewer: AI Agent
Date: 2026-02-13
Validation Tool: vacuum v0.23.8
Specs Reviewed:
- VOD2.0 Vaping Submission Returns API (
VOD2.0 OAS (1API MEP)/Vaping-Submission-return-(1API 1EP MM)/Get-Submit-vpd-return-data.yaml)- VOD2.0 Vaping Payments API (
VOD2.0 OAS (1API MEP)/Vaping-payment-(1API MEP MM)/financial-Direct-debit-data.yaml)- Individual Combined Details API (
Individual OAS (1API MEP)/Individual-combined-details.yaml)Context: Producer view of domain API contracts. Platform features (sparse fieldsets, resource traversal) may be added by platform layer. Reviewed for OpenAPI correctness, REST best practices, internal consistency, and fragment alignment.
Appendices:
- Appendix A: Detailed Validation Results (vacuum lint)
- Appendix B: Fragment Consistency Analysis
Executive Summary
Three producer domain API specifications have been provided for review. These define the core contracts that domain APIs will expose to consumers, with the understanding that additional platform features may be layered on top.
Review Focus:
- ✅ OpenAPI technical correctness
- ✅ REST API design best practices
- ✅ Internal consistency within specs
- ✅ HMRC platform pattern usage
- ✅ Content type handling
Key Findings:
- ❌ Vaping Payments spec has broken
$refpaths (won’t validate) - ✅ Fragment consistency verified - VOD2.0 specs internally consistent; Individual differs cosmetically (see Appendix B)
- ⚠️ Path structure conventions vary between specs
- ⚠️ API granularity considerations (Individual API scope, Payments API mixed concerns)
- ✅ Strong REST practices across all three specs
- ✅ Good HMRC platform standards alignment
Note: Server URLs in specs are understood to be placeholder/proposal values. Backend orchestration and implementation details are out of scope for this review.
🚨 Critical Issues
1. OpenAPI Validation Failures - Payments API
Issue: Vaping Payments API spec contains broken $ref paths that will cause validation failures.
Severity: Critical - spec will not validate with standard OpenAPI tooling
Location: Lines 99-113 and multiple other locations
Current (Broken):
- $ref: '#components/parameters/AddAccruingInterestDetails'
- $ref: '#components/parameters/AddPostedInterestDetails'
- $ref: '#components/parameters/AddLockInformation'
- $ref: '#components/parameters/SearchItem'
- $ref: '#components/parameters/SearchType'Correct Syntax:
- $ref: '#/components/parameters/AddAccruingInterestDetails'
- $ref: '#/components/parameters/AddPostedInterestDetails'
- $ref: '#/components/parameters/AddLockInformation'
- $ref: '#/components/parameters/SearchItem'
- $ref: '#/components/parameters/SearchType'Missing: Forward slash / before components
Impact:
- Spec validation will fail in OpenAPI validators (Spectral, Swagger Editor, Redocly)
- Code generators will fail or produce incorrect output
- API documentation tools won’t render properly
- CI/CD pipelines with OpenAPI validation will reject the spec
Additional Issue (Lines 534-535):
FinancialDetails:
$ref: './fragments/domain/payments/totalisation.yaml#/components/schemas/Totalisation'
Totalisation:
$ref: './fragments/domain/payments/financialdetails.yaml#/components/schemas/FinancialDetails'Schema references appear to point to each other’s files. This may be intentional (if schemas are defined in those fragment files) but should be verified for correctness.
Recommendation:
☑ Run spec through OpenAPI validator (e.g., spectral lint, swagger-cli validate)
☑ Fix all broken $ref paths (add missing / before components)
☑ Verify schema file references point to correct fragment files
☑ Add OpenAPI validation to CI/CD pipeline for future specs
Your Notes:
2. Fragment Consistency - Verified with Findings
Status: ✅ Analysis Complete (see Appendix B for detailed comparison)
Key Findings:
VOD2.0 Specs (Submission Returns + Payments):
- ✅ 100% identical fragments across all 11 common/security files
- Indicates consistent authoring process and template
- Suggests same team or well-coordinated standards
Individual API:
- ⚠️ Systematically differs from VOD2.0 in 9 of 11 fragments
- ✅ 2 fragments fully consistent (error404.yaml, error503.yaml)
- Pattern: Individual adds
titlefields and uses prefixed naming (e.g.,400_ErrorResponsevsError Response)
Nature of Differences:
-
Cosmetic (8 fragments): Individual adds optional
titlefieldscorrelationId.yaml: Addstitle: CorrelationId- Error fragments: Adds prefixed titles (e.g.,
title: 400_ErrorResponse) govUkOriginatorId.yaml,hip-origin.yaml,identifierParameter.yaml: Similar title additions
-
Potentially Functional (1 fragment): OAuth configuration
- Individual:
tokenUrl: https://example.com - VOD2.0:
tokenUrl: https://www.example.com/ - Needs verification: Are these placeholder URLs or should they match?
- Individual:
Impact Assessment:
- Functional: ✅ LOW - All fragments have identical structure and validation rules (except OAuth URL)
- Consumer: ✅ LOW - Error responses work identically; only documentation titles vary
- Maintenance: ⚠️ MEDIUM-HIGH - Three versions of “platform standard” creates update burden
Recommendation:
☑ Verified - see detailed analysis in Appendix B
☑ Standardize on VOD2.0 fragment style (recommended: less migration work)
☑ Verify OAuth token URL should be consistent
☑ Plan centralized fragment repository for future consistency
☑ Establish fragment governance and versioning policy
Your Notes:
3. Individual API - Invalid Timestamp
Issue: Integration catalogue metadata contains invalid timestamp.
Severity: Medium - will cause issues with metadata validation/parsing
Location: Line 8
Current:
x-integration-catalogue:
reviewed-date: 2025-08-05T11:50:92ZIssue: 92 seconds is invalid (valid range: 00-59 per ISO 8601)
Should be:
reviewed-date: 2025-08-05T11:50:09Z(or appropriate corrected timestamp)
Recommendation:
☑ Fix timestamp to valid ISO 8601 format
☑ Add timestamp format validation to spec linting/CI
Your Notes:
⚠️ Design Considerations
4. Path Structure Conventions
Issue: Different path structure conventions used across the three specs, particularly regarding version placement.
Comparison:
| Spec | Path Structure | Version Placement |
|---|---|---|
| VOD2.0 Submission Returns | /duty/vpd/submission-returns/{version} | Dynamic path parameter |
| VOD2.0 Payments | /payments/liabilities/financial-data/{version}/{idNumber}/{idType} | Middle of path |
| Individual | /individual/{identifier}/... | No version in path |
VOD2.0 Submission Returns:
/duty/vpd/submission-returns/{version}:
parameters:
- name: version
schema:
type: string
pattern: '^v[0-9]+(\.[0-9]+)*$'Considerations:
- Dynamic
{version}parameter allows flexible versioning (v1,v1.1,v1.1.1) - More complex for clients (must construct version string)
- Less discoverable (version not visible in base path)
Industry Patterns:
- Literal version:
/v1/duty/vpd/submission-returns(Stripe, GitHub, many REST APIs) - Domain prefix:
/duty/vpd/v1/submission-returns(domain-scoped versioning) - Path parameter:
/duty/vpd/submission-returns/{version}(as in spec)
VOD2.0 Payments - Version in Middle of Path:
/payments/liabilities/financial-data/{version}/{idNumber}/{idType}:Considerations:
- Version between resource path and identifiers is unusual
- Three path parameters make URL construction complex
{idNumber}before{idType}is counterintuitive (typically type → value)
More Conventional Alternative:
/payments/v1/liabilities/financial-data/{idType}/{idNumber}Individual API - No Version in Path:
/individual/{identifier}/comms-preferences
/individual/{identifier}/contact-detailsConsiderations:
- No version in path may indicate version in URL base or Accept header
- Less clear how versioning will be handled
- May rely on platform layer to add versioning
Questions for Clarification:
- What is the intended versioning strategy across domain APIs?
- Should all domain APIs use consistent version placement?
- Will platform layer standardize versioning approach?
- Is semantic versioning (v1.1.2) required or just major versions (v1, v2)?
Recommendation:
☐ Standardize on one versioning approach across domain APIs
☐ Document versioning strategy for domain API producers
☐ If dynamic version is kept, document allowed version format
☐ Consider usability: literal versions are simpler for clients
Your Notes:
5. Path Parameter Ordering - Payments API
Issue: Parameter ordering in Payments API paths follows pattern {idNumber}/{idType} which is counterintuitive.
Location: Multiple paths in Payments API
Current:
/payments/liabilities/financial-data/{version}/{idNumber}/{FinancialIdType}Industry Convention: Type parameter before value parameter
- More intuitive: know the type, then provide the value
- Better documentation ordering
- Clearer API structure
Examples of Type→Value Pattern:
/customers/{customerId}/orders/{orderId} # Type implicit in path
/identifiers/{idType}/{idValue} # Type explicit
/payments/liabilities/{idType}/{idNumber} # Type before numberCurrent Pattern ({idNumber}/{idType}):
- Client must know the number before selecting the type
- Less intuitive in documentation
- Unusual compared to common REST patterns
Recommendation:
☐ Reorder to {idType}/{idNumber} pattern
☐ Keep current ordering with documented rationale
☐ Verify consistency with other HMRC APIs
Your Notes:
6. Individual API - Broad Scope & Granularity
Issue: Individual API combines multiple distinct concerns into a single service with 11 different endpoints.
Current Structure:
Endpoints:
Communication Preferences:
- GET /individual/{id}/comms-preferences
- PUT /individual/{id}/comms-preferences
Contact Details:
- GET /individual/{id}/contact-details
- PUT /individual/{id}/contact-details/email
- PUT /individual/{id}/contact-details/contact-numbers
Identifiers:
- GET /individual/{id}/ids
Indicators:
- GET /individual/{id}/indicators
- PUT /individual/{id}/indicators
Key Life Dates:
- GET /individual/{id}/key-life-dates
- PUT /individual/{id}/key-life-dates
Marriage:
- GET /individual/{id}/marriage-cp
- PUT /individual/{id}/marriage-cp
Name:
- GET /individual/{id}/name-details
- PUT /individual/{id}/name-details/real
- PUT /individual/{id}/name-details/known-as
Sex:
- GET /individual/{id}/sex
Summary:
- GET /individual/{id}/summary
Search:
- POST /individual/traceScope Analysis:
| Concern Area | Endpoints | Change Frequency | Security Sensitivity |
|---|---|---|---|
| Demographics (name, sex, DOB, marriage) | 7 | Low | Medium-High (PII) |
| Contact (email, phone, preferences) | 4 | Medium | Medium (PII) |
| Identifiers (NINO, TRN, etc.) | 1 | Low | High (sensitive IDs) |
| Search/Trace | 1 | N/A | High (search PII) |
| Summary | 1 | Low | Varies |
Considerations:
Pros of Combined API:
- ✅ Single integration point for consumers
- ✅ Cohesive “individual” domain concept
- ✅ Simpler authentication (one set of scopes)
- ✅ Reduced operational overhead (one service to deploy/monitor)
Cons of Combined API:
- ⚠️ Changes to trace logic require versioning entire API
- ⚠️ Can’t evolve contact management independently from demographics
- ⚠️ All consumers affected by any change to any endpoint
- ⚠️ Harder to apply fine-grained security (field-level access)
- ⚠️ Larger service with multiple responsibilities
- ⚠️ Ownership complexity (who owns demographics vs. contact vs. search?)
Alternative Approach (for consideration):
Individual Demographics API:
- /individuals/{id}/demographics (name, sex, DOB, marriage)
- PUT operations for specific fields
Individual Contact API:
- /individuals/{id}/contact (email, phone, addresses)
- /individuals/{id}/contact/preferences
Individual Identifiers API:
- /individuals/{id}/identifiers (NINO, TRN, etc.)
Individual Search API:
- POST /individuals/search
- POST /individuals/trace
Questions for Clarification:
- Is this broad scope intentional (e.g., consumer needs, organizational structure)?
- Will all endpoints share the same lifecycle and versioning?
- What is the ownership model for this API?
- Are fine-grained OAuth scopes planned (e.g.,
read:individual-demographics,read:individual-contact)?
Emerging Pattern Consideration: A domain APIs approach might suggests bounded contexts with focused responsibilities. Consider whether decomposition would provide:
- Better alignment with domain boundaries
- Independent evolution of different concern areas
- Clearer ownership models
Recommendation:
☐ Keep combined API (document rationale and ownership)
☐ Consider decomposition into focused domain APIs
☐ Use fine-grained OAuth scopes to mitigate broad access concerns
☐ Document governance approach for multi-concern API
Your Notes:
7. Vaping Payments API - Mixed Business Objects
Issue: Single API combines two distinct business objects (Liabilities and Banking Services) with different characteristics.
Current Structure:
x-domain: Payments
x-business-objects:
- Liabilities # Financial data (read-only queries)
- Banking Services # Direct debit accounts (full CRUD)
Paths:
/payments/liabilities/financial-data/{version}/{idNumber}/{idType}:
GET: Query financial details, interest, penalties
/payments/banking-services/account-data/{version}:
POST: Create account + contact
/banking/accounts/{version}/{idType}/{idValue}:
GET: Retrieve account (with optional contact expansion)
PATCH: Update account details
DELETE: Delete account (with optional cascade)Characteristics Comparison:
| Aspect | Liabilities | Banking Services |
|---|---|---|
| Operations | GET only (query) | POST, GET, PATCH, DELETE (full CRUD) |
| Mutability | Read-only, calculated | Fully mutable, user-managed |
| Lifecycle | Derived from transactions | Active CRUD lifecycle |
| Security | Read-heavy, broad access | Write-sensitive, restricted access |
| Base Path | /payments/liabilities/... | /banking/accounts/... |
Considerations:
Pros of Combined API:
- ✅ Related domain (payments/banking for vaping duty)
- ✅ May share consumer applications
- ✅ Single API for vaping payment concerns
Cons of Combined API:
- ⚠️ Very different interaction patterns (read-only vs. full CRUD)
- ⚠️ Different evolution needs (query optimization vs. transaction reliability)
- ⚠️ Path inconsistency (
/payments/vs/banking/) - ⚠️ Different SLA requirements (query speed vs. transactional integrity)
- ⚠️ Mixed concerns in versioning (changing liability queries affects banking operations)
Path Structure Inconsistency:
- Liabilities use
/payments/liabilities/... - Banking uses
/banking/accounts/...(different base) - Suggests these may be conceptually separate concerns
Questions for Clarification:
- Do Liabilities and Banking Services share backend systems?
- Do they share consumer applications?
- Is there a business reason to version them together?
- Should the different base paths (
/payments/vs/banking/) be standardized?
Alternative Approach (for consideration):
Vaping Liabilities API:
- GET /vaping/liabilities/v1/financial-data/{idType}/{idNumber}
- GET /vaping/liabilities/v1/totalisation
Vaping Direct Debit API:
- POST /vaping/direct-debit/v1/accounts
- GET /vaping/direct-debit/v1/accounts/{accountId}
- PATCH /vaping/direct-debit/v1/accounts/{accountId}
- DELETE /vaping/direct-debit/v1/accounts/{accountId}
Recommendation:
☐ Keep combined API (document rationale for combining read-only + CRUD)
☐ Split into separate APIs (Liabilities + Direct Debit)
☐ Standardize base paths if keeping combined
☐ Use OpenAPI tags to clearly separate concerns
Your Notes:
8. Idempotency Key - Optional vs Required
Issue: VOD2.0 Submission Returns spec defines X-Idempotency-Key as optional for POST operations that create financial submissions.
Location: Lines 195-202
Current:
idempotencyKey:
name: X-Idempotency-Key
in: header
required: false
description: Unique key to ensure idempotent submission. Reuse the same key to avoid duplicate processing.Context: POST operation creates a VPD submission return (financial/compliance record)
Industry Practice:
- Stripe API: Idempotency-Key recommended for all POST/DELETE operations
- PayPal API: Idempotency-Key required for payment/financial operations
- Adyen API: Idempotency-Key required for transaction endpoints
- HMRC MTD APIs: Idempotency patterns used for tax submissions
Risk of Optional Idempotency Key:
- Network timeout → client retries → duplicate submission
- No safe retry mechanism for failed requests
- Financial/compliance implications of duplicate duty submissions
- Difficulty troubleshooting “did my submission succeed?”
Benefits of Required Idempotency Key:
- ✅ Safe retries (client can replay with same key)
- ✅ Prevents duplicate submissions
- ✅ Clear semantics for “this is the same request”
- ✅ Enables stateful backend validation (same key + different payload = error)
Considerations:
- Making it required changes client integration requirements
- Backend must implement idempotency key validation and storage
- Need to define key format, expiration, collision handling
Questions for Clarification:
- Is backend implementing idempotency key validation?
- What happens if client retries POST without idempotency key?
- Are there alternative idempotency mechanisms (e.g., content hash)?
- What is the guidance for clients on retry behavior?
Recommendation:
☐ Make X-Idempotency-Key required for POST operations
☐ Keep optional but document strong recommendation + retry guidance
☐ Verify backend idempotency implementation before finalizing
☐ Document idempotency key format, expiration, behavior
Your Notes:
9. Content Type Handling Consistency
Issue: Specs should clearly document expected content types for requests and responses.
Current State:
VOD2.0 Submission Returns:
requestBody:
content:
application/json:
schema: ...
responses:
'201':
content:
application/json:
schema: ...✅ Clear JSON request/response
VOD2.0 Payments:
requestBody:
content:
application/json: ...
# Also uses:
requestBody:
content:
application/merge-patch+json: # For PATCH operation
schema: ...✅ Clear content types, appropriate use of application/merge-patch+json for PATCH
Individual API:
responses:
'200':
content:
application/json;charset=UTF-8:
schema: ...✅ Clear JSON with explicit charset
Observations:
- All specs consistently use JSON for request/response bodies
- Payments API appropriately uses
application/merge-patch+jsonfor PATCH (RFC 7396) - Individual API explicitly specifies charset (redundant but harmless for UTF-8)
- No XML, form-data, or other content types used
Best Practice Notes:
application/jsonimplies UTF-8 by default (RFC 8259), socharset=UTF-8is optionalapplication/merge-patch+jsonis correct for JSON Merge Patch operations- All specs should document Accept header expectations if strict content negotiation is required
Recommendation:
☐ No changes needed - content types are appropriate
☐ Document Accept header behavior if content negotiation is strict
☐ Consider removing redundant charset=UTF-8 for consistency
Your Notes:
✅ Positive Observations
10. Strong REST API Design
All three specs demonstrate solid REST practices:
✅ Proper HTTP Verbs:
- GET for retrieval (idempotent, safe)
- POST for creation (non-idempotent without idempotency key)
- PUT for upsert/replace (idempotent)
- PATCH for partial updates (with appropriate merge-patch content type)
- DELETE for removal (idempotent)
✅ Appropriate Status Codes:
200: Successful retrieval
201: Resource created
204: Successful operation with no response body
400: Bad request (malformed syntax)
401: Unauthorized (missing/invalid auth)
403: Forbidden (insufficient permissions)
404: Not found
409: Conflict (idempotency, optimistic locking)
422: Unprocessable entity (semantic validation errors)
429: Too many requests (rate limiting)
500: Internal server error
503: Service unavailable✅ RESTful Resource Design:
- Noun-based paths (e.g.,
/submission-returns,/accounts,/individuals) - Hierarchical resource relationships (e.g.,
/individual/{id}/contact-details) - Appropriate use of path parameters for resource identifiers
✅ Content Negotiation:
- Proper
Content-Typeheaders - JSON request/response bodies
application/merge-patch+jsonfor PATCH operations
✅ Optimistic Locking:
headers:
ETag:
$ref: '#/components/headers/optimisticLock'Good pattern for preventing lost updates in concurrent scenarios.
Your Notes:
11. Comprehensive Error Handling
All specs provide well-structured error definitions:
✅ Shared Error Fragments:
- References to common error definitions via
$ref - Consistent error structure across APIs
- Reduces duplication
✅ Appropriate Error Codes:
'400':
$ref: './fragments/common/error400.yaml#/components/responses/BadRequest_400'
'403':
$ref: './fragments/common/error403.yaml#/components/responses/Forbidden_403'
'404':
$ref: './fragments/common/error404.yaml#/components/responses/NotFound_404'
'422':
$ref: './fragments/common/error422.yaml#/components/responses/UnprocessableEntity_422'✅ Error Response Coverage:
- Client errors (4xx): Malformed requests, validation, not found, conflicts
- Server errors (5xx): Internal errors, service unavailable
- Rate limiting (429): Too many requests
✅ Structured Error Responses (based on fragment naming):
- Consistent schema across error types
- Clear error codes and messages (assumed from fragment structure)
Assumption: Error fragments follow RFC 7807 Problem Details or similar structured format. Should verify fragments contain:
- Error code/type
- Human-readable message
- Optional details/field-level errors
Your Notes:
12. HMRC Platform Standards Alignment
Evidence of established platform patterns:
✅ Correlation ID Support:
parameters:
correlationId:
$ref: './fragments/common/correlationId.yaml#/components/parameters/correlationId'
headers:
correlationId:
$ref: './fragments/common/correlationId.yaml#/components/headers/correlationId'- End-to-end request tracing
- Debugging and observability
- Standard across HMRC APIs
✅ Integration Catalogue Metadata:
x-integration-catalogue:
reviewed-date: 2025-12-12T09:49:36.642678693Z
short-description: VPD Submission Returns
status: BETA
platform: HIP- Cataloging and discovery
- API lifecycle status
- Platform identification
✅ OAuth2 Security:
security:
- oAuth2: []
securitySchemes:
oAuth2:
$ref: './fragments/security/oauth2.yaml#/components/securitySchemes/oAuth2'- Standard authentication pattern
- Scope-based authorization
- Consistent across platform
✅ GOV.UK Patterns:
govUkOriginatorId:
$ref: './fragments/common/govUkOriginatorId.yaml#/components/parameters/govUkOriginatorId'- GOV.UK service identification
- Cross-government integration
✅ Optimistic Locking:
optimisticLock:
$ref: './fragments/domain/optimisticLock.yaml#/components/schemas/optimisticLock'- ETag-based concurrency control
- Prevents lost updates
Your Notes:
13. Advanced API Patterns
Specs demonstrate sophisticated API design patterns:
✅ Resource Expansion (Payments API):
parameters:
- name: expand
in: query
schema:
type: array
items:
enum: [contact]
description: Optional expansions. Example: ?expand=contact- Reduces over-fetching
- Client-controlled response size
- Similar to Stripe API pattern
✅ Cascade Delete Control (Payments API):
parameters:
- name: cascade
schema:
type: boolean
default: false
description: When true, deletes account and subordinate resources- Explicit control over delete behavior
- Safe default (cascade=false)
- Clear semantics
✅ Flexible Search Patterns (Individual API):
TraceRequestParameters:
oneOf:
- SurnamePostCodeObject
- FirstNameDateOfBirthPostCodeObject
- SurnameDateOfBirthObject- Multiple search strategies
- Schema-enforced validation
- Clear API contract
✅ Optimistic Locking (All APIs):
- ETag headers for concurrent update protection
- Version-aware updates
- Prevents lost update problem
Your Notes:
🔧 Specific Technical Issues
14. Individual API - Numeric Domain Metadata
Issue: Domain metadata uses numeric IDs instead of semantic names.
Location: Lines 16-17
Current:
x-integration-catalogue:
backends: [PAYE]
domain: 8
sub-domain: 8.17Consideration:
- Numeric domain IDs (
8,8.17) aren’t self-documenting - Requires separate registry/lookup to understand meaning
- Harder for developers to understand domain organization
Comparison to Other Specs:
VOD2.0 specs use semantic metadata:
x-domain: Duty
x-business-object: VPD
x-domain: Payments
x-business-objects:
- Liabilities
- Banking ServicesQuestions:
- Is there a domain registry mapping
8to a semantic name? - Is numeric domain ID required by integration catalogue?
- Should producer specs include semantic names for clarity?
Recommendation:
☐ Add semantic domain name alongside numeric ID (e.g., domain: 8 # Individual)
☐ Document domain registry mapping
☐ Use semantic names if numeric IDs not required
Your Notes:
15. Response Schema Circular Reference - Payments API
Issue: Potential circular reference or naming confusion in schema definitions.
Location: Lines 534-535
Current:
schemas:
FinancialDetails:
$ref: './fragments/domain/payments/totalisation.yaml#/components/schemas/Totalisation'
Totalisation:
$ref: './fragments/domain/payments/financialdetails.yaml#/components/schemas/FinancialDetails'Analysis:
FinancialDetailsschema referencesTotalisationintotalisation.yamlfileTotalisationschema referencesFinancialDetailsinfinancialdetails.yamlfile- This could be correct if:
- The fragment files define the actual schemas
- The main spec is just aliasing them
- This could be incorrect if:
- The files are swapped (wrong file names)
- There’s a circular dependency
Need to Verify:
- Check
fragments/domain/payments/totalisation.yaml- does it defineTotalisationschema? - Check
fragments/domain/payments/financialdetails.yaml- does it defineFinancialDetailsschema? - Ensure no circular dependencies between schemas
Recommendation:
☑ Verify fragment files contain correct schema definitions
☑ Check for circular references in schema relationships
☐ Fix if file references are swapped
☐ Add comment if intentional aliasing pattern
Your Notes:
16. Individual API - Trace Parameter Pattern
Issue: Complex oneOf discriminator pattern for trace search parameters.
Location: Lines 1069-1110
Current:
TraceRequestParameters:
type: object
oneOf:
- $ref: '#/components/schemas/SurnamePostCodeObject'
- $ref: '#/components/schemas/FirstNameDateOfBirthPostCodeObject'
- $ref: '#/components/schemas/SurnameDateOfBirthObject'
# Where each object defines a specific search strategy:
SurnamePostCodeObject:
properties:
surnamePostCode:
properties:
surname: ...
addressPostCode: ...
FirstNameDateOfBirthPostCodeObject:
properties:
firstNameDateOfBirthPostCode:
properties:
firstForename: ...
dateOfBirth: ...
addressPostcode: ...
SurnameDateOfBirthObject:
properties:
surnameDateofBirth:
properties:
surname: ...
dateOfBirth: ...Pattern Analysis:
Pros:
- ✅ Schema-enforced validation (only one strategy allowed)
- ✅ Clear, explicit search patterns
- ✅ Self-documenting API contract
- ✅ Prevents invalid parameter combinations
Cons:
- ⚠️ More complex for clients to implement
- ⚠️ Error messages may be unclear (“must match one of…“)
- ⚠️ Code generation may produce complex types
- ⚠️ Requires clients to understand all three patterns upfront
Alternative Pattern (simpler but less strict):
TraceRequest:
properties:
surname: { type: string }
postcode: { type: string }
firstName: { type: string }
dateOfBirth: { type: string, format: date }
description: |
Provide one of the following combinations:
- surname + postcode
- firstName + dateOfBirth + postcode
- surname + dateOfBirth(Validation logic enforced by server, not schema)
Consideration: oneOf is a strong validation pattern if search strategies are mutually exclusive business requirements. The current design enforces this at the schema level.
Recommendation:
☐ Keep oneOf pattern (strong validation for discrete search strategies)
☐ Add clear examples for each search strategy in spec
☐ Document error handling for invalid combinations
☐ Test client code generation produces usable types
Your Notes:
17. Payments API - Expand Parameter Design
Issue: Resource expansion pattern using query parameter.
Location: Lines 254-268
Current:
get:
summary: Get Account (optionally expanded with contact details)
parameters:
- name: expand
in: query
schema:
type: array
items:
type: string
enum: [contact]
uniqueItems: true
description: Optional expansions. Supported: contact. Example: ?expand=contactPattern Analysis:
Pros:
- ✅ Reduces over-fetching (contact details only when needed)
- ✅ Client controls response size
- ✅ Backward compatible (expand is optional)
- ✅ Industry-standard pattern (Stripe, Shopify, etc.)
Cons:
- ⚠️ Array parameter syntax varies across clients (
?expand=contactvs?expand[]=contact) - ⚠️ Response schema varies based on query parameter (harder to type)
- ⚠️ Documentation must show both response variants
Implementation Notes:
- OpenAPI spec shows
style: form, explode: truefor array handling - Single item:
?expand=contact - Multiple items (future):
?expand=contact&expand=addresses - Response should include
contactobject only when expanded
Response Schema Consideration:
AccountComposite:
properties:
account:
$ref: '#/components/schemas/AccountDetails'
contact:
$ref: '#/components/schemas/ContactDetails'
required: [account] # contact is conditionalThis correctly models contact as optional in response.
Platform Feature Note: Platform layer may add sparse fieldsets in addition to expand:
?expand=contact&fields[contact]=email
(Get contact details but only the email field)
Recommendation:
☐ Keep expand parameter (good pattern)
☐ Document expand + fields interaction if platform adds sparse fieldsets
☐ Provide examples showing expanded vs. non-expanded responses
☐ Ensure response schema correctly models conditional properties
Your Notes:
18. Payments API - Cascade Delete Considerations
Issue: Cascade delete pattern needs clear transactional semantics.
Location: Lines 380-445
Current:
delete:
summary: Delete Account (optional cascade to related resources)
parameters:
- name: cascade
in: query
schema:
type: boolean
default: false
description: |
When true, deletes account and subordinate resources (e.g., contact details).
When false, deletes only the account.Pattern Analysis:
Pros:
- ✅ Explicit control over delete scope
- ✅ Safe default (
cascade=false) - ✅ Self-documenting behavior
- ✅ Idempotent delete (documented in spec)
Questions Needing Answers (for implementation):
- Is delete transactional (all-or-nothing)?
- What happens if account deletes but contact delete fails?
- How are partial failures communicated to client?
- Is delete synchronous or asynchronous?
- What error code for partial failure (500? 207 Multi-Status?)?
Security Consideration (from spec):
cascade=truerequires bothaccounts.deleteandcontact.deletescopes.
Note: OpenAPI can’t express conditional security. This must be enforced at runtime.
Alternative Patterns (for consideration):
-
Separate endpoints:
DELETE /accounts/{id}(account only)DELETE /account-profiles/{id}(account + contacts always)
-
Async delete (if complex):
- Return
202 Acceptedwith job ID - Poll for completion
- Better for cascading deletes with many resources
- Return
Recommendation:
☐ Keep cascade parameter with clear transactional guarantees
☐ Document partial failure handling in implementation notes
☐ Ensure runtime enforces conditional scope requirements
☐ Consider async delete if cascade is complex/slow
Your Notes:
📋 Questions for Specification Authors
OpenAPI Technical
- Q1: Payments API - Verify
$refpaths are correct (missing/beforecomponents) - Q2: Payments API - Confirm schema files (financialdetails.yaml vs totalisation.yaml) contain correct definitions
- Q3: Individual API - What is correct timestamp for
reviewed-datefield?
API Design
- Q4: What is the intended versioning strategy? (literal paths vs. path parameters)
- Q5: Should all domain APIs use consistent version placement?
- Q6: Payments API - Should path parameters be reordered to
{idType}/{idNumber}? - Q7: Should idempotency key be required for financial submission operations?
Fragment Strategy
- Q8: Are common fragments (errors, correlationId, security) intentionally identical across specs?
- Answer: VOD2.0 specs have identical fragments. Individual differs in title fields (cosmetic). See Appendix B.
- Q9: Should fragments be consolidated to prevent drift?
- Recommendation: Yes - standardize on VOD2.0 style and centralize in future
- Q10: What is the governance model for updating shared fragments?
- Needs decision: Fragment ownership, approval process, versioning strategy
API Scope
- Q11: Individual API - Is broad scope (11 endpoints, multiple concerns) intentional?
- Q12: Individual API - What is the ownership model for this API?
- Q13: Payments API - Is there a rationale for combining Liabilities (read-only) with Banking Services (CRUD)?
- Q14: Should base paths be consistent within an API (
/payments/vs/banking/)?
Metadata
- Q15: Individual API - What do domain
8and sub-domain8.17represent? - Q16: Should semantic domain names be added for clarity?
Platform Integration
- Q17: Will platform layer add sparse fieldsets to these specs?
- Q18: How will these specs integrate with platform features (resource traversal, filtering)?
- Q19: Are there consumers waiting for these APIs?
🎯 Recommended Actions
Immediate (Before Finalization)
1. Fix Critical Validation Issues
- Payments API: Fix all broken
$refpaths (#components→#/components) - Payments API: Verify schema file references (financialdetails.yaml ↔ totalisation.yaml)
- Individual API: Fix invalid timestamp (92 seconds → valid value)
- All Specs: Run through OpenAPI validator (Spectral, Redocly, or Swagger Editor)
2. Fragment Consistency (COMPLETED ✅)
- Audited all common fragments across three specs
- Flagged divergence: Individual differs from VOD2.0 in 9 of 11 fragments (cosmetic title fields)
- NEW: Verify OAuth token URL difference is intentional
- NEW: Choose canonical fragment standard (VOD2.0 style recommended)
- Migrate Individual API to chosen standard
- Document fragment style guide and governance
3. Standardize Conventions
- Decide on versioning approach (literal vs. parameter)
- Standardize path parameter ordering (type → value)
- Align metadata approach (numeric vs. semantic domain IDs)
- Document API design conventions for future specs
Short-Term (Before Implementation)
4. Clarify API Scope & Boundaries
- Document rationale for Individual API broad scope (or plan decomposition)
- Document rationale for combining Liabilities + Banking in Payments API
- Define ownership models for multi-concern APIs
- Consider OAuth scope strategy for fine-grained access
5. Define Platform Integration
- Clarify relationship between producer specs and platform features
- Document how platform will add sparse fieldsets, resource traversal
- Define versioning strategy for producer vs. platform specs
- Establish fragment governance model
6. Document Implementation Patterns
- Idempotency key handling (required vs. optional, validation behavior)
- Cascade delete transactionality guarantees
- Resource expansion behavior and response schema variance
- Error handling patterns and RFC 7807 compliance
Medium-Term (Continuous Improvement)
7. Establish Governance
- Define domain ownership for each API
- Create fragment update/versioning process
- Establish API design review process
- Set up OpenAPI validation in CI/CD
8. Enhance Documentation
- Add comprehensive examples for all operations
- Document each search strategy (Individual trace patterns)
- Provide client integration guides
- Document migration paths if APIs evolve
9. Monitor & Iterate
- Gather consumer feedback on API usability
- Track common error patterns
- Monitor API granularity effectiveness
- Evolve based on real-world usage
📊 Spec Quality Assessment
| Criteria | VOD2.0 Submission Returns | VOD2.0 Payments | Individual | Notes |
|---|---|---|---|---|
| OpenAPI Validity | ✅ Pass | ❌ Fail | ⚠️ Warning | Payments: broken $ref paths; Individual: invalid timestamp |
| REST Best Practices | ✅ Strong | ✅ Strong | ✅ Strong | All specs demonstrate good REST design |
| Error Handling | ✅ Comprehensive | ✅ Comprehensive | ✅ Comprehensive | Shared error fragments, good coverage |
| Platform Standards | ✅ Aligned | ✅ Aligned | ✅ Aligned | Correlation ID, OAuth2, integration catalogue |
| Path Conventions | ⚠️ Dynamic version | ⚠️ Unusual ordering | ⚠️ No version | Inconsistent versioning approach |
| API Granularity | ✅ Focused | ⚠️ Mixed concerns | ⚠️ Broad scope | Payments mixes read/CRUD; Individual combines many concerns |
| Fragment Strategy | ⚠️ Local copies | ⚠️ Local copies | ⚠️ Local copies | Need to verify consistency, consider consolidation |
| Content Type Clarity | ✅ Clear | ✅ Clear | ✅ Clear | JSON throughout, appropriate content types |
| Advanced Patterns | ⚠️ Optional idempotency | ✅ Expand, cascade | ✅ OneOf search | Good patterns, some considerations noted |
Overall Assessment: Specs demonstrate strong REST API design fundamentals and good alignment with HMRC platform standards. Key improvements needed: fix validation errors, verify fragment consistency, standardize conventions, and clarify API scope decisions.
📚 References
OpenAPI Standards
REST API Design
HTTP Standards
API Patterns
- JSON:API Specification - Sparse fieldsets, resource inclusion patterns
- Stripe API Design - Expand parameter, idempotency keys
- Microsoft API Guidelines - Versioning, error handling
Internal Documentation
.ai/projects/domain-apis/- Domain API POC work (reference for emerging patterns).ai/projects/domain-apis/lessons-learned.md- Lessons from domain API implementations
✍️ Review Summary
Approval Status
⚠️ Request Changes - Specs require corrections before finalization
Critical Issues (Must Fix)
- ❌ Payments API: Broken
$refpaths (won’t validate) - ❌ Individual API: Invalid timestamp format
- ⚠️ OAuth token URL difference needs verification (Individual vs VOD2.0)
Design Considerations (Recommend Addressing)
- Fragment standardization (Individual vs VOD2.0 style - see Appendix B)
- Path structure conventions (versioning approach)
- API granularity (Individual API scope, Payments API mixed concerns)
- Idempotency key (optional vs. required)
- Path parameter ordering (type → value)
Strengths
- ✅ Strong REST API design across all specs
- ✅ Comprehensive error handling
- ✅ Good HMRC platform standards alignment
- ✅ Advanced API patterns (expand, cascade delete, flexible search)
Next Steps
- Fix validation errors in Payments and Individual APIs
- Verify and standardize common fragments
- Clarify API scope and versioning strategy
- Document design decisions and rationales
- Re-validate all specs with OpenAPI validator
Reviewer: AI Agent
Date: 2026-02-13
Status: Changes Requested
Re-review Required: After fixes applied
Appendix A: Detailed Validation Results (vacuum lint)
Tool: vacuum v0.23.8
Ruleset: recommended (54 rules)
Date: 2026-02-13
Validation Summary
| Spec | Quality Score | Errors | Warnings | Info | Status |
|---|---|---|---|---|---|
| Individual Combined Details | 39/100 🤒 | 2 | 94 | 22 | ⚠️ Needs Improvement |
| VOD2.0 Submission Returns | 56/100 F | 2 | 41 | 8 | ⚠️ Needs Improvement |
| VOD2.0 Payments | 10/100 🥵 | 21 | 109 | 30 | ❌ Critical Issues |
Payments API - Detailed Errors
Broken References (13 errors)
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/AddAccruingInterestDetails
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/AddPostedInterestDetails
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/AddLockInformation
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/AddPenaltyDetails
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/AddRegimeTotalisation
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/IncludeClearedItems
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/IncludePaymentOnAccount
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/IncludeStatisticalItems
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/DateFrom
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/DateTo
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/DateType
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/SearchItem
ERR unable to locate reference anywhere in the rolodex
└── reference: #components/parameters/SearchType
Operation Build Failures (7 errors)
ERR error building path item: array build failed: reference cannot be found
Operations fail to build because parameter references can’t be resolved.
Path Parameter Mismatches (3 errors)
Path parameter definitions don’t match operation parameter declarations.
Individual API - Detailed Violations
Top Rule Violations
| Rule | Violations | Description |
|---|---|---|
oas3-missing-example | 52 | Request/response schemas lack examples |
component-description | 15 | Components missing descriptions |
description-duplication | 13 | Duplicate descriptions (copy-paste) |
operation-description | 9 | Operations lack good descriptions |
oas3-parameter-description | 8 | Parameters missing descriptions |
oas-missing-type | 9 | Schemas missing explicit type field |
oas3-valid-schema-example | 8 | Examples don’t validate against schemas |
no-$ref-siblings | 2 | Properties alongside $ref (ignored) |
no-http-verbs-in-path | 1 | Path contains HTTP verb |
operation-tag-defined | 1 | Tag not defined in global tags |
Submission Returns API - Detailed Violations
Top Rule Violations
| Rule | Violations | Description |
|---|---|---|
oas3-missing-example | 25 | Request/response schemas lack examples |
component-description | 10 | Components missing descriptions |
oas-missing-type | 6 | Schemas missing explicit type field |
oas3-parameter-description | 5 | Parameters missing descriptions |
no-$ref-siblings | 2 | Properties alongside $ref (ignored) |
description-duplication | 2 | Duplicate descriptions |
operation-description | 1 | Operation lacks good description |
Payments API - Detailed Violations
Top Rule Violations (Beyond Errors)
| Rule | Violations | Description |
|---|---|---|
oas3-missing-example | 27 | Request/response schemas lack examples |
oas3-unused-component | 26 | Components defined but never referenced |
component-description | 25 | Components missing descriptions |
oas-missing-type | 22 | Schemas missing explicit type field |
oas3-parameter-description | 21 | Parameters missing descriptions |
resolving-references | 13 | Broken $ref paths (CRITICAL) |
description-duplication | 8 | Duplicate descriptions |
oas3-valid-schema-example | 8 | Examples don’t validate against schemas |
operation-operationId | 4 | Operations missing operationId |
path-params | 3 | Path parameter mismatches |
Quality Breakdown by Category
Individual API (39/100)
Category Violations Impact
──────────────────── ──────────── ──────────────────
Operations 1 Low
Tags 1 Low
Schemas 11 Medium
Descriptions 45 High
Examples 60 High
──────────────────── ──────────── ──────────────────
Total 118
Submission Returns API (56/100)
Category Violations Impact
──────────────────── ──────────── ──────────────────
Schemas 8 Medium
Descriptions 18 Medium
Examples 25 High
──────────────────── ──────────── ──────────────────
Total 51
Payments API (10/100)
Category Violations Impact
──────────────────── ──────────── ──────────────────
Operations 7 Critical (blocking)
Schemas 62 High
Descriptions 56 High
Examples 35 High
──────────────────── ──────────── ──────────────────
Total 160
Fix Priority Matrix
P0 - BLOCKING (Must Fix Before Use)
| Issue | Spec | Impact | Effort |
|---|---|---|---|
Broken $ref paths | Payments | ❌ Spec won’t parse | 15 min |
| Invalid timestamp | Individual | ⚠️ Metadata parsing fails | 2 min |
Estimated total: 20 minutes
P1 - HIGH (Fix Before Finalization)
| Issue | All Specs | Impact | Effort |
|---|---|---|---|
| Missing examples | All | Documentation quality | 2-3 hours |
| Missing types | All | Schema clarity | 30-60 min |
$ref siblings | Individual, Submission | Parsing inconsistency | 15 min |
Estimated total: 3-4 hours
P2 - MEDIUM (Fix Before Production)
| Issue | All Specs | Impact | Effort |
|---|---|---|---|
| Missing descriptions | All | Developer experience | 2-3 hours |
| Duplicate descriptions | All | Maintenance burden | 1 hour |
| Invalid examples | Individual, Payments | Trust in docs | 30 min |
Estimated total: 3-4 hours
P3 - LOW (Polish)
| Issue | Spec | Impact | Effort |
|---|---|---|---|
| Unused components | Payments | Spec clutter | 30 min |
| Missing operationId | Payments | Code gen naming | 15 min |
| HTTP verb in path | Individual | REST convention | 5 min |
Estimated total: 1 hour
Improvement Roadmap
Sprint 1: Unblock (Immediate - 30 min)
- Fix all broken
$refpaths in Payments API - Fix invalid timestamp in Individual API
- Run validation to confirm clean parse
Target: All specs validate without errors
Sprint 2: Quality (1-2 days)
- Add examples to all POST/PUT/PATCH operations
- Add explicit
typeto all schemas - Fix
$refsibling violations - Add missing parameter descriptions
Target: Quality Score > 70 for all specs
Sprint 3: Production Ready (2-3 days)
- Complete all component descriptions
- Fix duplicate descriptions
- Validate all schema examples
- Remove unused components
- Add operationIds
Target: Quality Score > 85 for all specs
Validation Commands
Run validation
# Individual API
vacuum lint ".ai/projects/hip/.local-only/Individual OAS (1API MEP)/Individual-combined-details.yaml"
# Submission Returns API
vacuum lint ".ai/projects/hip/.local-only/VOD2.0 OAS (1API MEP)/Vaping-Submission-return-(1API 1EP MM)/Get-Submit-vpd-return-data.yaml"
# Payments API
vacuum lint ".ai/projects/hip/.local-only/VOD2.0 OAS (1API MEP)/Vaping-payment-(1API MEP MM)/financial-Direct-debit-data.yaml"Quality Gates
# Fail build if errors found
vacuum lint spec.yaml --errors-only
# Target quality score
vacuum lint spec.yaml | grep "Quality Score"
# Minimum acceptable: > 70/100
# Production ready: > 85/100Rules Reference
Key vacuum rules applied:
oas3-missing-example: All operations should have request/response examplescomponent-description: All components should have clear descriptionsoas-missing-type: All schemas must have explicittypefieldoas3-parameter-description: All parameters should be documentedno-$ref-siblings: Don’t add properties alongside$ref(useallOf)oas3-valid-schema-example: Examples must validate against schemasresolving-references: All$refpaths must resolve correctlyoas3-unused-component: Remove components that aren’t referencedoperation-operationId: All operations should have unique IDsdescription-duplication: Avoid copy-paste descriptions
Full ruleset: https://quobix.com/vacuum/rulesets/recommended
End of Validation Results
Appendix B: Fragment Consistency Analysis
Analysis Date: 2026-02-13
Scope: Common and security fragments across all three specs
Method: Checksum comparison + manual diff review
Summary Findings
Key Finding: Individual API fragments systematically differ from VOD2.0 fragments. The two VOD2.0 specs (Submission Returns and Payments) have identical fragments, indicating consistent authoring.
Fragment Consistency Matrix
| Fragment | Individual vs VOD2.0 | VOD2.0 Internal | Assessment |
|---|---|---|---|
correlationId.yaml | ❌ Different | ✅ Identical | Minor (title field) |
error400.yaml | ❌ Different | ✅ Identical | Title naming convention |
error403.yaml | ❌ Different | ✅ Identical | Title naming convention |
error404.yaml | ✅ IDENTICAL | ✅ Identical | ✅ Fully consistent |
error422.yaml | ❌ Different | ✅ Identical | Title naming convention |
error500.yaml | ❌ Different | ✅ Identical | Title naming convention |
error503.yaml | ✅ IDENTICAL | ✅ Identical | ✅ Fully consistent |
govUkOriginatorId.yaml | ❌ Different | ✅ Identical | Minor (title field) |
hip-origin.yaml | ❌ Different | ✅ Identical | Minor (title field) |
identifierParameter.yaml | ❌ Different | ✅ Identical | Minor (title field) |
oauth2.yaml | ❌ Different | ✅ Identical | Title + token URL |
Results:
- ✅ 2 of 11 fragments fully identical across all specs (error404, error503)
- ⚠️ 9 of 11 fragments differ between Individual and VOD2.0
- ✅ VOD2.0 specs internally consistent (Submission Returns ≡ Payments)
Nature of Differences
Pattern 1: Title Field Differences (Cosmetic)
Individual API approach:
errorResponse_400:
title: 400_ErrorResponse # ← Prefixed with error code
description: Error Response Payload...VOD2.0 approach:
errorResponse_400:
title: Error Response # ← Generic, no prefix
description: Error Response Payload...Impact:
- Functional: None (title is optional metadata)
- Documentation: Slightly different generated code/docs
- Maintenance: Cosmetic drift
Pattern 2: Missing vs Present Titles
Example: correlationId.yaml
Individual API:
correlationId:
title: CorrelationId # ← Has title
type: string
format: uuidVOD2.0:
correlationId:
type: string # ← No title
format: uuidImpact: Cosmetic only
Pattern 3: OAuth Token URL Difference
Individual API:
tokenUrl: https://example.com # ← No www, no trailing slashVOD2.0:
tokenUrl: https://www.example.com/ # ← Has www, has trailing slashImpact:
- ⚠️ Potentially functional if token URLs should be consistent
- ⚠️ May affect OAuth authentication flow
- Needs verification: Are these placeholder URLs or real endpoints?
Root Cause Analysis
Evidence of Separate Authoring
VOD2.0 internal consistency:
- Submission Returns and Payments: 100% identical fragments
- Suggests: Same team, template, or time period
Individual divergence:
- Systematic pattern: Always adds titles, uses prefixed format
- Suggests: Different template version or authoring standards
Likely Scenarios
- Different teams: Individual API team vs VOD2.0 team with different conventions
- Different timelines: Individual created first (or last) with different standards
- Template evolution: Fragment template updated between spec authoring
- Intentional variation: Individual team preferred explicit titles for clarity
Impact Assessment
Functional Impact: LOW
- ✅ All fragments have identical structure and validation rules
- ✅ Error codes, patterns, required fields all match
- ⚠️ OAuth token URL difference needs verification
Consumer Impact: LOW
- ✅ Error response structure identical across APIs
- ⚠️ Documentation titles may vary slightly
- ⚠️ Generated client code may have minor naming differences
Maintenance Impact: MEDIUM to HIGH
- ❌ Three versions of “platform standard” fragments
- ❌ Updates must be applied to multiple fragment sets
- ❌ Risk of further drift over time
- ❌ No clear “canonical” version
Recommendations
Immediate (P0)
- Verify OAuth token URL
- Confirm if
https://example.comvshttps://www.example.com/is intentional - Standardize if they should match
- Document if intentionally different
- Confirm if
Short-Term (P1)
-
Choose canonical fragment standard
Option A: VOD2.0 fragments (Recommended)
- ✅ Already consistent across 2 specs
- ✅ Cleaner titles (no code prefixes)
- ✅ Less migration work (update Individual only)
Option B: Individual fragments
- Prefixed titles more explicit
- More work (update 2 specs)
Option C: New unified standard
- Best of both approaches
- Most work (update all 3 specs)
- Opportunity for improvement
-
Document chosen standard
- Create fragment style guide
- Explain rationale (why this approach)
- Provide examples
-
Migrate divergent specs to chosen standard
- Update Individual if choosing VOD2.0 style
- Update VOD2.0 specs if choosing Individual style
Medium-Term (P2)
-
Centralize fragments (per main review recommendation)
.ai/projects/hip/fragments/v1/ ├── common/ │ ├── correlationId.yaml # ← Canonical version │ ├── error*.yaml │ └── ... └── security/ └── oauth2.yaml- All specs reference centralized location
- Single source of truth
- Versioned evolution (v1, v2, etc.)
-
Establish fragment governance
- Owner: Who maintains platform fragments?
- Process: How are changes proposed/approved?
- Versioning: How are breaking changes handled?
- Migration: How are specs updated to new versions?
Long-Term (P3)
-
Automate consistency checks in CI/CD
# Check fragments match canonical versions vacuum lint --fragments-check # Or custom script ./scripts/check-fragment-consistency.sh -
Create fragment update tooling
- Script to update all specs to new fragment version
- Validation that specs still work after update
- Automated PR creation for fragment updates
Updated Main Review Recommendation
Section 2: Fragment Consistency Verification Needed
Original recommendation:
“Need to verify fragments are identical and flag any divergence”
Updated based on analysis:
✅ Verified:
- VOD2.0 Submission Returns and Payments have identical fragments (fully consistent)
- Individual API fragments differ systematically (title field conventions)
- 2 of 11 fragments fully consistent across all three specs
- Differences are cosmetic (title fields) except OAuth token URL
- No functional impact on error handling or API behavior
Recommendation: Standardize on VOD2.0 fragment style (less migration work) and centralize fragments for future consistency.
Detailed Differences
correlationId.yaml
correlationId:
+ title: CorrelationId # Individual adds this
type: string
format: uuiderror400.yaml (similar for 403, 422, 500)
errorResponseFailure_400:
- title: Failure Object in Error Response # VOD2.0: generic
+ title: 400_ErrorResponseFailure # Individual: prefixedoauth2.yaml
flows:
clientCredentials:
- tokenUrl: https://www.example.com/ # VOD2.0
+ tokenUrl: https://example.com # Individual
scopes:Analysis Complete: ✅
Action Required: Choose canonical fragment standard and document decision
Impact: LOW functional, MEDIUM maintenance
Next Steps: Discuss with spec authors, standardize chosen approach