Examples
PSD2 Flow examples
Full end-to-end scripts for both collection flows. Replace YOUR_API_KEY_HERE with your API key before running.
Prerequisites
Node.js 21+
#!/usr/bin/env node
// ============================================================
// PSD2 One-Time Collection Script
// ============================================================
const BASE_URL = "https://api.insurely.com";
const API_KEY = "YOUR_API_KEY_HERE";
const COMPANY = "se-demo-psd2";
const LOGIN_METHOD = "SWEDISH_MOBILE_BANKID_OTHER_DEVICE";
const PERSONAL_NUMBER = "200001012384";
const VERSION = "2026-04-01";
const backendHeaders = {
"authorization-token": API_KEY,
"Insurely-Version": VERSION,
"Content-Type": "application/json",
};
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function checkAvailability() {
console.log("==> Step 1: Checking company availability...");
const res = await fetch(`${BASE_URL}/companies/availability`, {
headers: backendHeaders,
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
}
async function startCollection() {
console.log("\n==> Step 2: Starting collection...");
const res = await fetch(`${BASE_URL}/collections`, {
method: "POST",
headers: backendHeaders,
body: JSON.stringify({
company: COMPANY,
loginMethod: LOGIN_METHOD,
parameters: [
{
type: "SWEDISH_BANKID",
personalNumber: PERSONAL_NUMBER,
},
{
type: "PSD2_AIS_CONSENT",
accountIds: [],
balances: true,
transactions: true,
},
],
}),
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
return data.id;
}
async function pollCollectionStatus(collectionId) {
console.log(
"\n==> Step 3: Polling collection status (scan QR when prompted)...",
);
const deadline = Date.now() + 5 * 60 * 1000;
while (Date.now() < deadline) {
const res = await fetch(
`${BASE_URL}/collections/${collectionId}/status`,
{ headers: backendHeaders },
);
const data = await res.json();
const status = data.status;
console.log(`Status: ${status}`);
if (status === "WAITING_FOR_USER_ACTION") {
const qrData =
data.extraInformation?.SWEDISH_MOBILE_BANKID_ANIMATED_QR_DATA;
if (qrData) {
console.log(`BankID QR Data: ${qrData}`);
}
}
if (
["COMPLETED", "COMPLETED_PARTIAL", "COMPLETED_EMPTY"].includes(status)
) {
console.log("Collection completed!");
return;
}
if (status === "FAILED") {
console.error("Collection FAILED:", JSON.stringify(data, null, 2));
process.exit(1);
}
await sleep(2000);
}
throw new Error("Collection polling timed out after 5 minutes.");
}
async function retrieveData(collectionId) {
console.log("\n==> Step 4: Retrieving collected data...");
const res = await fetch(
`${BASE_URL}/collections/${collectionId}/wealth/data`,
{ headers: backendHeaders },
);
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
}
async function main() {
await checkAvailability();
const collectionId = await startCollection();
await pollCollectionStatus(collectionId);
await retrieveData(collectionId);
}
main().catch((err) => {
console.error("Unexpected error:", err);
process.exit(1);
});#!/usr/bin/env node
// ============================================================
// PSD2 Consent-Based Collection Script
// ============================================================
const BASE_URL = "https://api.insurely.com";
const API_KEY = "YOUR_API_KEY_HERE";
const COMPANY = "se-demo-psd2";
const LOGIN_METHOD = "SWEDISH_MOBILE_BANKID_OTHER_DEVICE";
const PERSONAL_NUMBER = "200001012384"; // Valid personal number
const EXTERNAL_ID = "200001012384"; // Valid personal number
const VERSION = "2026-04-01";
const PSU_IP = "192.168.1.1";
const PSU_LOCALE = "sv-SE";
const VALID_UNTIL = (() => {
const _validUntil = new Date();
_validUntil.setMonth(_validUntil.getMonth() + 5);
return _validUntil.toISOString().split('T')[0]; // PSD2 doesn't accept periods longer than 6 months
})();
const PSU_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)";
const backendHeaders = {
"authorization-token": API_KEY,
"Insurely-Version": VERSION,
"Content-Type": "application/json",
};
function userHeaders(jwt) {
return {
Authorization: `Bearer ${jwt}`,
"Insurely-Version": VERSION,
"Content-Type": "application/json",
};
}
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// ── Step 1: Check company availability ──────────────────────
async function checkAvailability() {
console.log("==> Step 1: Checking company availability...");
const res = await fetch(`${BASE_URL}/companies/availability`, {
headers: backendHeaders,
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
}
// ── Step 2: Look up or create user ──────────────────────────
async function getOrCreateUser() {
console.log("\n==> Step 2: Looking up or creating user...");
const lookupRes = await fetch(`${BASE_URL}/users?externalId=${EXTERNAL_ID}`, {
headers: backendHeaders,
});
const lookupData = await lookupRes.json();
if (lookupData.userUuid) {
console.log(`Found existing user: ${lookupData.userUuid}`);
return lookupData.userUuid;
}
console.log("User not found. Creating user...");
const createRes = await fetch(`${BASE_URL}/users`, {
method: "POST",
headers: backendHeaders,
body: JSON.stringify({ externalId: EXTERNAL_ID }),
});
const createData = await createRes.json();
console.log(JSON.stringify(createData, null, 2));
console.log(`Created user: ${createData.userUuid}`);
return createData.userUuid;
}
// ── Step 3: Create session ───────────────────────────────────
async function createSession(userUuid) {
console.log("\n==> Step 3: Creating session...");
const res = await fetch(`${BASE_URL}/users/${userUuid}/sessions`, {
method: "POST",
headers: backendHeaders,
});
const data = await res.json();
console.log("Session token obtained.");
return data.token;
}
// ── Step 4: Create consent ───────────────────────────────────
async function createConsent(jwt) {
console.log("\n==> Step 4: Creating consent...");
const res = await fetch(`${BASE_URL}/consents`, {
method: "POST",
headers: userHeaders(jwt),
body: JSON.stringify({
company: COMPANY,
loginMethod: LOGIN_METHOD,
parameters: [
{
type: "PSD2_AIS_CONSENT",
accountIds: [],
balances: true,
transactions: true,
psuIpAddress: PSU_IP,
psuLocale: PSU_LOCALE,
validUntil: VALID_UNTIL,
},
{
type: "SWEDISH_BANKID",
personalNumber: PERSONAL_NUMBER,
},
],
}),
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
return data.consentId;
}
// ── Step 5: Poll consent status ──────────────────────────────
async function pollConsentStatus(consentId, jwt) {
console.log(
"\n==> Step 5: Polling consent status (scan QR when prompted)...",
);
const deadline = Date.now() + 5 * 60 * 1000;
while (Date.now() < deadline) {
const res = await fetch(`${BASE_URL}/consents/${consentId}/status`, {
headers: userHeaders(jwt),
});
const data = await res.json();
const status = data.status;
console.log(`Consent status: ${status}`);
if (status === "AWAITING_AUTHORIZATION") {
const authType = data.authInstructions?.type;
if (authType === "SWEDISH_BANKID_QR") {
console.log(`BankID QR Data: ${data.authInstructions.qrCodeData}`);
} else if (authType === "SWEDISH_BANKID_AUTOSTART_TOKEN") {
console.log(
`BankID autostart URL: bankid:///?autostarttoken=${data.authInstructions.autoStartToken}&redirect=null`,
);
}
}
if (status === "AUTHORIZED") {
console.log("Consent authorized!");
return;
}
if (["FAILED", "EXPIRED", "REVOKED"].includes(status)) {
console.error(`Consent ended with status: ${status}`);
console.error(JSON.stringify(data, null, 2));
process.exit(1);
}
const pollingInterval = data.pollingInterval ?? 2000;
await sleep(pollingInterval);
}
throw new Error("Consent polling timed out after 5 minutes.");
}
// ── Step 6: Start collection with stored consent ─────────────
async function startCollection(consentId, jwt) {
console.log("\n==> Step 6: Starting collection with stored consent...");
const res = await fetch(`${BASE_URL}/collections`, {
method: "POST",
headers: userHeaders(jwt),
body: JSON.stringify({
company: COMPANY,
parameters: [
{
type: "STORED_CONSENT",
consentId: consentId,
psuIpAddress: PSU_IP,
psuUserAgent: PSU_USER_AGENT,
},
],
}),
});
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
return data.id;
}
// ── Step 7: Poll collection status ───────────────────────────
async function pollCollectionStatus(collectionId, jwt) {
console.log("\n==> Step 7: Polling collection status...");
const deadline = Date.now() + 5 * 60 * 1000;
while (Date.now() < deadline) {
const res = await fetch(`${BASE_URL}/collections/${collectionId}/status`,
{ headers: backendHeaders },
);
const data = await res.json();
const status = data.status;
console.log(`Status: ${status}`);
if (
["COMPLETED", "COMPLETED_PARTIAL", "COMPLETED_EMPTY"].includes(status)
) {
console.log("Collection completed!");
return;
}
if (status === "FAILED") {
console.error("Collection FAILED:", JSON.stringify(data, null, 2));
process.exit(1);
}
await sleep(2000);
}
throw new Error("Collection polling timed out after 5 minutes.");
}
// ── Step 8: Retrieve data ────────────────────────────────────
async function retrieveData(collectionId, jwt) {
console.log("\n==> Step 8: Retrieving collected data...");
const res = await fetch(
`${BASE_URL}/collections/${collectionId}/wealth/data`,
{ headers: userHeaders(jwt) },
);
const data = await res.json();
console.log(JSON.stringify(data, null, 2));
}
async function main() {
await checkAvailability();
const userUuid = await getOrCreateUser();
const jwt = await createSession(userUuid);
const consentId = await createConsent(jwt);
await pollConsentStatus(consentId, jwt);
const collectionId = await startCollection(consentId, jwt);
await pollCollectionStatus(collectionId, jwt);
await retrieveData(collectionId, jwt);
}
main().catch((err) => {
console.error("Unexpected error:", err);
process.exit(1);
});Last updated on