Skip to main content

Banking Embed Integration

Embedded integration allows you to place the finoOS Banking Connect user interface directly within your application. Rather than redirecting end users to an external page, the entire bank connect flow — from bank selection through credential entry to consent — happens inside your product. This provides a seamless experience where users never leave your application context.

Under the hood, the Banking Connect UI is loaded in a sandboxed iframe that communicates with your host application via the browser's postMessage API. finoOS offers two integration paths that build on this mechanism:

Iframe IntegrationWidget Integration
ApproachEmbed the Banking Connect UI directly via an <iframe> elementUse the @finodigital/uba-widget npm package
CommunicationRaw window.postMessageClean CustomEvent API
Setup effortManual iframe attributes, origin checks, URL constructionAutomatic — the package handles it for you
Best forNon-JavaScript backends, full control scenariosModern JS/TS frontends, faster integration

Both options use the same underlying iframe mechanism. The widget package is a thin wrapper that reduces boilerplate and improves the developer experience.


Prerequisites

Before embedding the Banking Connect UI you need a valid session. The session is created through the finoOS Banking API as described in the Banking Connect section. The session creation returns three values that are required for both integration options:

{
"sessionId": "eyJhbG...",
"userIdentifier": "a1b2c3d4...",
"redirectURL": "https://uba.fino.run/auth?sessionId=..."
}
ParameterDescription
sessionIdAuthenticates the current Banking Connect session
userIdentifierIdentifies the end user across sessions
redirectURLPre-built URL that can be used for redirect-based or embedded integration

Pass sessionId and userIdentifier to your frontend for the embedded integration. See Banking Connect for details on how to create sessions for new and recurring users.


Option 1: Iframe Integration

1. Build the Iframe URL

The base URL follows the pattern:

https://uba.{stage}.fino.run/auth?param1=value1&param2=value2

Example:

const iframeUrl = new URL('https://uba.fino.run/auth');
iframeUrl.searchParams.set('service', 'myapp');
iframeUrl.searchParams.set('tenant', 'mycompany');
iframeUrl.searchParams.set('sessionId', sessionId);
iframeUrl.searchParams.set('userIdentifier', userIdentifier);
iframeUrl.searchParams.set('redirectURL', 'https://myapp.com/callback');
iframeUrl.searchParams.set('exitURL', 'https://myapp.com/cancel');
iframeUrl.searchParams.set('embedded', 'true');
iframeUrl.searchParams.set('hideHeader', 'true');

2. URL Parameters Reference

Required Parameters

ParameterTypeDescription
servicestringName of your service / application (default: 'uba')
tenantstringTenant identifier for loading tenant-specific config (default: 'none')
sessionIdstringSession identifier obtained from the API
userIdentifierstringUser identifier hash obtained from the API
redirectURLstringURL the user is redirected to on success

Optional Parameters

ParameterTypeDescription
fallbackURLstringURL the user is redirected to on error
exitURLstringURL the user is redirected to on abort
embeddedbooleanSet to true for iframe mode (adapts UI for embedding)
recurringbooleanIf true, users are stored and can manage accounts later
bankCodestringPreselect a bank by code (skips bank search step)
langstringLanguage code: en, de, ru, tr
hideHeaderbooleanHides the header bar
hideBoxbooleanHides the shadow box around forms
hideTrustLabelbooleanHides the trust label in the login flow
hideExitbooleanHides the exit button
hideLoginContainerbooleanHides the wrapper container on the login screen
skipAccountSelectbooleanAuto-select all accounts (skips account selection)
selectAccountstringIBAN to auto-select a specific account
singleAccountOnlybooleanRestrict selection to a single account
autoSelectSingleAccountbooleanAuto-select when only one account exists
showBalancebooleanShow balance for each bank account
readonlybooleanSet UBA to read-only mode
managementbooleanForward to management state after login
moneyTransferbooleanActivate money transfer functionality
paymentbooleanDisplay payment terms instead of standard terms
paymentIdstringPayment identifier for payment flow
storeSecretsbooleanWhether to store bank credentials (default: true)
onlySelectAccountsbooleanOnly allow (de)selecting existing accounts
consentCheckbooleanShow data transfer consent checkbox
colorSchemestringColor scheme mode: dark, light, or system (default: light)
themeobjectTheme object to customize appearance (JSON)
demostringDemo mode identifier

3. Embed the Iframe

<iframe
id="uba-iframe"
src="YOUR_IFRAME_URL"
allow="clipboard-write; clipboard-read; local-network-access; payment"
sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-top-navigation"
referrerpolicy="strict-origin-when-cross-origin"
title="Bank Connect - fino.digital"
style="width: 100%; height: 600px; border: none;"
></iframe>
Important

Both the sandbox and allow attributes are required for UBA to function correctly. Do not remove individual sandbox permissions.

4. Listen to PostMessage Events

UBA communicates with the parent window via window.postMessage. Always validate the message origin.

window.addEventListener('message', (event) => {
// Security: only accept messages from UBA
if (event.origin !== 'https://uba.fino.run') {
return;
}

const { type, message, payload, user } = event.data;

switch (type) {
case 'ready':
console.log('UBA loaded, version:', payload.version);
break;

case 'bank-select':
console.log('Bank selected:', message.bank.shortName);
break;

case 'bank-login':
console.log('Login initiated for:', message.bank.shortName);
break;

case 'bank-connect':
console.log('Accounts connected:', message.accountIds);
console.log('Bank login ID:', message.bankLoginId);
// Use accountIds to fetch transaction data via finoOS API
break;

case 'bank-disconnect':
console.log('Bank disconnected:', message.bankLoginId);
break;

case 'redirect':
console.log('Redirecting to:', message.url, 'reason:', message.reason);
break;

case 'state':
console.log('State change:', message);
break;

case 'ERROR':
console.error('UBA error:', message);
break;

case 'EXIT':
console.log('User exited the flow');
break;
}
});

5. Events Reference

Events sent from UBA to the parent window

Event TypeWhenPayload
readyIframe has loaded and initialized{ version, features? }
bank-selectUser selected a bank{ bank: { bankCode, shortName, icon } }
bank-loginUser submitted bank credentials{ bank }
bank-connectAccounts successfully connected{ accountIds, bankLoginId }
bank-accountsAccounts loaded on management screen{ accounts }
bank-syncUser triggered account syncSync challenge details
bank-disconnectUser removed a bank connection{ bankLoginId }
bank-redirectRedirect-based login startedRedirect details
redirectUBA is about to redirect the user{ url, reason }
handle-redirectRedirect callback receivedRedirect context
stateTAN challenge or redirect state changedState details

Additional granular events

Event TypeDescription
BANK_SEARCH_INITBank search initialized
BANK_LOGIN_SUCCESSBank login succeeded
BANK_LOGIN_ERRORBank login failed
BANK_LOGIN_TIMEOUTBank login timed out
BANK_LOGIN_CONFLICTConflicting bank login detected
BANK_LOGIN_ABORTBank login aborted
BANK_ACCOUNT_SELECTAccount selection started
BANK_ACCOUNT_SELECT_SUCCESSAccounts selected successfully
BANK_ACCOUNT_SELECT_ERRORAccount selection failed
BANK_ACCOUNT_SELECT_TOGGLEAccount toggle changed
BANK_REDIRECT_SUCCESSRedirect login succeeded
BANK_REDIRECT_ERRORRedirect login failed
BANK_SYNC_SUCCESSSync completed
BANK_SYNC_ERRORSync failed
TAN_CHALLENGE_SUBMITTAN submitted
TAN_CHALLENGE_SUCCESSTAN challenge passed
TAN_CHALLENGE_ERRORTAN challenge failed
EXITUser exited the flow
ERRORGeneral error occurred
SUCCESSOperation succeeded

6. Handling the Redirect Callback

After a successful bank connect, UBA redirects the user to your redirectURL with query parameters appended:

https://myapp.com/callback?userIdentifier=a1b2c3d4&sessionId=eyJhbG...

On your callback page, extract these parameters to continue working with the finoOS API:

const params = new URLSearchParams(window.location.search);
const userIdentifier = params.get('userIdentifier');
const sessionId = params.get('sessionId');
tip

In embedded mode you typically handle the flow entirely via postMessage events (especially bank-connect) and may not need the redirect callback at all. Set redirectURL anyway as a fallback.

7. Theming

You can customize UBA's appearance in two ways: setting a color scheme (light/dark mode) and providing a theme object with specific color overrides. Both can be used independently or combined.

Color Scheme

Control light and dark mode via the colorScheme URL parameter:

iframeUrl.searchParams.set('colorScheme', 'dark');
ValueBehavior
lightLight mode (default)
darkDark mode
systemFollows the user's OS preference via prefers-color-scheme

The color scheme is resolved with this precedence: URL parameter > tenant config > default (light).

Theme Color Overrides

Pass a theme parameter as a JSON-encoded object to override individual colors:

iframeUrl.searchParams.set('theme', JSON.stringify({
primaryColor: '#1A73E8',
secondaryColor: '#34A853',
backgroundColor: '#FFFFFF'
}));

Supported color keys:

KeyDefaultDescription
primaryColor#F7C708Primary brand color (buttons, highlights)
secondaryColor#288CD1Secondary brand color
backgroundColor#F8F9FAPage background
accentColor#288CD1Accent elements
textColor#363636Body text
linkColor#F7C708Links
infoColor#3e8ed0Info alerts and badges
successColor#48c78eSuccess states
warningColor#ffe08aWarning states
dangerColor#f14668Error and danger states

All values must be valid hex colors (3 or 6 digits, with or without the # prefix).

Combining Color Scheme and Theme

You can use colorScheme and theme together. For example, enable dark mode while overriding the background and text colors:

iframeUrl.searchParams.set('colorScheme', 'dark');
iframeUrl.searchParams.set('theme', JSON.stringify({
backgroundColor: '#272727',
textColor: '#FBF8F6'
}));
tip

Theming can also be configured at the tenant level via the fino tenant service. Tenant-level theme settings apply as defaults and can still be overridden by URL parameters.

8. Error Codes

When errors occur, the event payload may include one of these error codes:

CodeDescription
INVALID_PINUser entered invalid PIN
INVALID_OTPInvalid one-time password
MISSING_TOKENNo authentication at provider API
ACCESS_BLOCKEDAccess to bank backend blocked
HBCI_ERRORUnknown HBCI protocol error
OFFLINE_ERRORBank is offline
BANK_ERRORBank reported a system error
NOT_SUPPORTEDBank not supported
ACCESS_DENIEDProvider API error
SCA_REQUIREDStrong customer authentication required
INVALID_CONSENTConsent is invalid
AUTH_NOT_COMPLETEDAuthentication not completed (redirect flow)
UNKNOWNUnexpected error

Option 2: Widget Integration (via npm)

The @finodigital/uba-widget npm package wraps the iframe integration into a developer-friendly API. Under the hood it uses the exact same iframe mechanism, so all events and parameters described above apply. The key differences are:

  • No manual iframe HTML — The package creates and manages the iframe for you, including all required sandbox and allow attributes.
  • Automatic origin validation — Message origin checks are built in.
  • Clean event API — Events are dispatched as standard CustomEvents on the element instead of raw postMessage. No need to parse event.data manually.
  • Programmatic control — Methods like reload(), destroy(), setSession(), and postMessage() give you full control over the widget lifecycle.

Quick Setup

npm install @finodigital/uba-widget

As a Web Component

<fino-uba
session-id="eyJhbG..."
user-identifier="a1b2c3d4"
service="myapp"
tenant="mycompany"
embedded="true"
hide-header
redirect-url="https://myapp.com/callback"
origin="https://uba.fino.run"
color-scheme="dark"
theme='{"primaryColor":"#1A73E8","backgroundColor":"#272727"}'
></fino-uba>

<script type="module">
import '@finodigital/uba-widget';

const widget = document.querySelector('fino-uba');

widget.addEventListener('ready', (e) => {
console.log('UBA ready:', e.detail.payload.version);
});

widget.addEventListener('bank-connect', (e) => {
console.log('Connected accounts:', e.detail.payload.accountIds);
});
</script>

Programmatic API

import { createUbaWidget } from '@finodigital/uba-widget';

const widget = createUbaWidget({
sessionId: 'eyJhbG...',
userIdentifier: 'a1b2c3d4',
service: 'myapp',
tenant: 'mycompany',
hideHeader: true,
colorScheme: 'dark',
theme: { primaryColor: '#1A73E8', backgroundColor: '#272727' },
origin: 'https://uba.fino.run',
});

document.getElementById('uba-container').appendChild(widget.element);

await widget.ready();

widget.on('bank-connect', ({ payload }) => {
console.log('Connected:', payload.accountIds);
});

// Later: update session, reload, or clean up
widget.setSession('newSessionId', 'newUserIdentifier');
widget.reload();
widget.destroy();

For the complete API reference, configuration options, and advanced usage, see the full documentation on npm: @finodigital/uba-widget on npm


Environments

EnvironmentBase URL
Testinghttps://uba.test.fino.run/
Productionhttps://uba.fino.run/

Adjust the origin in your postMessage validation and widget origin attribute accordingly.


Sandbox & Demo

For testing without real API credentials, UBA provides a sandbox mode:

URL: https://uba.{stage}.fino.run/sandbox

The sandbox auto-creates a session with the fino demo bank. Use these test credentials:

FieldValue
Bankfino SCA Bank
Usernamefino01
Password654321

Interactive demo pages are also available: