Which APIs are affected by this migration?
- Legacy: Payments API v1.3.2 (multi-country)
- New:
- Payments SE Private API v2.0.0
- Payments SE Corporate API v2.0.0
The Accounts API (v2.1.30) and Subscriptions API (v1.0.22) are not part of this migration.
Payments SE Corporate API v2.0.0Where do I start?
- Review the new OpenAPI specifications on the Developer Portal.
- Subscribe your app to the relevant product (
payments-se-privateand/orpayments-se-corporate) via the Subscriptions API. - Develop and test in the Sandbox environment.
- Use the Go Live process on the Developer Portal when ready for production.
What's the recommended migration path?
- Subscribe your existing app to the new products via the Subscriptions API:
payments-se-private(PIS scope)payments-se-corporate(PIS scope)
- Update base URL from
/openbanking/psd2/v1to/openbanking/psd2/se/v2. - Split your code paths by customer segment (private vs corporate).
- Map your product calls to the new endpoint structure (
/single/{product}vs/signing-basket). - Adapt payloads to the product-specific schemas — particularly
creditorAccount.accountType,chargeBearer,paymentTypeInformationand remittance text patterns. - Move SEK Credit Transfer flows into baskets — single
sek-credit-transferis not supported in v2. - Re-test SCA flows — both
DECOUPLEDandREDIRECTare still supported, but URLs and link structures are versioned. - Regression-test cancellation flows for future-dated payments.
- Verify in Sandbox, then complete Go Live on the Developer Portal.
Which payment products are supported in the new Swedish APIs?
| Product | Code (URL identifier) | Private | Corporate |
|---|---|---|---|
| Handelsbanken Domestic Credit Transfer (SHBCT) | handelsbanken-domestic-credit-transfer | ✓ | ✓ |
| SEK Credit Transfer (SEKCT) — former bank transfers and giro payments | sek-credit-transfer | ✓ (basket only) | ✓ (basket only) |
| SEPA Credit Transfer (SEPACT) | sepa-credit-transfer | – | ✓ |
| Cross Currency Credit Transfer (CCCT) | cross-currency-credit-transfer | ✓ | ✓ |
What are the new base URLs?
| Environment | URL |
|---|---|
| Sandbox | https://sandbox.handelsbanken.com/openbanking/psd2/se/v2 |
| Live | https://api.handelsbanken.com/openbanking/psd2/se/v2 |
Note the new /se/v2 path segment. The legacy API used /openbanking/psd2/v1.
What is the URL structure for the new endpoints?
The new APIs split clearly between customer segment and payment type:
/payments/private/single/{paymentProduct}— single private payments/payments/private/signing-basket— private basket payments/payments/corporate/single/{paymentProduct}— single corporate payments/payments/corporate/signing-basket— corporate basket payments/payments/private/accounts/shorttermconsents— short-term consent (Private only)
In v1.3.2, all payments used a single /payments/{paymentProduct} endpoint regardless of customer type.
What is the mapping from legacy product names to the new ones?
| Legacy v1.3.2 product | New SE v2.0.0 product |
|---|---|
swedish-domestic-giro-payment | sek-credit-transfer (SEKCT, basket only) |
swedish-domestic-credit-transfer | sek-credit-transfer (SEKCT, basket only) |
sepa-credit-transfer | sepa-credit-transfer (Corporate only) |
cross-currency-credit-transfer | cross-currency-credit-transfer |
| (new in v2) | handelsbanken-domestic-credit-transfer (SHBCT) |
Important: In v2, SEKCT can only be executed inside a signing basket — single SEKCT payments are no longer supported. SHBCT, SEPACT and CCCT can only be executed as single payments.
How have signing baskets changed?
| Aspect | Legacy v1.3.2 | New v2.0.0 |
|---|---|---|
| Eligibility | Sweden, individuals, swedish-domestic-giro-payment only | Both Private and Corporate, sek-credit-transfer |
| Max payments per basket | 30 | 30 (Private) / 100 (Corporate) |
| Min payments per basket | 1 | 1 |
What are the main structural changes in the payment payload?
- Dedicated product-specific schemas: each product (SHBCT, SEKCT, SEPACT, CCCT) now has its own validated request schema with the right field set, account types and patterns. The legacy API used one generic
PaymentPayloadfor all products. creditorAccount.accountTypeis now strictly enumerated per product:- SHBCT —
BBANonly (8–9 digit Swedish SHB account number) - SEKCT —
BBAN,IBAN(Swedish IBANs only),PG,BG - SEPACT, CCCT Nordic, CCCT EU-payment —
IBANonly - CCCT Normal / Express —
IBANorBBAN
- SHBCT —
chargeBearervalues are now tied to product:SHAR— SEPACT, CCCT Nordic, CCCT EU-paymentCHAR,CRED,DEBT— CCCT Normal and Express
PaymentTypeInformationuses ISO 20022 codes (SEPA,URGP,NURG) and category-purpose codes (SUPP,NOPA,EUSE).- Stricter character patterns apply to
creditor.name,remittanceInformation.textand similar fields — particularly for SHBCT, where remittance text is limited to 14 characters.
Can I use the V2 to Cancel or ask for Status on payments created in V1?
No, You must use the old endpoint (V1) to cancel and ask for status on payments.
Where do I get support?
- Developer Portal:https://developer.handelsbanken.com/api/
- Technical guidelines:https://developer.handelsbanken.com/api/psd2/guidelines/payments
- Sandbox info:https://developer.handelsbanken.com/api/sandbox/geninfo
- Country-specific (SE):https://developer.handelsbanken.com/api/apis/payments/se
- Go Live:https://developer.handelsbanken.com/api/psd2/live
Is SEK Credit Transfer available only as a basket payment?
Yes. There is no /payments/corporate/single/sek-credit-transfer endpoint. SEK credit transfers can be initiated only via POST /payments/corporate/signing-basket.
A basket containing a single payment is the correct and supported approach: the request requires the sekCreditTransfers array with minItems 1 (maxItems 100), so an array of length 1 is the intended way to initiate an individual cross-bank SEK transfer.
How do we check status after executing a basket?
There is no basket-level status endpoint. Use the existing per-payment endpoint GET /payments/corporate/{paymentProduct}/{paymentId}/status with paymentProduct = sek-credit-transfer and the paymentId returned per payment in the initiation response.
The initiation response returns a payments array, each element carrying its own paymentId plus your tppPaymentReference; use that mapping to poll each payment.
On PARTIAL_FAILURE, how do we identify which payments failed?
The basket execute response returns only a basket-leveltransactionStatus (SUCCEEDED / FAILED / PARTIAL_FAILURE); it does not break results down per payment.
To identify individual outcomes, call the per-payment status endpoint for each paymentId. At payment level, transactionStatus uses ISO 20022 codes (ACTC, ACCP, ACSC, ACCC, PATC, CANC, RJCT). A failed payment shows RJCT, with reasonCode and reason giving the specific cause.
Recommended PARTIAL_FAILURE pattern
- Execute basket. If
transactionStatus = PARTIAL_FAILURE,- Iterate the
paymentsarray from the initiation response,- Call
GET .../sek-credit-transfer/{paymentId}/statusfor each, treatingRJCT+reasonCode/reasonas the failure detail.