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 Integration | Widget Integration | |
|---|---|---|
| Approach | Embed the Banking Connect UI directly via an <iframe> element | Use the @finodigital/uba-widget npm package |
| Communication | Raw window.postMessage | Clean CustomEvent API |
| Setup effort | Manual iframe attributes, origin checks, URL construction | Automatic — the package handles it for you |
| Best for | Non-JavaScript backends, full control scenarios | Modern 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=..."
}
| Parameter | Description |
|---|---|
sessionId | Authenticates the current Banking Connect session |
userIdentifier | Identifies the end user across sessions |
redirectURL | Pre-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¶m2=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
| Parameter | Type | Description |
|---|---|---|
service | string | Name of your service / application (default: 'uba') |
tenant | string | Tenant identifier for loading tenant-specific config (default: 'none') |
sessionId | string | Session identifier obtained from the API |
userIdentifier | string | User identifier hash obtained from the API |
redirectURL | string | URL the user is redirected to on success |
Optional Parameters
| Parameter | Type | Description |
|---|---|---|
fallbackURL | string | URL the user is redirected to on error |
exitURL | string | URL the user is redirected to on abort |
embedded | boolean | Set to true for iframe mode (adapts UI for embedding) |
recurring | boolean | If true, users are stored and can manage accounts later |
bankCode | string | Preselect a bank by code (skips bank search step) |
lang | string | Language code: en, de, ru, tr |
hideHeader | boolean | Hides the header bar |
hideBox | boolean | Hides the shadow box around forms |
hideTrustLabel | boolean | Hides the trust label in the login flow |
hideExit | boolean | Hides the exit button |
hideLoginContainer | boolean | Hides the wrapper container on the login screen |
skipAccountSelect | boolean | Auto-select all accounts (skips account selection) |
selectAccount | string | IBAN to auto-select a specific account |
singleAccountOnly | boolean | Restrict selection to a single account |
autoSelectSingleAccount | boolean | Auto-select when only one account exists |
showBalance | boolean | Show balance for each bank account |
readonly | boolean | Set UBA to read-only mode |
management | boolean | Forward to management state after login |
moneyTransfer | boolean | Activate money transfer functionality |
payment | boolean | Display payment terms instead of standard terms |
paymentId | string | Payment identifier for payment flow |
storeSecrets | boolean | Whether to store bank credentials (default: true) |
onlySelectAccounts | boolean | Only allow (de)selecting existing accounts |
consentCheck | boolean | Show data transfer consent checkbox |
colorScheme | string | Color scheme mode: dark, light, or system (default: light) |
theme | object | Theme object to customize appearance (JSON) |
demo | string | Demo 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>
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 Type | When | Payload |
|---|---|---|
ready | Iframe has loaded and initialized | { version, features? } |
bank-select | User selected a bank | { bank: { bankCode, shortName, icon } } |
bank-login | User submitted bank credentials | { bank } |
bank-connect | Accounts successfully connected | { accountIds, bankLoginId } |
bank-accounts | Accounts loaded on management screen | { accounts } |
bank-sync | User triggered account sync | Sync challenge details |
bank-disconnect | User removed a bank connection | { bankLoginId } |
bank-redirect | Redirect-based login started | Redirect details |
redirect | UBA is about to redirect the user | { url, reason } |
handle-redirect | Redirect callback received | Redirect context |
state | TAN challenge or redirect state changed | State details |
Additional granular events
| Event Type | Description |
|---|---|
BANK_SEARCH_INIT | Bank search initialized |
BANK_LOGIN_SUCCESS | Bank login succeeded |
BANK_LOGIN_ERROR | Bank login failed |
BANK_LOGIN_TIMEOUT | Bank login timed out |
BANK_LOGIN_CONFLICT | Conflicting bank login detected |
BANK_LOGIN_ABORT | Bank login aborted |
BANK_ACCOUNT_SELECT | Account selection started |
BANK_ACCOUNT_SELECT_SUCCESS | Accounts selected successfully |
BANK_ACCOUNT_SELECT_ERROR | Account selection failed |
BANK_ACCOUNT_SELECT_TOGGLE | Account toggle changed |
BANK_REDIRECT_SUCCESS | Redirect login succeeded |
BANK_REDIRECT_ERROR | Redirect login failed |
BANK_SYNC_SUCCESS | Sync completed |
BANK_SYNC_ERROR | Sync failed |
TAN_CHALLENGE_SUBMIT | TAN submitted |
TAN_CHALLENGE_SUCCESS | TAN challenge passed |
TAN_CHALLENGE_ERROR | TAN challenge failed |
EXIT | User exited the flow |
ERROR | General error occurred |
SUCCESS | Operation 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');
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');
| Value | Behavior |
|---|---|
light | Light mode (default) |
dark | Dark mode |
system | Follows 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:
| Key | Default | Description |
|---|---|---|
primaryColor | #F7C708 | Primary brand color (buttons, highlights) |
secondaryColor | #288CD1 | Secondary brand color |
backgroundColor | #F8F9FA | Page background |
accentColor | #288CD1 | Accent elements |
textColor | #363636 | Body text |
linkColor | #F7C708 | Links |
infoColor | #3e8ed0 | Info alerts and badges |
successColor | #48c78e | Success states |
warningColor | #ffe08a | Warning states |
dangerColor | #f14668 | Error 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'
}));
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:
| Code | Description |
|---|---|
INVALID_PIN | User entered invalid PIN |
INVALID_OTP | Invalid one-time password |
MISSING_TOKEN | No authentication at provider API |
ACCESS_BLOCKED | Access to bank backend blocked |
HBCI_ERROR | Unknown HBCI protocol error |
OFFLINE_ERROR | Bank is offline |
BANK_ERROR | Bank reported a system error |
NOT_SUPPORTED | Bank not supported |
ACCESS_DENIED | Provider API error |
SCA_REQUIRED | Strong customer authentication required |
INVALID_CONSENT | Consent is invalid |
AUTH_NOT_COMPLETED | Authentication not completed (redirect flow) |
UNKNOWN | Unexpected 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
sandboxandallowattributes. - Automatic origin validation — Message origin checks are built in.
- Clean event API — Events are dispatched as standard
CustomEvents on the element instead of rawpostMessage. No need to parseevent.datamanually. - Programmatic control — Methods like
reload(),destroy(),setSession(), andpostMessage()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
| Environment | Base URL |
|---|---|
| Testing | https://uba.test.fino.run/ |
| Production | https://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:
| Field | Value |
|---|---|
| Bank | fino SCA Bank |
| Username | fino01 |
| Password | 654321 |
Interactive demo pages are also available:
- Iframe demo: https://uba.test.fino.run/demo/iframe — includes a live event log, session parameter editor, and copy-paste iframe code.
- Widget demo: https://uba.test.fino.run/demo/widget — includes configuration toggles, generated markup, and a live event log.