# Technical architecture of a PSD2 API: what to know before writing the first line of code

> A technical overview of PSD2 for CTOs, developers and technical PMs: API standards, security (mTLS, eIDAS certificates), OAuth2, SCA flows, consent management, implementation pitfalls.

_Published: 2026-04-07 · Category: technical · Tags: #dsp2 #psd2 #open-banking #architecture #api #technical_

---

[DSP2](https://openfinanceguide.com/en/glossary/dsp2) is often seen from the business side as a simple "obligation for banks to open up APIs". On the technical side, it's a non-trivial assembly: multiple standards, qualified electronic certificates, hardened OAuth2, asynchronous authentication flows, and very real fragmentation between banks.

This article gives a **high-level but precise** view of that architecture, for CTOs, technical PMs and developers who need to scope a PSD2 project — whether to integrate a partner [TPP](https://openfinanceguide.com/en/glossary/tpp) or to expose your own APIs. For the business / product version, see instead: [Understanding PSD2 visually](/blog/comprendre-psd2-visuellement).

## The PSD2 stack in one picture

A PSD2 call crosses several layers that don't serve the same purpose. None of them is optional.

```mermaid
flowchart TB
    subgraph APP["TPP application"]
        BL["Business logic\n(aggregation, payment…)"]
    end

    subgraph SEC["Security layer"]
        OAUTH["OAuth2\n(consent & tokens)"]
        SIG["Request signing\n(QSealC)"]
        TLS["mTLS\n(QWAC)"]
    end

    subgraph API["ASPSP API"]
        STD["API standard\n(STET / Berlin Group / OB UK)"]
        ENDPOINTS["AIS / PIS / CBPII endpoints"]
    end

    BL --> OAUTH
    OAUTH --> SIG
    SIG --> TLS
    TLS --> STD
    STD --> ENDPOINTS
```

Key takeaway: you **don't just** sign your requests over plain HTTPS. The channel is **mTLS** (both parties authenticate), the payloads are **signed** independently, and authorisation goes through a strictly profiled **OAuth2**.

## API standards: not one, but three

The directive requires opening up APIs but **does not dictate the format**. Three standards have emerged in Europe.

| Standard                                | Coverage           | Style                                                           |
| --------------------------------------- | ------------------ | --------------------------------------------------------------- |
| [STET](https://openfinanceguide.com/en/glossary/stet)            | France and Belgium | REST/JSON, based on [ISO 20022](https://openfinanceguide.com/en/glossary/iso-20022)           |
| [Berlin Group](https://openfinanceguide.com/en/glossary/berlin-group)    | Most of the EU     | REST/JSON, the most widespread                                  |
| [Open Banking UK](https://openfinanceguide.com/en/glossary/open-banking-uk) | United Kingdom     | The most mature, mandatory [FAPI](https://openfinanceguide.com/en/glossary/fapi) profile |

Concrete consequences:

- Covering **all of France** = supporting STET (BNP, Société Générale, BPCE, Crédit Agricole, La Banque Postale, etc.) **plus** a few Berlin Group implementations from online banks.
- Scaling across Europe = adding Berlin Group with **per-country variants** (Germany, Spain and Italy don't follow exactly the same version).
- In practice, many teams use an **aggregator** ([Bridge](https://openfinanceguide.com/en/glossary/bridge), [Tink](https://openfinanceguide.com/en/glossary/tink), [TrueLayer](https://openfinanceguide.com/en/glossary/truelayer), [Yapily](https://openfinanceguide.com/en/glossary/yapily)) that absorbs this fragmentation in exchange for a margin.

> **Info:**
> Outside the EU, the OpenID Foundation's [FAPI](https://openfinanceguide.com/en/glossary/fapi) standard
>   is becoming the reference (UK, Brazil, Australia). Berlin Group and STET reuse
>   its principles but in a more flexible way — convergence is expected with
>   [DSP3](https://openfinanceguide.com/en/glossary/dsp3) and [FIDA](https://openfinanceguide.com/en/glossary/fida).

## The chain of trust: eIDAS, QWAC and QSealC

PSD2 answers a simple question: **how does a bank know that an API call really comes from a licensed TPP?** The answer lies in the qualified electronic certificates of the [eIDAS TSP](https://openfinanceguide.com/en/glossary/eidas-tsp) regulation.

```mermaid
flowchart TB
    NCA["⚖️ NCA\n(e.g. ACPR)"]
    QTSP["🔏 QTSP\n(e.g. Certigna)"]
    TPP["🏢 TPP\nHolds the certificates"]
    ASPSP["🏦 ASPSP\nChecks on every call"]

    NCA -->|"1. Licenses the TPP"| TPP
    TPP -->|"2. Requests certificates"| QTSP
    QTSP -->|"3. Issues QWAC + QSealC"| TPP
    TPP -->|"4. Presents its certificates"| ASPSP
    ASPSP -->|"5. Checks against the registry"| NCA
```

Two certificates, two uses:

- [QWAC](https://openfinanceguide.com/en/glossary/qwac) — used to establish the
  [mTLS](https://openfinanceguide.com/en/glossary/mtls) channel. It identifies the TPP at the transport
  level. Think "ID card".
- [QSealC](https://openfinanceguide.com/en/glossary/qsealc) — signs the **content** of requests (detached
  signature). It guarantees the **integrity** and **non-repudiation** of the
  payload. Think "wax seal on the envelope".

Both **encode the TPP's PSD2 roles** (AISP / PISP / CBPII). The bank dynamically checks that the certificate actually covers the requested operation.

## OAuth2: materialising consent

On the authorisation side, it's [OAuth2](https://openfinanceguide.com/en/glossary/oauth2) in its **Authorization Code + PKCE** flow. Nothing exotic, but with constraints:

1. The TPP builds the authorisation URL and redirects the PSU to the bank.
2. The bank authenticates the PSU (login + [SCA](https://openfinanceguide.com/en/glossary/sca)) then shows the consent screen.
3. Return to the TPP with a single-use code.
4. Exchange of the code for a (short-lived) **access token** and a (long-lived) **refresh token** (up to 180 days on the AISP side).
5. Every API call carries `Authorization: Bearer <token>` **and** the QSealC signature **and** goes through mTLS.

For stricter profiles ([FAPI](https://openfinanceguide.com/en/glossary/fapi) 1.0 Advanced or 2.0), you add [JWT / JWS / JWE](https://openfinanceguide.com/en/glossary/jwt-jws-jwe) for signed requests (JAR), and binding of the token to the client certificate. It's mandatory in the UK, optional / not enforced on Berlin Group and STET.

## The three SCA flows to support

The moment when the PSU authenticates with their bank (the [SCA](https://openfinanceguide.com/en/glossary/sca)) can take **three forms**, and **the bank chooses**, not you. Covering the whole market requires handling all three — see [Flux SCA](https://openfinanceguide.com/en/glossary/sca-flows).

| Flow          | How it works                                                                    | When the bank picks it             |
| ------------- | ------------------------------------------------------------------------------- | ---------------------------------- |
| **Redirect**  | The PSU is redirected to their bank's website or app, then comes back.          | The de facto standard in France.   |
| **Decoupled** | Push notification in the banking app, biometric validation, polling on the TPP. | Neobanks, better mobile UX.        |
| **Embedded**  | The PSU enters their credentials in the TPP interface, which forwards them.      | Rare, discouraged, often refused.  |

```mermaid
sequenceDiagram
    autonumber
    participant TPP
    participant AS as Authorization Server (ASPSP)
    participant PSU
    participant RS as Resource Server (ASPSP)

    TPP->>AS: 1. Creates the consent
    AS-->>TPP: consentId + auth URL
    TPP->>PSU: 2. Redirect / Decoupled / Embedded
    PSU->>AS: 3. Authentication + SCA
    AS-->>TPP: 4. OAuth code (callback)
    TPP->>AS: 5. Exchange code → access_token (mTLS)
    AS-->>TPP: access_token + refresh_token
    TPP->>RS: 6. GET /accounts (Bearer + QSealC + mTLS)
    RS-->>TPP: Account data
```

Common pitfalls:

- In **decoupled** mode, some banks require a minimum delay between triggering and polling, others limit the number of calls and require a webhook.
- In **redirect** mode, mobile returns via deep link are a source of bugs: plan a web fallback.
- A single initial SCA, then **180 days** of AIS access via refresh token without a new SCA — that's the 2022 EBA rule. Before 2022 it was 90 days.

## Consent: an object to model end to end

Consent is not a boolean. It's an **object** stored on the bank's side, with:

- A **scope** (accounts, access types: balances, transactions, details).
- A **time window** (up to 180 days for AIS, single-use for PIS).
- A **frequency** (4 refreshes per day without the PSU present).
- A **state** that changes over time.

```mermaid
stateDiagram-v2
    [*] --> Created: TPP creates the consent
    Created --> Authenticated: PSU authenticates + SCA
    Authenticated --> Valid: Bank validates
    Created --> Rejected: PSU refuses
    Created --> Expired: SCA deadline exceeded
    Valid --> Revoked: PSU revokes
    Valid --> Expired: 180 days elapsed
    Rejected --> [*]
    Revoked --> [*]
    Expired --> [*]
```

On the implementation side, you must:

- **Persist** the consentId and tie it to your PSU.
- **Monitor** its state (poll + webhook if available).
- **Trigger** a renewal before expiry, ideally with a UX that anticipates it.
- **Handle revocation** on the bank side: a consent that's active on your end can be killed without you knowing until you call the API.

## Security: [mTLS](https://openfinanceguide.com/en/glossary/mtls), signing, idempotency

Three technical requirements shape any PSD2 integration.

- **[mTLS](https://openfinanceguide.com/en/glossary/mtls)** on all back-channel calls. The TPP presents its [QWAC](https://openfinanceguide.com/en/glossary/qwac), the bank presents its own.
- **[HTTP Signature](https://openfinanceguide.com/en/glossary/http-signature)** of requests via QSealC: the signature covers the critical headers and a hash of the body. Essential on the PIS side so a payment initiation cannot be repudiated.
- **[Idempotency key](https://openfinanceguide.com/en/glossary/idempotency-key)** on all PIS operations to avoid double payments in case of a network retry.

On top of this comes robust **error** handling: HTTP codes, business codes specific to each standard, rate limits, banking maintenance windows. No ASPSP API has a contractual 99.99% SLA.

## Webhook or polling?

To track the state of a consent, an initiated payment or a decoupled SCA flow, two mechanisms coexist — see [Webhook / Polling](https://openfinanceguide.com/en/glossary/webhook-polling).

- **Polling**: the TPP queries the status API at intervals. Universal, but costly and slow.
- **Webhook**: the bank calls a TPP endpoint. Reactive, but few banks expose a reliable one on the EU PSD2 side.

In practice you combine the two: webhook when available, polling as a fallback, with exponential backoff.

## Testing without breaking prod

Three environments to know:

- **Public sandbox**: open, without real eIDAS certificates. Useful for a POC.
- **Certified sandbox**: requires **test certificates** issued by a QTSP, with a per-bank enrolment process. This is where you validate the end-to-end integration.
- **Production**: requires [ACPR](https://openfinanceguide.com/en/glossary/acpr) licensing (or equivalent), valid QWAC + QSealC certificates, and prior registration with each bank.

The **move from sandbox to prod is rarely seamless**: payload sizes, latencies, error codes, real versus simulated SCA — every bank has surprises in store.

## What's next? What [DSP3](https://openfinanceguide.com/en/glossary/dsp3) and [PSR](https://openfinanceguide.com/en/glossary/psr) change

For technical teams, the changes to anticipate:

- **API quality** measured and published by regulators: SLA, response times, error rates.
- **End of screen scraping**: no more fallback outside the API.
- **Reinforced anti-fraud** (notably [APP fraud](https://openfinanceguide.com/en/glossary/app-fraud)): [VoP](https://openfinanceguide.com/en/glossary/vop) (Verification of Payee) on SEPA, sharing of indicators between players.
- **FAPI convergence** more than likely, alignment with the [EUDI Wallet](https://openfinanceguide.com/en/glossary/eudi-wallet) for identity.
- **[FIDA](https://openfinanceguide.com/en/glossary/fida)**: extension to new data families (savings, credit, insurance). The technical model is heavily inspired by PSD2 but with finer-grained **permission schemes**.

## To sum up

Building or integrating a PSD2 API is not "plugging in yet another REST API". It's:

1. Choosing one or more **standards** (STET, Berlin Group, possibly OB UK).
2. Obtaining and maintaining **eIDAS certificates** (QWAC + QSealC).
3. Implementing [OAuth2](https://openfinanceguide.com/en/glossary/oauth2) with PKCE, request signing and idempotency.
4. Supporting the **three SCA flows** on both the UX and backend sides.
5. Modelling **consent** as a first-class object, with its lifecycle.
6. Handling **fragmentation** bank by bank — or outsourcing it to an aggregator.

For the product / business version, read [Understanding PSD2 visually](/blog/comprendre-psd2-visuellement). For the full vocabulary, the [Open Banking glossary](/glossary) is indexed by category.

---

Source: https://openfinanceguide.com/en/blog/architecture-api-dsp2
