Getting started
The PaySaxas XS2A API implements the Berlin Group NextGenPSD2 XS2A Implementation Guidelines v1.3.15 (JSON). This page takes you from a certificate to a first account read.
Who can connect
Any firm authorised under PSD2 as an AISP, PISP or CBPII in an EEA member state, holding a valid eIDAS QWAC that carries its PSD2 role attributes.
There is nothing to apply for. No contract, no fee, no onboarding form, no “contact us first” step — RTS 2018/389 Art. 32(3) forbids all of them. Your first request with a valid certificate registers you automatically.
We do corroborate your authorisation against the EBA/NCA register when you first appear and periodically after that. If your home regulator withdraws your authorisation, your access stops — that is the register speaking, not us.
Base URLs
| Environment | Host |
|---|---|
| Sandbox — start here | https://xs2a.paysaxas.money |
| Production | https://xs2a.paysaxas.com |
The sandbox is also listed first in the OpenAPI servers block, so a generated
client points at it by default rather than at production.
Both terminate mutual TLS. A request without a client certificate is refused
with CERTIFICATE_MISSING, whatever else it contains.
Start in the sandbox — see sandbox.md.
Headers you will always send
| Header | Required | Meaning |
|---|---|---|
X-Request-ID |
Yes | UUID. Echoed back. The idempotency key for payment initiation. |
Consent-ID |
On account data and CoF | The consent you are acting under |
PSU-IP-Address |
When the customer is present | Marks the request attended — see below |
TPP-Redirect-URI |
Yes, on consent and payment creation | Where we return the customer. Absolute https, no fragment — anything else is a FORMAT_ERROR |
TPP-NOK-Redirect-URI |
Optional | Where we return them if they decline |
Attended vs unattended
PSU-IP-Address is not bookkeeping. Sending it says “the customer is at their
screen right now”, and those requests are uncapped. Requests without it are
unattended and limited to four per consent, per data type, per day (RTS
Art. 36(5)). Send it when it is true, and do not send it when it is not.
Three requests, end to end
1. Create a consent
curl -X POST https://xs2a.paysaxas.com/v1/consents \
--cert tpp-qwac.pem --key tpp-qwac.key \
-H "Content-Type: application/json" \
-H "X-Request-ID: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721" \
-H "TPP-Redirect-URI: https://your-app.example/psd2/return" \
-H "PSU-IP-Address: 203.0.113.9" \
-d '{
"access": {
"accounts": [{"iban": "FI2112345600000785"}],
"balances": [{"iban": "FI2112345600000785"}],
"transactions": [{"iban": "FI2112345600000785"}]
},
"recurringIndicator": true,
"validUntil": "2026-12-31",
"frequencyPerDay": 4
}'
{
"consentId": "PSXS-CONSENT-3f2b...",
"consentStatus": "received",
"_links": {
"scaRedirect": { "href": "https://platform.paysaxas.com/customer/xs2a/authorise/AUTH-...?state=..." },
"status": { "href": "/v1/consents/PSXS-CONSENT-3f2b.../status" }
}
}
The consent does not exist yet. received means “asked for, not granted”.
Note that we did not tell you whether we hold that IBAN. At this point nobody has authenticated, so answering would disclose facts about accounts no consent covers. A well-formed IBAN we do not hold is accepted exactly like one we do.
2. Send the customer to scaRedirect
They authenticate on our surface and see exactly what you asked for: your name,
the data types, the accounts, the validity. They approve or decline, and we
return them to your TPP-Redirect-URI with the state we generated. See what state is and how long it lasts.
You never see their credentials. There is no embedded or decoupled alternative — see sca-redirect-flow.md.
3. Read the accounts
curl https://xs2a.paysaxas.com/v1/accounts \
--cert tpp-qwac.pem --key tpp-qwac.key \
-H "X-Request-ID: 6d2b1e64-8a0f-4e0e-9f0c-2b0a2c9b1f11" \
-H "Consent-ID: PSXS-CONSENT-3f2b..." \
-H "PSU-IP-Address: 203.0.113.9"
{
"accounts": [
{
"resourceId": "0f0f...",
"iban": "FI2112345600000785",
"currency": "EUR",
"name": "Operating EUR",
"product": "current_account",
"cashAccountType": "CACC"
}
]
}
Only the accounts the customer actually holds and consented to appear. If you asked about others, they are silently absent rather than reported.
resourceId is what you address the account by. It is the {account_id}
path parameter for everything account-scoped:
GET /v1/accounts/0f0f.../balances
GET /v1/accounts/0f0f.../transactions
It is opaque, stable for the life of the account, and not the IBAN — an IBAN in that position is not recognised. Read it from the listing rather than constructing or storing a path.
What to build next
- consents.md — lifetime, renewal, revocation
- payments.md — initiating SEPA credit transfers
- funds-confirmations.md — the CBPII yes/no
- error-catalogue.md — every code we emit and what to do
Things that commonly surprise integrators
- A 4xx is often the interface working. A refused consent is an answer, not an outage. Our published availability figures count 5xx only, and you should treat the two differently as well.
- Some refusals are identical on purpose. An unknown consent and another provider’s consent return byte-identical responses. Do not build logic that depends on telling them apart — there is nothing to tell.
- Consents expire on a clock you can see. 180 days maximum from the
customer’s authentication. Read
validUntilon the consent and renew before it, rather than waiting forCONSENT_EXPIRED. - Transaction history is paged — follow
_links.next. A response carries at most 250 rows. When more history exists,transactions._links.next.hrefis present and already carries yourbookingStatusand date filters; follow it until it is absent, which is the end. It is omitted rather than emptied, so there is nothing to poll. If you would rather build URLs yourself, every transaction carries anentryReferenceyou can pass back asentryReferenceFrom. Paging is keyset-based: a payment arriving mid-traversal cannot shift a page boundary, so you will not see a duplicate or miss a row. bookedandpendingare different money. OnGET /v1/accounts/{id}/transactions,bookingStatusselects which arrays come back —booked(the default),pending, orboth. A pending transaction is authorised but not yet booked and can still fail; do not reconcile against it. A payment the customer cancelled, or that we refused, appears in neither array: no money moved, so there is nothing on the account to report.