Collecting PSD2 Banking Data
This guide explains how to collect PSD2 Account Information Service (AIS) data — checking accounts, savings accounts, credit cards, balances, and transactions — using the Insurely API.
You will learn:
- How users, sessions, consents, and collections work together for PSD2 data collection
- Two collection modes: one-time and consent-based (reusable)
- Step-by-step API call sequences with request/response examples
Prerequisites
All examples use the 2026-04-01 API version — include the Insurely-Version: 2026-04-01 header in every request.
Overview
PSD2 data collection involves four core concepts: Users, Sessions, Consents, and Collections.
- User — Created once per individual. Maps your
externalIdto auserUuidin Insurely. - Session — Created per interaction window. Provides a JWT that scopes operations to a user.
- Consent (consent-based only) — Created once per bank. Stores PSD2 authorization, reusable for multiple collections until expired or revoked. User authenticates with BankID once during consent creation.
- Collection — A single data fetch returning accounts, balances, and transactions. In the one-time flow, the user authenticates with BankID during the collection itself.
Authentication model
| Method | Header | Used For |
|---|---|---|
| API key | authorization-token | Backend-to-backend: creating users, sessions, querying collections/consents per user |
| JWT session token | Authorization: Bearer <JWT> | User-scoped: creating consents, polling consent status, starting collections |
Key distinction
Consent endpoints (/consents/*) require JWT authentication. Collection endpoints (/collections/*) accept both API key and JWT.
User management
The User API lets you create and manage users that map to individuals in your system. Each user is identified by an externalId (your own identifier) and receives a userUuid within Insurely.
Create a user — Call POST /users with an externalId that maps to your internal user identifier. You receive a UserResponse containing the userUuid.
curl -X POST https://api.insurely.com/users \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01" \
-H "Content-Type: application/json" \
-d '{ "externalId": "your-internal-user-id" }'{
"userUuid": "abc-123",
"externalId": "your-internal-user-id",
"anonymized": false,
"createdAt": "2026-03-05T10:00:00Z",
"updatedAt": "2026-03-05T10:00:00Z"
}Create a session — Call POST /users/{userUuid}/sessions to obtain a JWT token. The SessionResponse includes the token and its expiresAt timestamp.
curl -X POST https://api.insurely.com/users/abc-123/sessions \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01"{
"token": "eyJhbG...",
"userUuid": "abc-123",
"expiresAt": "2026-03-05T12:00:00Z"
}Use the JWT for user-scoped calls — Pass the JWT as Authorization: Bearer <JWT> when creating consents, starting collections, or polling statuses.
You can look up an existing user by their externalId using GET /users?externalId=..., or retrieve a user directly by userUuid using GET /users/{userUuid}. To delete a user, call DELETE /users/{userUuid} — this anonymizes the user by setting externalId to null.
Consent lifecycle
A consent represents explicit PSD2 user authorization. It is created via POST /consents and its status is polled via GET /consents/{consentId}/status.
INITIATED → AWAITING_AUTHORIZATION → AUTHORIZED
→ FAILED
→ EXPIRED (after expiresAt)
→ REVOKED (via revoke endpoint)The CreateConsentRequest specifies the company, loginMethod, and parameters including a Psd2AisConsentParameter defining the scope (accountIds, balances, transactions) plus PSD2-required fields (psuIpAddress, optional psuLocale, optional validUntil).
During AWAITING_AUTHORIZATION, the ConsentStatusResponse includes authInstructions — either a SwedishBankIdQr with qrCodeData (for QR code flow) or a SwedishBankIdAutostartToken with autoStartToken (for same-device flow). The pollingInterval field (milliseconds) indicates how often to poll.
Once AUTHORIZED, the consent can be used to start one or more collections until it expires or is revoked via DELETE /consents/{consentId}.
Two Ways to Collect PSD2 Data
Option A: One-Time Collection (Direct Authentication)
Use this when you need to fetch PSD2 data once and do not need to re-collect later without user interaction. The user authenticates directly during the collection flow — no separate consent is created or stored.
When to use:
- Single data fetch (e.g., onboarding snapshot)
- You don't need recurring access
- You want the simplest integration path
Check company availability — Call GET /companies/availability to verify the target company supports PSD2 AIS and is currently available.
curl https://api.insurely.com/companies/availability \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01"Start collection — Call POST /collections with a SwedishBankIdParameter and a Psd2AisConsentParameter defining the scope.
curl -X POST https://api.insurely.com/collections \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01" \
-H "Content-Type: application/json" \
-d '{
"company": "se-demo-psd2",
"loginMethod": "SWEDISH_MOBILE_BANKID_OTHER_DEVICE",
"parameters": [
{
"type": "SWEDISH_BANKID",
"personalNumber": "200001012384"
},
{
"type": "PSD2_AIS_CONSENT",
"accountIds": [],
"balances": true,
"transactions": true,
}
]
}'{
"id": "coll-456",
"status": "RUNNING",
"company": "se-demo-psd2",
"pollingTimeout": "2026-03-05T10:05:00Z",
"extraInformation": null
}Poll collection status — Poll GET /wealth/collection/{collectionId}/status until the status is terminal. During WAITING_FOR_USER_ACTION, present BankID authentication to the user using the extraInformation field.
curl https://api.insurely.com/wealth/collection/coll-456/status \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01"{
"id": "coll-456",
"status":"WAITING_FOR_USER_ACTION",
"company":"se-demo-psd2",
"extraInformation": {
"SWEDISH_BANKID_QRCODE": "BASE_64_ENCODED_QR_CODE",
"SWEDISH_MOBILE_BANKID_ANIMATED_QR_DATA": "BANK_ID_TOKEN CODE"
}
}Respect pollingTimeout — poll before this timestamp or the collection may be terminated. Recommended interval: 1–2 seconds.
Retrieve data — Call GET /collections/{collectionId}/wealth/data once the collection reaches COMPLETED, COMPLETED_PARTIAL, or COMPLETED_EMPTY.
curl https://api.insurely.com/collections/coll-456/wealth/data \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01"Returns an array of FinancialProduct objects (accounts, balances, transactions).
Notes
- A short-lived consent is created during the collection process which will be automatically deleted once the collection is completed.
- Failure to create a consent will result in the collection being marked as
FAILED.
Sequence Diagram
Option B: Consent-Based Collection (Reusable)
Use this when you need recurring access to PSD2 data. You create and authorize a consent first, then use that consent for one or more collections without requiring the user to re-authenticate each time (until the consent expires or is revoked).
When to use:
- Recurring data fetches (e.g., daily balance checks, weekly transaction syncs)
- You want to separate the authorization step from data fetching
- You need to refresh data without user interaction
Create a user and session first if you haven't already. The consent and collection steps below require a JWT. You only need to create a user once, and then it can be reused to create a new session.
Check company availability — Call GET /companies/availability to verify the target company supports PSD2 AIS.
curl https://api.insurely.com/companies/availability \
-H "authorization-token: <API_KEY>" \
-H "Insurely-Version: 2026-04-01"Create consent — Call POST /consents with a Psd2AisConsentParameter and optionally a SwedishBankIdParameter.
curl -X POST https://api.insurely.com/consents \
-H "Authorization: Bearer <JWT>" \
-H "Insurely-Version: 2026-04-01" \
-H "Content-Type: application/json" \
-d '{
"company": "se-demo-psd2",
"loginMethod": "SWEDISH_MOBILE_BANKID_OTHER_DEVICE",
"parameters": [
{
"type": "PSD2_AIS_CONSENT",
"accountIds": [],
"balances": true,
"transactions": true,
"psuIpAddress": "192.168.1.1",
"psuLocale": "sv-SE",
"validUntil": "2026-05-31"
},
{
"type": "SWEDISH_BANKID",
"personalNumber": "200001012384"
}
]
}'Note: Some companies don't accept validity periods beyond 6 months
{
"consentId": "consent-789",
"company": "se-demo-psd2",
"status": "INITIATED",
"scope": {
"type": "PSD2_AIS",
"accountIds": [],
"balances": true,
"transactions": true
},
"expiresAt": "2026-05-31T23:59:59Z"
}Poll consent status and handle BankID authentication — Poll GET /consents/{consentId}/status until the status reaches a terminal state.
curl https://api.insurely.com/consents/consent-789/status \
-H "Authorization: Bearer <JWT>" \
-H "Insurely-Version: 2026-04-01"During AWAITING_AUTHORIZATION, the ConsentStatusResponse contains authInstructions:
- QR flow:
{ "type": "SWEDISH_BANKID_QR", "qrCodeData": "bankid.xxxx..." }— render the QR code for the user to scan. QR data changes on each poll — always use the latest. - Same-device flow:
{ "type": "SWEDISH_BANKID_AUTOSTART_TOKEN", "autoStartToken": "uuid" }— launch BankID app withbankid:///?autostarttoken=<token>&redirect=null
Respect the pollingInterval (milliseconds) from the response. Typically 1–2 seconds. Terminal states: AUTHORIZED (success), FAILED (check error.code and error.message), EXPIRED, REVOKED.
Start collection using the authorized consent — Call POST /collections with a StoredConsentParameter referencing the consent.
curl -X POST https://api.insurely.com/collections \
-H "Authorization: Bearer <JWT>" \
-H "Insurely-Version: 2026-04-01" \
-H "Content-Type: application/json" \
-d '{
"company": "se-demo-psd2",
"parameters": [
{
"type": "STORED_CONSENT",
"consentId": "consent-789",
"psuIpAddress": "192.168.1.1",
"psuUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}
]
}'{
"id": "coll-456",
"status": "RUNNING",
"company": "se-demo-psd2",
"pollingTimeout": "2026-03-05T10:05:00Z",
"extraInformation": null
}No BankID interaction is needed — the consent's stored credentials are used.
Poll collection status — Poll GET /wealth/collection/{collectionId}/status until terminal. With a stored consent, the collection typically goes RUNNING → COLLECTING → COMPLETED without user interaction.
curl https://api.insurely.com/wealth/collection/coll-456/status \
-H "Authorization: Bearer <JWT>" \
-H "Insurely-Version: 2026-04-01"Retrieve data — Call GET /collections/{collectionId}/wealth/data once the collection reaches a terminal status.
curl https://api.insurely.com/collections/coll-456/wealth/data \
-H "Authorization: Bearer <JWT>" \
-H "Insurely-Version: 2026-04-01"Returns an array of FinancialProduct objects.
Reusing a consent
Repeat steps 4–6 with the same consentId. No new BankID authorization is needed as long as the consent is still AUTHORIZED and not expired. If the consent has expired, create a new one (back to step 2).
Sequence Diagram
Consent Management
Listing Consents
- As the user (
Authorization: Bearer): GET /consents — returns all consents for the authenticated user - As the backend (
authorization-token): GET /users/{id}/consents — returns all consents for a specific user
Revoking a Consent
Revoke a previously authorized consent via DELETE /consents/{consentId}:
curl -X DELETE https://api.insurely.com/consents/consent-789 \
-H "Authorization: Bearer <JWT>" \
-H "Insurely-Version: 2026-04-01"- The revocation is forwarded to the bank (ASPSP)
- Once revoked, the consent cannot be used for new collections
Consent Expiration
- The
validUntilfield in the create request is a requested expiration date; the bank may enforce a shorter period - The actual expiration is in the
expiresAtfield of the ConsentStatusResponse - Expired consents cannot be used — create a new consent when the previous one expires
- Check consent status before starting a collection to avoid using an expired consent
Listing Collections
- As the user (
Authorization: Bearer): GET /collections — returns all CollectionSummary objects for the authenticated user- Optional query params:
latest=true(latest per company),statusIn=COMPLETED,FAILED(filter by status)
- Optional query params:
- As the backend (
authorization-token): GET /users/{id}/collections — returns all collections for a specific user
Choosing Between One-Time and Consent-Based
| One-Time | Consent-Based | |
|---|---|---|
| User interaction | Required every time | Only on initial consent creation |
| Best for | Single snapshots, onboarding | Recurring fetches, background refresh |
| BankID auth | During collection | During consent creation only |
| Reusable | No | Yes, until consent expires or is revoked |
| Complexity | Lower (fewer API calls) | Higher (consent lifecycle management) |
| Consent expiration | N/A | Must handle expiry and re-consent |
| API calls for first fetch | 2 (start + poll/data) | 6 (user + session + consent + poll consent + start + poll/data) |
| API calls for subsequent fetch | 2 (same as first) | 2 (start + poll/data, reuse consent) |
Important Notes
PSU Presence (PSD2 Compliance)
PSD2 requires signaling whether the end user (PSU — Payment Service User) is present when interacting with the bank. This affects how the bank processes the request — without PSU presence fields, the request may be treated as a background refresh, which some banks handle differently or reject.
When creating a consent — include PSU fields in the Psd2AisConsentParameter:
| Field | Required | Description |
|---|---|---|
psuIpAddress | Yes | The end user's IP address (IPv4 or IPv6). |
psuLocale | Optional | Locale string in xx-YY format (e.g., sv-SE, en-GB). Used for localization of consent flows. |
validUntil | Optional | Requested consent expiration date in YYYY-MM-DD format. The bank may enforce a shorter period. |
When starting a collection with a stored consent — include PSU fields in the StoredConsentParameter to signal that the user is present:
| Field | Required | Description |
|---|---|---|
psuIpAddress | Optional | The end user's IP address. Set this when the user is actively requesting the data fetch. |
psuUserAgent | Optional | The end user's browser or device User-Agent string. Set this when the user is actively requesting the data fetch. |
If the collection is a background refresh (no user present), omit both fields.
When starting a One-Time Collection collection without a stored consent — you can omit the PSU fields, they are optional.
Account Scoping
The accountIds field in the Psd2AisConsentParameter controls which accounts the consent/collection covers:
- Empty array or omitted: Consent applies to all accounts the user has at the bank
- Specific IDs: Consent applies only to those accounts
Swedish BankID Authentication Variants
For Swedish companies, two BankID flows are available:
| Login Method | Auth Instructions Type | User Experience |
|---|---|---|
SWEDISH_MOBILE_BANKID_OTHER_DEVICE | SwedishBankIdQr | User scans QR code with BankID app on another device. QR data refreshes on each poll — always render the latest. |
SWEDISH_MOBILE_BANKID_SAME_DEVICE | SwedishBankIdAutostartToken | Client launches BankID app directly via bankid:/// URI with the autostart token. |
Error Handling
Consent errors (when status is FAILED):
- The ConsentStatusError object contains
code(e.g.,AUTHENTICATION_TIMEOUT) and a human-readablemessage
Collection errors:
- Terminal failure statuses include:
FAILED,AUTHENTICATION_TIMEOUT,INCORRECT_CREDENTIALS,AUTHENTICATION_CANCELLED,AUTHENTICATION_CONFLICT,THIRD_PARTY_ERROR,ACCOUNT_TEMPORARILY_LOCKED - For
THIRD_PARTY_ERROR, the issue is on the bank's side — retry later - See Collection statuses for the full list with recommended actions
Polling Best Practices
- Consent polling: Use the
pollingIntervalfield from the ConsentStatusResponse (in milliseconds). Typically 1–2 seconds. - Collection polling: Respect the
pollingTimeouttimestamp in the CollectionStatus — poll before this time or the collection may be terminated. Recommended interval: 1–2 seconds. - Do not poll more aggressively than indicated — excessive polling may trigger rate limiting (HTTP 429).
Last updated on