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
-
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. -
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). -
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). -
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. -
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:
+-----------------------------------------------------------------------+
| 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):
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:
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):
GET /consents/ct-abc-123
Host: api.bank.com
Authorization: Bearer {access_token}
Response:
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):
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):
+-----------------------------------------------------------------------+
| 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.
+-----------------------------------------------------------------------+
| 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.