Window Bridge API
The Window Bridge API provides a request/response communication channel between the Fluid widget and the host page. It complements the existing one-way event model for cases where the widget needs to ask the host page for data and await a typed response.
Events vs. Bridge
| Use case | Mechanism |
|---|---|
| Widget notifying the host that something happened | DOM CustomEvent (fluid-info, fluid-command, fluid-error) |
| Widget requesting data or an action from the host and awaiting a typed response | window.fluid.bridge.handleRequest(...) |
How the method name connects both sides
The method name string is the shared key between the widget and your page. Your handler must be registered under exactly the same name that the widget uses when it calls that method. If the names do not match, the request rejects immediately with BRIDGE_HANDLER_NOT_FOUND.
Registering a handler
The host page registers a handler by calling window.fluid.bridge.handleRequest(methodName, handler). This should be done before the widget needs the data — typically right after the Fluid script is loaded.
Bridge handlers must be registered after the Fluid script has finished executing, otherwise window.fluid.bridge is undefined. See Script loading for bridge usage below for both inline and programmatic loading patterns.
window.fluid.bridge.handleRequest('verifyBonusCode', async ({ code }) => {
const result = await myApi.verifyBonusCode(code);
if (!result.valid) {
return { status: 'invalid' };
}
return { status: 'valid', bonus: result.bonus, autoOptedIn: result.autoOptedIn };
});
Calling handleRequest again with the same method name silently replaces the previous handler.
Defining the typed contract
Bridge method names and their request/response shapes are defined by augmenting the FluidBridgeContract interface from the @fluidpayments/types package in your host application.
@fluidpayments/types ships response types for each bridge method:
import type {
FluidBridgeVerifyBonusCodeResponse,
FluidBridgeVerify2FAResponse
} from '@fluidpayments/types';
declare module '@fluidpayments/types' {
interface FluidBridgeContract {
verifyBonusCode: {
request: { code: string };
response: FluidBridgeVerifyBonusCodeResponse;
};
verify2FA: {
request: { code: string };
response: FluidBridgeVerify2FAResponse;
};
}
}
This gives you compile-time safety: your handler signature will be typed according to the contract you define.
Timeout and errors
All bridge requests have a fixed timeout of 5000ms. If the handler does not respond within that time, or if no handler has been registered, the returned promise rejects with a FluidBridgeError object:
type FluidBridgeError = {
code: 'BRIDGE_HANDLER_NOT_FOUND' | 'BRIDGE_TIMEOUT' | 'BRIDGE_HANDLER_ERROR';
message: string;
details?: unknown; // present for BRIDGE_HANDLER_ERROR — contains the original thrown value
};
code | Cause |
|---|---|
BRIDGE_HANDLER_NOT_FOUND | No handler has been registered for the requested method |
BRIDGE_TIMEOUT | The handler did not resolve within 5000ms |
BRIDGE_HANDLER_ERROR | The handler threw or returned a rejected promise |
Available methods
List of bridge methods that the Fluid Widget may invoke. You only need to register handlers for the methods your integration uses.
verifyBonusCode
Validates a manual bonus code entered by the user. The widget invokes this method when the user clicks "Apply" in the manual bonus code input.
Required when the manualBonusCodesEnabled operator configuration flag is enabled. If the flag is enabled but no handler is registered, the widget will show a validation error after the 5000ms timeout.
Request payload
{ code: string }
| Field | Type | Description |
|---|---|---|
code | string | The bonus code the user entered (trimmed) |
Response payload
type FluidBridgeVerifyBonusCodeResponse = {
status: 'valid' | 'invalid';
bonus?: FluidBonusData;
autoOptedIn?: boolean;
};
| Field | Type | Required | Description |
|---|---|---|---|
status | 'valid' | 'invalid' | Yes | Whether the code is valid |
bonus | FluidBonusData | When status is 'valid' | The bonus details to display |
autoOptedIn | boolean | No | Controls whether the user is automatically opted into the bonus. Defaults to false when omitted |
The bonus object follows the same FluidBonusData shape used in the bonuses attribute:
{
code: string;
title: string;
description: string;
logoUrl?: string;
termsAndConditions?: string;
minDeposit?: number;
maxDeposit?: number;
maxBonus?: number;
maxBonusPercentage?: number;
paymentMethodFilter?: string[];
}
autoOptedIn behaviour
When autoOptedIn is true, the user is automatically opted into the validated bonus without needing to confirm via the bonus selection UI.
When autoOptedIn is false or absent, the user must confirm their bonus selection — for stored-methods users the bonus list modal auto-opens after validation.
| Flow | autoOptedIn | Behaviour |
|---|---|---|
| First-time depositor (no stored methods) | false / absent | Default — bonus shown on the bonus selection step; user confirms before proceeding |
| First-time depositor (no stored methods) | true | Bonus selector card shown on step 1, bonus step skipped |
| Stored methods | true | Default — works as before |
| Stored methods | false / absent | Bonus list modal auto-opens after validation |
Example
// Register the verifyBonusCode handler once after the Fluid script loads
window.fluid.bridge.handleRequest('verifyBonusCode', async ({ code }) => {
const result = await myApi.verifyBonusCode(code);
if (!result.valid) {
return { status: 'invalid' };
}
return {
status: 'valid',
bonus: {
code: result.code,
title: result.title,
description: result.description,
logoUrl: result.logoUrl,
termsAndConditions: result.termsAndConditions,
minDeposit: result.minDeposit,
maxBonus: result.maxBonus,
maxBonusPercentage: result.maxBonusPercentage,
paymentMethodFilter: result.paymentMethodFilter,
},
autoOptedIn: result.autoOptedIn,
};
});
verify2FA
Validates a 6-digit authenticator code entered by the user during the withdrawal flow. The widget invokes this method when the user clicks "Verify code" in the 2FA verification step.
Required when the two-factor-auth-enabled attribute is set to "true" on the widget element. If the attribute is enabled but no handler is registered, the widget will show a validation error after the 5000ms timeout.
Request payload
{ code: string }
| Field | Type | Description |
|---|---|---|
code | string | The 6-digit authenticator code the user entered |
Response payload
type FluidBridgeVerify2FAResponse =
| { status: 'valid' }
| { status: 'invalid' }
| { status: 'rate_limited'; retryAfterMs?: number };
| Field | Type | Required | Description |
|---|---|---|---|
status | 'valid' | 'invalid' | 'rate_limited' | Yes | The verification result |
retryAfterMs | number | No | Milliseconds before the user can retry. Only used when status is 'rate_limited'. The widget shows a countdown and auto-re-enables the input after this period. The {seconds} placeholder in the rate limit content key is interpolated from this value |
Example
// Register the verify2FA handler once after the Fluid script loads
window.fluid.bridge.handleRequest('verify2FA', async ({ code }) => {
const result = await myApi.verify2FA(code);
if (result.valid) {
return { status: 'valid' };
}
if (result.rateLimited) {
return { status: 'rate_limited', retryAfterMs: result.retryAfterMs };
}
return { status: 'invalid' };
});
Script loading for bridge usage
Bridge handlers must be registered after the Fluid script has finished executing. There are two common loading patterns:
Inline <script> tag
Use a plain <script> tag — not type="module", async, or defer — so that window.fluid.bridge is available immediately in the next script block:
<script src="https://get.fluidpayments.io/index.js"></script>
<script>
// window.fluid.bridge is guaranteed to exist here
window.fluid.bridge.handleRequest('verifyBonusCode', async ({ code }) => {
// ...
});
</script>
Programmatic loading
When loading the script dynamically (common in SPA / framework integrations), register handlers in the onload callback:
const script = document.createElement('script');
script.src = 'https://get.fluidpayments.io/index.js';
script.onload = () => {
// window.fluid.bridge is guaranteed to exist here
window.fluid.bridge.handleRequest('verifyBonusCode', async ({ code }) => {
// ...
});
};
document.head.appendChild(script);
Do not set script.async = true — async loading defers execution, so window.fluid.bridge may not exist when your onload callback runs. For framework-specific examples (e.g. React useEffect), see Framework integrations.
Defensive fallback: queue-drain shim
If your integration cannot guarantee that handler registration happens after the script loads (e.g. handlers are registered at module scope in a bundled app), you can install a lightweight shim before loading the Fluid script:
<script>
window.fluid = window.fluid || {};
window.fluid.bridge = window.fluid.bridge || {
__queue: [],
handleRequest(method, handler) { this.__queue.push([method, handler]); }
};
</script>
On init the widget captures any queued handleRequest calls and replays them into the real handler registry. This is a defensive fallback — the recommended approaches are the inline <script> tag or the onload callback above.
Limitations
- There is no per-request timeout configuration — the 5000ms timeout applies to all methods.
- The handler registry is shared across all widget instances on the page. If more than one Fluid widget element is present simultaneously, they share the same handlers.