Migration guide

Restructured fees object (from 2025-01-01)

In 2025-01-01 and earlier, Fees carried flat fields whose meaning depended on the feesCollected boolean: true meant the figures were personalized to the customer (with their actual discounts), false meant generic / list-price fallback figures. 2026-04-01 splits these two modes into separate, mutually-exclusive sub-objects and removes the flag.

  • fees.personalized (PersonalizedFees) — values personalized to the holder, including the holder's actual discounts. Present when the source exposes per-holder fee data.
  • fees.generic (GenericFees) — generic / list-price mapping, not personalized to the holder and likely missing discounts. Present when per-holder fee data is unavailable.
  • Exactly one of fees.personalized / fees.generic is present per response; its presence is the personalized-vs-generic signal.
  • previousYear (last year's actual total fees paid) exists only on personalized; it is not meaningful for the generic / list-price side.
  • The fees object is absent entirely when no fee data — neither personalized nor generic — is available for the product.
  • variable remains a decimal ratio in [0, 1] (e.g. 0.0065 = 0.65%) — same representation as the old feeVariable, no unit change.
// personalized — per-holder figures with actual discounts
{
  "fees": {
    "personalized": {
      "fixed":        { "amount": 100, "currency": "SEK" },
      "variable":     0.0065,
      "previousYear": { "amount": 513, "currency": "SEK" }
    }
  }
}

// generic — list-price mapping, no previousYear
{
  "fees": {
    "generic": {
      "fixed":    { "amount": 100, "currency": "SEK" },
      "variable": 0.0065
    }
  }
}

Field mapping

2025-01-012026-04-01
fees.feesCollected: truevalues move under fees.personalized (object present)
fees.feesCollected: falsevalues move under fees.generic (object present)
fees.feeFixedfees.personalized.fixed or fees.generic.fixed
fees.feeVariablefees.personalized.variable or fees.generic.variable (units unchanged)
fees.feesPreviousYearfees.personalized.previousYear only (dropped on the generic branch)
fees.feesCollected (the flag)removed — replaced by which sub-object is present

Steps

  1. Replace the feesCollected branch with a presence check. Instead of reading fees.feesCollected and then fees.feeFixed / fees.feeVariable, check which sub-object exists: fees.personalized present → use personalized figures; fees.generic present → use generic figures; fees absent → no fee data for this product.
  2. Rename the leaf fields: feeFixedfixed, feeVariablevariable (now nested under the chosen sub-object). variable semantics and units are unchanged.
  3. feesPreviousYearpersonalized.previousYear. It is only available on the personalized branch. The previous feesCollected: false + feesPreviousYear combination no longer exists — generic responses carry no previous-year total.
  4. Handle the absent fees object. Earlier versions effectively always returned a fees object; 2026-04-01 omits it when there is no fee data, so guard for fees being undefined.
// 2025-01-01
const fees = product.fees;
const personalized = fees.feesCollected === true;
const fixed = fees.feeFixed;
const variable = fees.feeVariable;

// 2026-04-01
const fees = product.fees;                             // may be undefined
const personalized = !!fees?.personalized;
const figures = fees?.personalized ?? fees?.generic;   // undefined if no fee data
const fixed = figures?.fixed;
const variable = figures?.variable;
const previousYear = fees?.personalized?.previousYear; // personalized only

Collections are now a top-level resource (from 2025-01-01)

In 2025-01-01 a wealth collection lived under the product namespace: it was created at POST /wealth/collection and read under /wealth/collection/{collectionId}/... (insurance collections lived under /insurance/collection/...). In 2026-04-01 collections become a single shared, top-level resource. A collection is created at POST /collections regardless of product, and the wealth data is read from a product sub-path — /collections/{collectionId}/wealth/data, mirroring the Insurance API's /collections/{collectionId}/insurance/data.

Endpoint mapping

2025-01-01 (wealth)2026-04-01
POST /wealth/collectionPOST /collections (shared across products)
GET /wealth/collection/{collectionId}/statusGET /collections/{collectionId}/status
GET /wealth/collection/{collectionId}/dataGET /collections/{collectionId}/wealth/data
POST /wealth/collection/{collectionId}/supplement-infoPOST /collections/{collectionId}/supplement-info

Typed request parameters

POST /collections replaces the untyped 2025-01-01 request body — a loginMethod plus an input map keyed by uppercase strings, with separate top-level consents and filter arrays — with a single typed parameters array. Each entry carries a type discriminator and its own named, lower-camelCase fields.

  • Credentials become typed parameters (SWEDISH_BANKID, EMAIL) instead of keys in input.
  • Consents are explicit, typed parameters. PSD2 Account Information Service consent is new in this version — supply it as a Psd2AisConsentParameter (type: PSD2_AIS_CONSENT), or reference a previously-authorized consent with a StoredConsentParameter. This replaces the untyped consents array.
  • SST / session identifiers (SESSION_ID, ADVISOR_HANDLE, EXTERNAL_REFERENCE) move from inline input keys into a SESSION_METADATA parameter's attributes map.
// 2025-01-01
{
  "company": "se-avanza",
  "loginMethod": "SWEDISH_MOBILE_BANKID_OTHER_DEVICE",
  "input": { "SWEDISH_PERSONAL_NUMBER": "199001011234" }
}

// 2026-04-01
{
  "company": "se-avanza",
  "loginMethod": "SWEDISH_MOBILE_BANKID_OTHER_DEVICE",
  "parameters": [
    { "type": "SWEDISH_BANKID", "personalNumber": "199001011234" }
  ]
}

Login methods (Sweden)

The loginMethod enum values are unchanged; only the shape of the credential payload changes. Each method now carries its credential in a typed parameter rather than in the input map:

Login method2025-01-01 input2026-04-01 parameter
SWEDISH_MOBILE_BANKID_SAME_DEVICE / …_OTHER_DEVICE / …_ANY_DEVICE / …_SAME_DEVICE_CLIENT_SIDE_AUTHENTICATION / SWEDISH_SECURITY_TOKEN{ "SWEDISH_PERSONAL_NUMBER": "…" }{ "type": "SWEDISH_BANKID", "personalNumber": "…" }
EMAIL{ "USERNAME": "…" }{ "type": "EMAIL", "email": "…" }

All Swedish BankID variants — including the _TEST (BankID sandbox) and _MOCK (deterministic, short-circuited) flows — reuse the same SwedishBankIdParameter (type: SWEDISH_BANKID); switch environments by changing only the loginMethod.

Steps

  1. Repoint the base path. Replace /wealth/collection with /collections, and read data from /collections/{collectionId}/wealth/data instead of /wealth/collection/{collectionId}/data. Status and supplement-info keep the same suffixes under the new prefix.
  2. Convert the request body. Move each input key into a typed entry in the parameters array (e.g. SWEDISH_PERSONAL_NUMBER{ "type": "SWEDISH_BANKID", "personalNumber": … }).
  3. Move consents and session data into parameters. Replace the consents array with the relevant typed consent parameter, and move SESSION_ID / ADVISOR_HANDLE / EXTERNAL_REFERENCE into a SESSION_METADATA parameter's attributes.