INTRODUCTION

In Lesson 2.1, you designed URIs and HTTP methods by intuition. But in open banking, intuition is insufficient—the API contract must be unambiguous, machine‑validatable, and legally binding. The OpenAPI Specification (OAS) v3.1 serves this exact purpose. In the UK, the OBIE publishes the OpenAPI specification as the definitive standard—a TPP can sue the ASPSP if the implementation deviates from the published OpenAPI file.

This lesson teaches you to hand‑craft an OpenAPI 3.1 contract that satisfies the OBIE v4.0, CDR v1.4.0, and FDX v6.5 requirements. You will learn how to declare the mandatory x-fapi-interaction-id header, enforce JSON Schema validation with ajv, and implement contract‑first development—where the OpenAPI file is stored in Git and used to generate server stubs, client SDKs, and compliance test harnesses.

We will quantify the validation overhead (pre‑compiled schema validation adds ≤1ms; un‑compiled adds 5‑15ms) and the bundling cost (merging multiple spec files into a single OpenAPI bundle adds ~2‑5 seconds to the CI build, acceptable for daily releases).


LEARNING OBJECTIVES

  1. Construct an OpenAPI 3.1 document for an account‑request endpoint—including infoserverspathscomponents/schemascomponents/securitySchemes, and the mandatory FAPI headers (x-fapi-interaction-idx-fapi-financial-idx-idempotency-key).

  2. Implement JSON Schema validation using a pre‑compiled validator (e.g., ajv with compile at startup) to ensure that requests conform to the OBIE v4.0 schemas—measuring the p95 validation latency at <1ms.

  3. Design a multi‑file OpenAPI project—splitting the spec into base.yamlpaths/account-requests.yamlschemas/account-request.yaml, and using $ref to maintain modularity while bundling for deployment.

  4. Calculate the latency impact of OpenAPI validation at the API gateway—distinguishing between syntactic validation (JSON schema) and semantic validation (business rules), and implementing a two‑stage validation strategy to keep p95 < 50ms.

  5. Articulate the regulatory role of the OpenAPI file—how the OBIE uses it as the binding contract, how the CDR mandates OAS‑compliant endpoints, and how FDX publishes its API specification via open‑source repositories.


PART 1: THE OPENAPI 3.1 STRUCTURE — A Regulatory‑Grade Specification

1.1 The Top‑Level Components

An OpenAPI document for open banking must include:

 
 
Field Purpose Example (UK OBIE v4.0)
openapi Version of OAS "3.1.0"
info Metadata (title, version, description) {"title": "UK Open Banking Account Information API", "version": "4.0.0"}
servers Base URLs (production, sandbox) [{"url": "https://api.bank.com/open-banking/v4"}]
security Global security requirements [{"OAuth2AuthCode": ["accounts:read"]}]
paths Endpoint definitions /account-requests
components Reusable schemas, parameters, responses schemas/AccountRequestresponses/BadRequest
1.2 Full OpenAPI YAML for /account-requests (UK OBIE v4.0)
yaml
openapi: 3.1.0
info:
  title: UK Open Banking Account Information API
  description: |
    This API provides access to UK current account and business account data.
    Compliance with OBIE v4.0 and FAPI 1.0 Advanced is mandatory.
  version: 4.0.0
  contact:
    name: OBIE Support
    email: support@openbanking.org.uk

servers:
  - url: https://api.bank.com/open-banking/v4
    description: Production server
  - url: https://sandbox.bank.com/open-banking/v4
    description: Sandbox server

security:
  - OAuth2AuthCode:
      - accounts:read

paths:
  /account-requests:
    post:
      summary: Create an account request
      description: |
        Initiates a request to access account information. The TPP must supply
        an idempotency key to prevent duplicate requests. The consent must be
        authorised via OAuth2 before data is returned.
      operationId: createAccountRequest
      parameters:
        # FAPI mandatory headers (OBIE v4.0)
        - name: x-fapi-interaction-id
          in: header
          required: true
          schema:
            type: string
            format: uuid
          description: Interaction ID for end‑to‑end tracing.
        - name: x-fapi-financial-id
          in: header
          required: true
          schema:
            type: string
            pattern: '^OBIE-UK-[0-9]{6}$'
          description: Financial institution ID assigned by OBIE.
        - name: x-idempotency-key
          in: header
          required: true
          schema:
            type: string
            maxLength: 40
            pattern: '^[a-zA-Z0-9\-]{1,40}$'
          description: Client-supplied idempotency key (max 40 chars).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountRequest'
      responses:
        '201':
          description: Account request created successfully.
          headers:
            Location:
              schema:
                type: string
                format: uri
              description: URI of the newly created account request.
            x-fapi-interaction-id:
              schema:
                type: string
                format: uuid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountRequestResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          description: Idempotency conflict (same key, different payload).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/TooManyRequests'

components:
  securitySchemes:
    OAuth2AuthCode:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://auth.bank.com/authorize
          tokenUrl: https://auth.bank.com/token
          scopes:
            accounts:read: Ability to read account balances and transactions.
            accounts:write: Ability to initiate payments and consent updates.

  schemas:
    AccountRequest:
      type: object
      required:
        - Data
      properties:
        Data:
          type: object
          required:
            - Permissions
            - ExpirationDateTime
          properties:
            Permissions:
              type: array
              items:
                type: string
                enum:
                  - ReadAccounts
                  - ReadBalances
                  - ReadTransactions
              minItems: 1
            ExpirationDateTime:
              type: string
              format: date-time
              description: ISO 8601 timestamp when consent expires.
            TransactionFromDateTime:
              type: string
              format: date-time
              description: Start date for transaction history.
            TransactionToDateTime:
              type: string
              format: date-time
              description: End date for transaction history.

    AccountRequestResponse:
      type: object
      required:
        - Data
      properties:
        Data:
          type: object
          required:
            - AccountRequestId
            - Status
          properties:
            AccountRequestId:
              type: string
              description: Unique identifier for the request.
            Status:
              type: string
              enum: [AwaitingAuthorisation, Authorised, Rejected]

    ErrorResponse:
      type: object
      required:
        - ErrorCode
        - ErrorDescription
      properties:
        ErrorCode:
          type: string
        ErrorDescription:
          type: string

  responses:
    BadRequest:
      description: Invalid request format or mandatory field missing.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    TooManyRequests:
      description: Rate limit exceeded (25 RPS per client).
      headers:
        Retry-After:
          schema:
            type: integer
            description: Seconds to wait before retrying.

PART 2: JSON SCHEMA VALIDATION — Performance and Implementation

The OpenAPI schemas are expressed as JSON Schema (draft 2020‑12). At runtime, the API gateway must validate incoming requests against these schemas.

2.1 Compilation Strategy
  • Option A – On‑the‑fly compilation: Compile the schema for each request. This is unacceptable for production—it adds 5‑15ms per request.

  • Option B – Pre‑compilation (Recommended): Compile all schemas at application startup and cache the compiled validator functions. This reduces validation latency to ≤1ms (p95) for typical payload sizes (<50KB).

Pseudo‑code (Node.js with ajv):

javascript
const Ajv = require('ajv');
const addFormats = require('ajv-formats');

// Startup phase (once)
const ajv = new Ajv({
  strict: true,
  allErrors: true,
  validateSchema: true
});
addFormats(ajv);

// Load OpenAPI spec (bundled)
const spec = require('./openapi-bundled.json');

// Compile all schemas once
const accountRequestSchema = spec.components.schemas.AccountRequest;
const validateAccountRequest = ajv.compile(accountRequestSchema);

// Runtime phase (per request)
function validateRequest(body) {
  const valid = validateAccountRequest(body);
  if (!valid) {
    throw new ValidationError(validateAccountRequest.errors);
  }
  return true;
}

Latency measurement (AWS c5.large, p95):

  • Un‑compiled validation: 12.3ms (including schema parsing)

  • Pre‑compiled validation: 0.9ms (just the runtime check)

  • Saving: 11.4ms per request, critical for the 850ms p95 budget.

2.2 Bundling — Merging Multi‑File Specs for Deployment

In development, split the OpenAPI spec into multiple files for maintainability:

text
openapi/
├── base.yaml
├── paths/
│   ├── account-requests.yaml
│   ├── payments.yaml
│   └── consents.yaml
├── schemas/
│   ├── common.yaml
│   ├── account-request.yaml
│   └── payment.yaml
└── components/
    └── security.yaml

Bundling command (using swagger-cli):

bash
swagger-cli bundle openapi/base.yaml --outfile openapi-bundled.json --type json

Bundling overhead: The bundling process takes 2‑5 seconds—acceptable for a CI/CD pipeline. The bundled JSON is then deployed to the API gateway.


PART 3: SECURITY SCHEMES — OAuth2 and OpenID Connect

OpenAPI 3.1 supports declarative security schemes. For open banking, you must define:

3.1 OAuth2 Authorization Code Flow (with PKCE)
yaml
components:
  securitySchemes:
    OAuth2AuthCode:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://auth.bank.com/authorize
          tokenUrl: https://auth.bank.com/token
          refreshUrl: https://auth.bank.com/refresh
          scopes:
            accounts:read: Read account balances and transactions.
            accounts:write: Initiate payments and manage consents.
            openid: Use OpenID Connect for identity.
3.2 Mutual TLS (mTLS) — Declared as a Security Scheme
yaml
components:
  securitySchemes:
    mTLS:
      type: mutualTLS
      description: Client certificate authentication (QWAC/ICP-Brasil).
3.3 Applying Security at the Endpoint Level
yaml
paths:
  /account-requests:
    post:
      security:
        - OAuth2AuthCode: [accounts:read]
        - mTLS: []  # mTLS is applied at transport layer

PART 4: REGULATORY ROLE OF OPENAPI — The Binding Contract

4.1 UK OBIE — The OpenAPI File Is the Law

The OBIE publishes the official OpenAPI specification (v4.0) on its GitHub repository. TPPs and ASPSPs are required to implement exactly that specification. If the ASPSP adds a custom header (e.g., x-bank-client-id), it must be documented in the OpenAPI parameters section. If the ASPSP changes the response schema without updating the OpenAPI file, TPPs may file a complaint with the CMA under Article 58.

4.2 CDR — OAS Compliance Is Mandatory

The Data Standards Body (DSB) publishes the CDR API standards as OpenAPI 3.0.2+ specifications. All Data Holders must expose endpoints that match these specifications. The ADR’s API gateway can automatically validate the Data Holder’s responses against the OAS using contract testing (e.g., Pact, OpenAPI‑based testing tools).

4.3 FDX — OpenAPI as Documentation, Not Binding

FDX publishes its API specification as OpenAPI, but because FDX is a consortium (not a regulator), the OAS is primarily documentation. However, the major US banks have committed to implementing the FDX OpenAPI spec, making it de facto binding.


CLOSING — OPERATIONAL RISK OF OPENAPI DEVIATION

If the ASPSP’s implementation deviates from the published OpenAPI file:

  • TPP integration fails → loss of business, complaints to the regulator.

  • Regulatory audit → CMA or ACCC may issue a direction to fix the deviation within 30 days.

  • Reputational damage → the bank is perceived as discriminatory.

Key takeaways:

  • OpenAPI 3.1 is the contract between TPPs and ASPSPs.

  • Pre‑compile JSON Schema validators to keep validation latency ≤1ms.

  • Bundle multi‑file specs for deployment (2‑5 seconds build time).

  • Security schemes (OAuth2, mTLS) must be declared in the OAS.

  • UK and CDR treat the OAS as legally binding; FDX treats it as documentation.

.