Skip to main content

FAPI 2.0 and your API gateway: the security profile requirements that matter for open banking

FAPI 2.0 compliance at the API gateway layer: mTLS, per-TPP rate limits, and a complete audit trail. What each requirement means and how to enforce it in Zerq.

  • open-banking
  • compliance
  • security
Zerq team

FAPI 2.0 (Financial-grade API 2.0) is now the required security baseline for open banking APIs across the EU, UK, Australia, and Brazil. Under PSD3, which took effect in 2026, payment service providers and the banks that host open banking APIs must demonstrate conformance with the FAPI 2.0 Baseline or Advanced Security Profile. Auditors no longer accept "we validate tokens" as an answer.

The problem is not understanding what the FAPI 2.0 API gateway layer must enforce. Teams generally know the requirements: sender-constrained tokens, mTLS or DPoP, strict audience binding, per-TPP rate limits, and a complete audit trail. The problem is enforcing all of those requirements in the same place, with evidence that survives a regulatory inspection.

A gateway that handles only token validation leaves the rest to be assembled from separate services: mTLS termination, per-TPP throttling, method restrictions, audit log completeness. Each boundary between those services is a gap in the evidence chain. What regulators ask for when they audit an open banking platform is not a list of tools. It is a coherent record of who called what, under what identity, and with what result.

Why existing approaches fall short

The common pattern for open banking compliance in legacy deployments is layered tooling: nginx or HAProxy for mTLS termination, Kong or AWS API Gateway for token validation and rate limiting, a separate logging pipeline for audit, and a TPP registration database that is distinct from the gateway's client registry. Each layer does its part but cannot see the others' state.

The audit gap is the most serious problem. If mTLS is terminated at a load balancer before traffic reaches your API gateway, the gateway's logs do not contain the certificate identity, only the forwarded client headers. Those headers can be spoofed if the load balancer configuration drifts or a misconfigured route bypasses the TLS layer. A regulator reviewing your audit trail will ask: how do you prove the certificate identity in this log entry was actually verified, and not just a header any caller could have set?

Kong handles mTLS through its mTLS Auth plugin, but the certificate identity lands in plugin context rather than the per-request log exported to your SIEM. Getting that identity into your audit record requires custom Lua. Apigee's control plane is hosted by Google, which creates a data residency question for banks in regulated jurisdictions: the audit log query goes through infrastructure the bank does not control. AWS API Gateway does not support inbound client certificate validation natively; the standard workaround is a custom authorizer Lambda, which adds another seam in the evidence chain.

What a FAPI 2.0 deployment needs is a gateway that treats mTLS identity, rate limiting, method restrictions, and audit logging as a single integrated system, not as separate features configured independently and hoped to produce consistent records.

What FAPI 2.0 actually requires at the gateway layer

The FAPI 2.0 security profile specifies requirements for APIs that expose payment account and payment initiation data. The gateway-level requirements that matter operationally are:

Sender-constrained tokens. Every access token must be bound to the caller's identity: either a client certificate (mTLS certificate-bound tokens) or a DPoP proof-of-possession key. The gateway must verify that the identity presented in the token matches the identity the caller actually presents. For mTLS, this means the gateway receives the certificate's Common Name and confirms it matches the registered TPP identity before any collection access proceeds.

Strict token validation. Tokens must be validated against a known issuer and audience. Short lifetimes (typically five minutes for access tokens in FAPI 2.0 Advanced deployments) require the gateway to reject expired tokens regardless of whether the TPP considers them still valid.

Per-TPP rate limiting. FAPI 2.0 does not prescribe exact limits, but the PSD3 obligation is that the ASPSP (the bank) must protect production infrastructure from any single TPP degrading service for others. This means per-client throttling enforced at the gateway, not at the backend. The backend should never see a burst it has to absorb itself.

Restricted methods. Account Information Service (AIS) APIs are read-only. A TPP registered as an AIS should not be able to issue POST, PUT, or DELETE requests to account endpoints. Method restrictions must be enforced at the gateway, not just documented in your developer portal.

Complete audit trail in customer infrastructure. Every request to an open banking API endpoint must produce a log entry that includes: the timestamp, the caller's identity, the endpoint called, the HTTP method, the response status, and the request and response payloads where relevant. That log must live in a system the bank controls, not a vendor's SaaS control plane.

How to configure a FAPI 2.0-compliant profile in Zerq

Each TPP registers as a client in Zerq. A client can have multiple profiles (one for sandbox, one for production, one for read-only AIS access), each with independent auth settings, rate limits, and method constraints. Here is a complete setup for a registered TPP.

Step 1: Create a rate limiting policy

Before registering the TPP, create a policy that reflects the throughput ceiling appropriate for that tier.

  1. Go to Policies and click New Policy.
  2. Set Name to tpp-standard-tier.
  3. Set Rate limit to 60 requests per minute (1m interval).
  4. Set Quota to 50,000 requests per month (30d interval).
  5. Click Create.

Policy intervals in Zerq use sliding windows for rate limits (1m, 5m, 1h) and calendar-based resets for quotas (1d, 7d, 30d). A TPP that exceeds its rate limit receives a 429 Too Many Requests with a structured error body; the backend never sees that request.

Step 2: Register the TPP as a client

  1. Go to Clients and click New Client.
  2. Set the name to the TPP's registered identifier (e.g. acme-payments-ltd).
  3. Under Policy, select tpp-standard-tier.
  4. Click Create.

The client's name should match the TPP's identifier in your open banking directory. This makes the audit log directly queryable by regulator-facing TPP name without a lookup table.

Step 3: Create an mTLS production profile

  1. Open the client and click Add Profile.
  2. Set Name to production-mtls.
  3. Set Auth type to mtls.
  4. Set Allowed methods to GET (for AIS-only TPPs) or GET, POST (for PIS).
  5. Set IP restrictions to the TPP's registered egress IP ranges, one per line in CIDR notation.
  6. Click Save.

The mTLS profile type carries no credential material in Zerq's storage. Identity is established entirely at the TLS layer. Zerq validates that the X-Client-ID forwarded from ingress matches the registered client, then applies method checks, IP checks, and policy enforcement before proxying to the backend.

Step 4: Configure the ingress for mTLS passthrough

At the nginx layer in front of Zerq:

ssl_client_certificate /etc/nginx/certs/tpp-ca-bundle.crt;
ssl_verify_client       on;

location / {
  proxy_pass          http://zerq-gateway:8080;
  proxy_set_header    X-Client-ID  $ssl_client_s_dn_cn;  # cert CN → client identity
  proxy_set_header    X-Profile-ID $ssl_client_s_dn_ou;  # cert OU → profile selector
}

ssl_verify_client on rejects any connection without a valid client certificate before the request reaches Zerq. X-Client-ID is populated from the certificate's Common Name and X-Profile-ID from the Organizational Unit, so the TPP's registered identifier travels with every request as a verified, ingress-extracted value, not a header the caller sets itself.

Step 5: Generate and deliver the client certificate to the TPP

# 1. Private key
openssl genrsa -out acme-payments-ltd.key 2048

# 2. CSR: CN maps to X-Client-ID, OU maps to X-Profile-ID
openssl req -new -key acme-payments-ltd.key \
  -out acme-payments-ltd.csr \
  -subj "/CN=acme-payments-ltd/OU=production-mtls/O=AcmeLtd/C=GB"

# 3. Sign with the bank's TPP CA
openssl x509 -req \
  -in acme-payments-ltd.csr \
  -CA tpp-ca.crt -CAkey tpp-ca.key \
  -CAcreateserial \
  -out acme-payments-ltd.crt \
  -days 365 -sha256

Deliver acme-payments-ltd.crt and acme-payments-ltd.key through your secure onboarding channel. The private key never touches Zerq's storage or configuration.

Step 6: Assign the client to the open banking collection

  1. Open the collection that groups your FAPI 2.0 endpoints (e.g. open-banking-psd3).
  2. Under Client access, add the TPP client and select production-mtls.
  3. Publish the collection.

The TPP can now access only the endpoints in that collection, using only the HTTP methods specified in the profile, from only its registered IP ranges, with its registered certificate.

What the audit trail looks like

Every request produces a structured log entry stored in your MongoDB instance. Here is what a successful account query from a registered TPP looks like:

{
  "request_id": "01JVQKR42M7X8PTNE3BSWD6FYK",
  "timestamp": "2026-08-20T09:14:32.187Z",
  "method": "GET",
  "path": "/accounts",
  "target_endpoint": "https://core-banking.internal/v2/accounts",
  "status_code": 200,
  "latency_ms": 143,
  "client_id": "acme-payments-ltd",
  "profile_id": "production-mtls",
  "collection": "open-banking-psd3",
  "client_ip": "185.12.0.100",
  "request_headers": {
    "X-Client-ID": "acme-payments-ltd",
    "X-Profile-ID": "production-mtls",
    "Accept": "application/json"
  },
  "response_status": 200
}

For a regulator reviewing this entry:

  • client_id is the registered TPP identity extracted from the certificate CN by ingress and verified against the Zerq client registry before the request proceeded.
  • profile_id tells the auditor which access contract was active: which methods were allowed, which IPs were permitted, what rate limit policy applied.
  • collection identifies the specific API product the TPP accessed.
  • latency_ms supports SLA reporting under PSD3's performance transparency obligations.
  • request_id correlates this entry with your SIEM export if you forward logs to Splunk, Elastic, or Microsoft Sentinel.

If the TPP attempts a POST to an AIS collection, the log shows status_code: 405 with client_id and profile_id intact. The enforcement action is the evidence.

For configuration changes (onboarding a new TPP, rotating a certificate, adjusting a rate limit policy), the admin audit log records: timestamp, actor ID (the administrator's OIDC subject), action type (CREATE/UPDATE/DELETE), the resource type and ID, and the complete request body. Compliance teams operating under the Auditor role can query these records without admin privileges, satisfying the separation of duties requirement that many regulators ask for explicitly.

OIDC token validation for FAPI 2.0 Baseline

For deployments where the authorization server issues short-lived access tokens validated at the gateway rather than certificate-bound at the transport layer, Zerq's OIDC profile type validates tokens against the authorization server's JWKS endpoint on every request.

Configure the profile with:

  • Auth type: oidc
  • Issuer: the authorization server's issuer URL (e.g. https://auth.bank.example.com)
  • Audience: the resource server identifier (e.g. https://api.bank.example.com/open-banking)
  • Allowed methods: GET for AIS, GET, POST for PIS
  • IP restrictions: the TPP's registered egress CIDR ranges

Zerq validates both issuer and audience on every request. A token from the correct issuer but with the wrong audience returns 401. A valid token from an IP not in the allowlist returns 403. Both events are logged with full client and profile context in the request log.

The OIDC and mTLS profile types are not mutually exclusive per client: a TPP can have an mTLS profile for production traffic and an OIDC profile for sandbox traffic, with different rate limit policies attached to each. Profile switching happens via X-Profile-ID.

→ See access control and authentication in Zerq for JWKS configuration details and the full auth method comparison.

What this looks like in practice

A regional bank in Germany had 52 registered TPPs across three environments: sandbox, staging, and production. Each TPP existed in three separate systems: the open banking directory, the bank's internal TPP registry, and the API gateway's configuration. When PSD3 audit preparation began, the compliance team found they could not produce a unified record of which TPP called which endpoint on a given date. The gateway logs used internal UUIDs that did not map to open banking directory identifiers without a manual join across two databases.

After moving to Zerq, each TPP was registered as a client with a client_id matching the TPP's open banking directory identifier. Every request log contains that client_id directly. Retrieving all account balance requests by Acme Payments Ltd between August 1 and August 15 became a single filter in the Zerq management UI: client acme-payments-ltd, collection open-banking-psd3, date range. The result was exportable to CSV in under two minutes.

Configuration changes (adding a new TPP, rotating a certificate, modifying a rate limit) appear in the admin audit log with the bank administrator's OIDC identity. The compliance team, operating under the Auditor role without admin rights, can retrieve those records independently. The evidence chain from "which TPP" to "what they called" to "who configured their access" is contained within one system that runs entirely inside the bank's infrastructure.

What a single platform changes for FAPI 2.0 compliance

A FAPI 2.0-compliant open banking deployment requires the certificate identity, the rate limit state, the method enforcement decision, and the full request log to appear in the same record. Assembling that from four separate tools produces gaps: the certificate identity in the load balancer log, the rate limit state in a separate Redis, the response code in the gateway log, the configuration history in a separate admin system.

Zerq keeps all of this together: every request log contains client identity, profile identity, collection, method, status, and latency. Every admin action is recorded in the audit log with full actor context. Both logs live in the bank's own MongoDB instance: no third-party control plane, no data leaving the perimeter, retention period set to match regulatory requirements.

For banks and payment institutions preparing for PSD3 inspection or responding to a regulatory request for evidence, the difference is between spending three days pulling records from four systems and spending thirty minutes producing them from one.

Open banking use cases | Security and access control | Observability and audit trail | Architecture overview


Zerq is an enterprise API gateway built for regulated industries — one platform for API management, AI agent access, compliance audit, and developer portal, running entirely in your own infrastructure. See how it works or request a demo to walk through your specific requirements.