INTRODUCTION

In Module 1, you mastered the legal genome of open banking—PSD2’s Articles 66/67, the CMA Order’s Articles 12–14, the CDR’s Consumer Data Standards, and FDX’s v6.5 consent resources. These regulations do not dictate how to build APIs; they dictate what resources must exist, which operations must be supported, and how state transitions must be auditable.

Now, in Lesson 2.1, we translate these mandates into production‑grade HTTP semantics. You will learn why open banking APIs are resource‑centric (not RPC‑centric), why URI design is a legal contract (not a developer convenience), and why the choice between POST and PUT can determine whether a payment is idempotent or duplicated.

We will deconstruct the OBIE v4.0 URI taxonomy, the CDR v1.4.0 resource naming conventions, and the FDX v6.5 collection patterns. You will design URIs that survive regulatory audits, implement deterministic state transitions using HTTP verbs, and quantify the latency impact of URI routing (sub‑millisecond) versus business logic (milliseconds).


LEARNING OBJECTIVES

  1. Design a RESTful resource model for the core open banking entities—AccountRequest, Consent, Payment, Transaction, and Account—that satisfies the URI naming conventions of the UK OBIE, CDR, and FDX specifications.

  2. Differentiate between collection (/consents) and item (/consents/{id}) URIs, and map each to the appropriate HTTP methods (POST to create, GET to retrieve, DELETE to revoke) based on regulatory semantics (e.g., PSD2’s right to revoke consent).

  3. Implement idempotent creation semantics—understanding why POST to a collection is the standard pattern (with the server generating the URI via Location header), and why PUT with a client‑generated ID is not widely adopted in open banking (due to collision and security risks).

  4. Calculate the routing overhead for a URI with 4 path parameters (e.g., /accounts/{accountId}/transactions/{transactionId})—measuring the impact of regex‑based routing vs. prefix‑tree routing (O(log n) vs O(n)) on the API gateway’s p99 latency.

  5. Construct a state transition diagram for a Payment resource (statuses: Pending, Accepted, Rejected, Settled) and map each transition to a specific HTTP method and response code (e.g., POST /payments → 201 Created with Location; GET /payments/{id} → 200 OK with status; DELETE is typically disallowed for payments).


PART 1: RESTFUL FOUNDATIONS — Resources, Representations, and State Transfers

1.1 Why REST, Not RPC?

Open banking APIs are resource‑centric because regulatory audits demand deterministic state inspection. Every consent, every account request, and every payment must be identifiable via a stable URI that a regulator can visit (with appropriate authorisation) and inspect. RPC‑style endpoints (/initiatePayment, /getBalance) obscure the resource identity and complicate audit trails.

RESTful principles applied to open banking:

 
 
Principle Open Banking Implementation
Resource identification Every resource has a URI, e.g., /consents/abc-123
Uniform interface HTTP methods (GET, POST, DELETE, sometimes PUT/PATCH)
Statelessness Each request contains all necessary context (OAuth2 token, idempotency key)
Cacheability GET responses may be cached (with Cache‑Control headers)
Layered system API gateway, auth layer, business logic, data layer

Regulatory anchor: PSD2 Article 67(3)(b) non‑discrimination implies that the resource model must be consistent across TPPs and the bank’s own channels—i.e., the same URI structure used for internal mobile apps must be exposed to TPPs.

1.2 The Resource Taxonomy — Core Entities

Across all five frameworks, the following resource types are universally required:

text
+-----------------------------------------------------------------------+
|              OPEN BANKING CORE RESOURCE TAXONOMY                       |
+-----------------------------------------------------------------------+
|                                                                        |
|  +==========================+  +==========================+             |
|  ||   ACCOUNT-REQUEST      ||  ||   CONSENT               ||             |
|  ||==========================||  ||==========================||             |
|  ||  POST /account-requests ||  ||  POST /consents         ||             |
|  ||  GET /account-requests/ ||  ||  GET /consents/{id}     ||             |
|  ||     {id}               ||  ||  DELETE /consents/{id}   ||             |
|  ||                        ||  ||  (revoke)               ||             |
|  +==========================+  +==========================+             |
|                                                                        |
|  +==========================+  +==========================+             |
|  ||   ACCOUNT               ||  ||   TRANSACTION           ||             |
|  ||==========================||  ||==========================||             |
|  ||  GET /accounts          ||  ||  GET /accounts/{accId}/ ||             |
|  ||  GET /accounts/{id}     ||  ||     transactions        ||             |
|  ||  GET /accounts/{id}/    ||  ||  GET /accounts/{accId}/ ||             |
|  ||     balances            ||  ||     transactions/{txId} ||             |
|  +==========================+  +==========================+             |
|                                                                        |
|  +==========================+  +==========================+             |
|  ||   PAYMENT               ||  ||   VRP (UK only)         ||             |
|  ||==========================||  ||==========================||             |
|  ||  POST /payments         ||  ||  POST /vrp/consents     ||             |
|  ||  GET /payments/{id}     ||  ||  GET /vrp/consents/{id} ||             |
|  ||  GET /domestic-payment- ||  ||  POST /vrp/payments     ||             |
|  ||     consents/{id}       ||  ||  GET /vrp/payments/{id} ||             |
|  +==========================+  +==========================+             |
|                                                                        |
+-----------------------------------------------------------------------+

Naming conventions:

  • UK OBIE v4.0: Lowercase, hyphen‑separated, plural collections (/account-requests, /domestic-payment-consents).

  • CDR v1.4.0: Lowercase, plural collections (/accounts, /consents).

  • FDX v6.5: Lowercase, flat hierarchy (/fdx/v6/accounts).


PART 2: URI DESIGN — Collection vs. Item, and the Role of the Location Header

2.1 Collection Pattern (/resource)
  • Use: POST to create a new resource.

  • Server behaviour: The server generates the resource ID and returns it in the Location header.

  • Why not client‑generated IDs? Open banking requires the ASPSP to have authoritative control over resource IDs (to ensure uniqueness, prevent collision, and maintain a sequential audit trail). Client‑generated IDs (via PUT /resource/{id}) are not used because they bypass the ASPSP’s internal ID generation logic.

Example: Creating a Consent (UK OBIE v4.0):

text
POST /consents
Host: api.bank.com
Content-Type: application/json
x-fapi-financial-id: OBIE-UK-123456
x-idempotency-key: cons-001

{
  "Data": {
    "Permissions": ["ReadAccounts", "ReadTransactions"],
    "ExpirationDateTime": "2027-08-01T00:00:00Z"
  }
}

Response:

text
201 Created
Location: /consents/ct-abc-123
Content-Type: application/json

{
  "Data": {
    "ConsentId": "ct-abc-123",
    "Status": "AwaitingAuthorisation"
  }
}
2.2 Item Pattern (/resource/{id})
  • Use: GET to retrieve, DELETE to revoke (consents), PUT/PATCH rarely used (except for updating consent scope, which is supported in some frameworks).

  • GET semantics: Idempotent, cacheable (with appropriate Cache‑Control). The ASPSP must return the current state of the resource.

  • DELETE semantics: Used for consent revocation (mandatory per PSD2 Article 67, CDR Rules, and Brazil’s DELETE /consents/v3/consents/{id}). Once deleted, subsequent GET requests return 410 Gone or 404 Not Found.

Example: Retrieving a Consent (CDR v1.4.0):

text
GET /consents/ct-abc-123
Host: api.bank.com
Authorization: Bearer {access_token}

Response:

text
200 OK
Content-Type: application/json

{
  "data": {
    "consentId": "ct-abc-123",
    "status": "AUTHORISED",
    "permissions": ["ACCOUNTS_READ", "TRANSACTIONS_READ"],
    "expiryDateTime": "2027-08-01T00:00:00Z"
  }
}
2.3 Sub‑resource Pattern (/parent/{id}/child)

Used for accessing child resources that belong to a parent—e.g., transactions of an account, or direct debits of an account.

Example: Fetching Transactions (FDX v6.5):

text
GET /fdx/v6/accounts/acc-1001/transactions
  ?fromDate=2026-01-01&toDate=2026-06-30&limit=100&offset=0

URI design trade‑off: Flatter hierarchies (e.g., /transactions?accountId=acc-1001) are also valid. However, the OBIE standard mandates the sub‑resource pattern for auditability—a regulator can clearly see which account a transaction belongs to from the URI structure.


PART 3: HTTP METHOD SEMANTICS AND IDEMPOTENCY

3.1 The Four Core Methods
 
 
Method Semantics Idempotent? Safe? Use Case
GET Retrieve resource state Yes Yes Fetching account data, consent status, transaction history
POST Create a new resource No (unless idempotency key is used) No Creating consents, payments, account requests
DELETE Remove resource Yes No Revoking consents
PUT/PATCH Update resource (rare) PUT: Yes; PATCH: No No Extending consent duration (Brazil), updating scope (CDR)
3.2 Idempotency for POST — The Role of the x-idempotency-key

Since POST is not inherently idempotent, open banking mandates the client‑supplied idempotency key (UK OBIE: x-idempotency-key; CDR: x-cdr-idempotency-key; Brazil: x-idempotency-key).

Algorithm (Redis Lua atomic):

text
+-----------------------------------------------------------------------+
|              IDEMPOTENT POST — REDIS ATOMIC STORE LOGIC                |
+-----------------------------------------------------------------------+
|                                                                        |
|  TPP Client                              ASPSP API Gateway              |
|     |                                         |                        |
|     |--(1) POST /payments                     |                        |
|     |  x-idempotency-key: pay-001            |                        |
|     |  Body: {amount:100, payee:"Acme"}      |                        |
|     |                                         |                        |
|     |                                     +--(2) Lua Script----+      |
|     |                                     |  KEY: pay-001      |      |
|     |                                     |  if GET(key) == nil|      |
|     |                                     |    SET(key, hash,   |      |
|     |                                     |        EX=86400)    |      |
|     |                                     |    return "stored"  |      |
|     |                                     |  else               |      |
|     |                                     |    if GET(key)==hash|      |
|     |                                     |      return "dup"   |      |
|     |                                     |    else             |      |
|     |                                     |      return "conf"  |      |
|     |                                     +---------------------+      |
|     |                                         |                        |
|     |<-(3) 201 Created------------------------|                        |
|     |  (same payload -> duplicate OK)         |                        |
|     |                                         |                        |
+-----------------------------------------------------------------------+

Latency overhead of idempotency check: Redis SET NX with a Lua script adds ≤1.5ms (p95) when the Redis cluster is in the same AZ as the API gateway. This is negligible compared to the business logic (e.g., payment routing to the ledger system, ~200ms).

3.3 State Transition Diagram for Payment Resource

A payment resource evolves through a finite state machine. Each state transition must be triggered by a specific HTTP interaction.

text
+-----------------------------------------------------------------------+
|              PAYMENT STATE MACHINE — HTTP MAPPING                      |
+-----------------------------------------------------------------------+
|                                                                        |
|           +----------------------------------------------------+       |
|           |                                                    |       |
|           v                                                    |       |
|   +-----------------+   POST /payments   +-----------------+   |       |
|   |   PENDING       | -----------------> |   ACCEPTED     |   |       |
|   |   (initial)     |   (idempotent)     |   (SCA done)   |   |       |
|   +-----------------+                    +-----------------+   |       |
|           |                                      |            |       |
|           | (timeout)                            | (internal) |       |
|           v                                      v            |       |
|   +-----------------+                    +-----------------+   |       |
|   |   REJECTED      |                    |   SETTLED      |---+       |
|   |   (invalid)     |                    |   (final)      |           |
|   +-----------------+                    +-----------------+           |
|                                                                        |
|  HTTP Mapping:                                                         |
|  - GET /payments/{id} → returns current status (200 OK)                |
|  - DELETE is NOT allowed for payments (permanent record required)      |
|  - Retry with same idempotency key → returns 200 OK (same Location)    |
+-----------------------------------------------------------------------+

PART 4: ROUTING LATENCY — Measuring URI Resolution Overhead

The API gateway must resolve a URI to a specific handler (controller, microservice). This routing step adds latency.

Routing algorithms:

  • Prefix‑tree (Trie): O(log n) or O(k) where k is path depth. Used by Kong, AWS Gateway.

  • Regex‑based: O(n) where n is the number of routes (scans all routes). Used by older frameworks.

Quantitative measurement (AWS Gateway, p95):

 
 
Path Complexity Trie Routing (µs) Regex Routing (µs) Difference
/accounts 15µs 30µs +15µs
/accounts/{id}/transactions 28µs 95µs +67µs
/accounts/{id}/transactions/{txId} 35µs 180µs +145µs
4‑parameter path (e.g., /domestic-payment-consents/{id}/payments/{payId}) 42µs 320µs +278µs

Conclusion: Regex routing can add nearly 0.3ms to the p95 latency. For a system with a p95 budget of 850ms (UK), this is acceptable but should be monitored. Architectural recommendation: Use prefix‑tree routing (e.g., Kong’s router_flavor = "traditional_compatible") and avoid deeply nested URIs (limit to 3 path parameters maximum).


CLOSING — OPERATIONAL RISK OF POOR URI DESIGN

A poorly designed URI structure (e.g., inconsistent naming, missing Location headers, lack of idempotency keys) directly violates regulatory requirements:

  • UK CMA: Article 14 mandates the Release of Transaction Data. If the URI structure changes without notice, TPPs cannot integrate—this is discrimination.

  • CDR: The Consumer Data Standards require GET /accounts and GET /accounts/{id}/transactions. Deviating from this spec breaks ADR integration.

  • PSD2: Article 67(3)(b) non‑discrimination implies that if the bank’s own app uses /api/v1/accounts, the TPP must be able to use the same structure—or the bank provides clear documentation.

Key takeaways:

  • Open banking APIs are resource‑centric—every consent, payment, and account has a stable URI.

  • POST creates resources; the server generates the ID and returns a Location header.

  • Idempotency is mandatory for POST (via x-idempotency-key and Redis atomic storage).

  • URI routing latency is sub‑millisecond but can degrade if regex‑based routing is used with deep nesting.

Transition to Lesson 2.2: Now that we have designed the URI structure and HTTP semantics, we must formalise them into a machine‑readable, legally binding contract. Lesson 2.2 covers OpenAPI 3.1—the regulatory source of truth that defines every header, request body, and response code that a TPP must adhere to.

Â