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 $ref paths (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 title fields and uses prefixed naming (e.g., 400_ErrorResponse vs Error Response)

Nature of Differences:

  1. Cosmetic (8 fragments): Individual adds optional title fields

    • correlationId.yaml: Adds title: CorrelationId
    • Error fragments: Adds prefixed titles (e.g., title: 400_ErrorResponse)
    • govUkOriginatorId.yaml, hip-origin.yaml, identifierParameter.yaml: Similar title additions
  2. 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?

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:92Z

Issue: 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:

SpecPath StructureVersion 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-details

Considerations:

  • 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 number

Current 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/trace

Scope Analysis:

Concern AreaEndpointsChange FrequencySecurity Sensitivity
Demographics (name, sex, DOB, marriage)7LowMedium-High (PII)
Contact (email, phone, preferences)4MediumMedium (PII)
Identifiers (NINO, TRN, etc.)1LowHigh (sensitive IDs)
Search/Trace1N/AHigh (search PII)
Summary1LowVaries

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:

AspectLiabilitiesBanking Services
OperationsGET only (query)POST, GET, PATCH, DELETE (full CRUD)
MutabilityRead-only, calculatedFully mutable, user-managed
LifecycleDerived from transactionsActive CRUD lifecycle
SecurityRead-heavy, broad accessWrite-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+json for 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/json implies UTF-8 by default (RFC 8259), so charset=UTF-8 is optional
  • application/merge-patch+json is 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-Type headers
  • JSON request/response bodies
  • application/merge-patch+json for 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.17

Consideration:

  • 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 Services

Questions:

  • Is there a domain registry mapping 8 to 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:

  • FinancialDetails schema references Totalisation in totalisation.yaml file
  • Totalisation schema references FinancialDetails in financialdetails.yaml file
  • 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 define Totalisation schema?
  • Check fragments/domain/payments/financialdetails.yaml - does it define FinancialDetails schema?
  • 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=contact

Pattern 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=contact vs ?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: true for array handling
  • Single item: ?expand=contact
  • Multiple items (future): ?expand=contact&expand=addresses
  • Response should include contact object only when expanded

Response Schema Consideration:

AccountComposite:
  properties:
    account:
      $ref: '#/components/schemas/AccountDetails'
    contact:
      $ref: '#/components/schemas/ContactDetails'
  required: [account]  # contact is conditional

This 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=true requires both accounts.delete and contact.delete scopes.

Note: OpenAPI can’t express conditional security. This must be enforced at runtime.

Alternative Patterns (for consideration):

  1. Separate endpoints:

    • DELETE /accounts/{id} (account only)
    • DELETE /account-profiles/{id} (account + contacts always)
  2. Async delete (if complex):

    • Return 202 Accepted with job ID
    • Poll for completion
    • Better for cascading deletes with many resources

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 $ref paths are correct (missing / before components)
  • Q2: Payments API - Confirm schema files (financialdetails.yaml vs totalisation.yaml) contain correct definitions
  • Q3: Individual API - What is correct timestamp for reviewed-date field?

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 8 and sub-domain 8.17 represent?
  • 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?

Immediate (Before Finalization)

1. Fix Critical Validation Issues

  • Payments API: Fix all broken $ref paths (#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

CriteriaVOD2.0 Submission ReturnsVOD2.0 PaymentsIndividualNotes
OpenAPI Validity✅ Pass❌ Fail⚠️ WarningPayments: broken $ref paths; Individual: invalid timestamp
REST Best Practices✅ Strong✅ Strong✅ StrongAll specs demonstrate good REST design
Error Handling✅ Comprehensive✅ Comprehensive✅ ComprehensiveShared error fragments, good coverage
Platform Standards✅ Aligned✅ Aligned✅ AlignedCorrelation ID, OAuth2, integration catalogue
Path Conventions⚠️ Dynamic version⚠️ Unusual ordering⚠️ No versionInconsistent versioning approach
API Granularity✅ Focused⚠️ Mixed concerns⚠️ Broad scopePayments mixes read/CRUD; Individual combines many concerns
Fragment Strategy⚠️ Local copies⚠️ Local copies⚠️ Local copiesNeed to verify consistency, consider consolidation
Content Type Clarity✅ Clear✅ Clear✅ ClearJSON throughout, appropriate content types
Advanced Patterns⚠️ Optional idempotency✅ Expand, cascade✅ OneOf searchGood 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

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)

  1. ❌ Payments API: Broken $ref paths (won’t validate)
  2. ❌ Individual API: Invalid timestamp format
  3. ⚠️ OAuth token URL difference needs verification (Individual vs VOD2.0)

Design Considerations (Recommend Addressing)

  1. Fragment standardization (Individual vs VOD2.0 style - see Appendix B)
  2. Path structure conventions (versioning approach)
  3. API granularity (Individual API scope, Payments API mixed concerns)
  4. Idempotency key (optional vs. required)
  5. Path parameter ordering (type → value)

Strengths

  1. ✅ Strong REST API design across all specs
  2. ✅ Comprehensive error handling
  3. ✅ Good HMRC platform standards alignment
  4. ✅ Advanced API patterns (expand, cascade delete, flexible search)

Next Steps

  1. Fix validation errors in Payments and Individual APIs
  2. Verify and standardize common fragments
  3. Clarify API scope and versioning strategy
  4. Document design decisions and rationales
  5. 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

SpecQuality ScoreErrorsWarningsInfoStatus
Individual Combined Details39/100 🤒29422⚠️ Needs Improvement
VOD2.0 Submission Returns56/100 F2418⚠️ Needs Improvement
VOD2.0 Payments10/100 🥵2110930❌ 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

RuleViolationsDescription
oas3-missing-example52Request/response schemas lack examples
component-description15Components missing descriptions
description-duplication13Duplicate descriptions (copy-paste)
operation-description9Operations lack good descriptions
oas3-parameter-description8Parameters missing descriptions
oas-missing-type9Schemas missing explicit type field
oas3-valid-schema-example8Examples don’t validate against schemas
no-$ref-siblings2Properties alongside $ref (ignored)
no-http-verbs-in-path1Path contains HTTP verb
operation-tag-defined1Tag not defined in global tags

Submission Returns API - Detailed Violations

Top Rule Violations

RuleViolationsDescription
oas3-missing-example25Request/response schemas lack examples
component-description10Components missing descriptions
oas-missing-type6Schemas missing explicit type field
oas3-parameter-description5Parameters missing descriptions
no-$ref-siblings2Properties alongside $ref (ignored)
description-duplication2Duplicate descriptions
operation-description1Operation lacks good description

Payments API - Detailed Violations

Top Rule Violations (Beyond Errors)

RuleViolationsDescription
oas3-missing-example27Request/response schemas lack examples
oas3-unused-component26Components defined but never referenced
component-description25Components missing descriptions
oas-missing-type22Schemas missing explicit type field
oas3-parameter-description21Parameters missing descriptions
resolving-references13Broken $ref paths (CRITICAL)
description-duplication8Duplicate descriptions
oas3-valid-schema-example8Examples don’t validate against schemas
operation-operationId4Operations missing operationId
path-params3Path 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)

IssueSpecImpactEffort
Broken $ref pathsPayments❌ Spec won’t parse15 min
Invalid timestampIndividual⚠️ Metadata parsing fails2 min

Estimated total: 20 minutes

P1 - HIGH (Fix Before Finalization)

IssueAll SpecsImpactEffort
Missing examplesAllDocumentation quality2-3 hours
Missing typesAllSchema clarity30-60 min
$ref siblingsIndividual, SubmissionParsing inconsistency15 min

Estimated total: 3-4 hours

P2 - MEDIUM (Fix Before Production)

IssueAll SpecsImpactEffort
Missing descriptionsAllDeveloper experience2-3 hours
Duplicate descriptionsAllMaintenance burden1 hour
Invalid examplesIndividual, PaymentsTrust in docs30 min

Estimated total: 3-4 hours

P3 - LOW (Polish)

IssueSpecImpactEffort
Unused componentsPaymentsSpec clutter30 min
Missing operationIdPaymentsCode gen naming15 min
HTTP verb in pathIndividualREST convention5 min

Estimated total: 1 hour


Improvement Roadmap

Sprint 1: Unblock (Immediate - 30 min)

  • Fix all broken $ref paths 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 type to all schemas
  • Fix $ref sibling 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/100

Rules Reference

Key vacuum rules applied:

  • oas3-missing-example: All operations should have request/response examples
  • component-description: All components should have clear descriptions
  • oas-missing-type: All schemas must have explicit type field
  • oas3-parameter-description: All parameters should be documented
  • no-$ref-siblings: Don’t add properties alongside $ref (use allOf)
  • oas3-valid-schema-example: Examples must validate against schemas
  • resolving-references: All $ref paths must resolve correctly
  • oas3-unused-component: Remove components that aren’t referenced
  • operation-operationId: All operations should have unique IDs
  • description-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

FragmentIndividual vs VOD2.0VOD2.0 InternalAssessment
correlationId.yaml❌ Different✅ IdenticalMinor (title field)
error400.yaml❌ Different✅ IdenticalTitle naming convention
error403.yaml❌ Different✅ IdenticalTitle naming convention
error404.yamlIDENTICAL✅ IdenticalFully consistent
error422.yaml❌ Different✅ IdenticalTitle naming convention
error500.yaml❌ Different✅ IdenticalTitle naming convention
error503.yamlIDENTICAL✅ IdenticalFully consistent
govUkOriginatorId.yaml❌ Different✅ IdenticalMinor (title field)
hip-origin.yaml❌ Different✅ IdenticalMinor (title field)
identifierParameter.yaml❌ Different✅ IdenticalMinor (title field)
oauth2.yaml❌ Different✅ IdenticalTitle + 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: uuid

VOD2.0:

correlationId:
  type: string            # ← No title
  format: uuid

Impact: Cosmetic only

Pattern 3: OAuth Token URL Difference

Individual API:

tokenUrl: https://example.com        # ← No www, no trailing slash

VOD2.0:

tokenUrl: https://www.example.com/   # ← Has www, has trailing slash

Impact:

  • ⚠️ 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

  1. Different teams: Individual API team vs VOD2.0 team with different conventions
  2. Different timelines: Individual created first (or last) with different standards
  3. Template evolution: Fragment template updated between spec authoring
  4. 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)

  1. Verify OAuth token URL
    • Confirm if https://example.com vs https://www.example.com/ is intentional
    • Standardize if they should match
    • Document if intentionally different

Short-Term (P1)

  1. 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
  2. Document chosen standard

    • Create fragment style guide
    • Explain rationale (why this approach)
    • Provide examples
  3. Migrate divergent specs to chosen standard

    • Update Individual if choosing VOD2.0 style
    • Update VOD2.0 specs if choosing Individual style

Medium-Term (P2)

  1. 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.)
  2. 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)

  1. Automate consistency checks in CI/CD

    # Check fragments match canonical versions
    vacuum lint --fragments-check
    # Or custom script
    ./scripts/check-fragment-consistency.sh
  2. 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: uuid

error400.yaml (similar for 403, 422, 500)

  errorResponseFailure_400:
-   title: Failure Object in Error Response   # VOD2.0: generic
+   title: 400_ErrorResponseFailure           # Individual: prefixed

oauth2.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