Skip to main content

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 caseMechanism
Widget notifying the host that something happenedDOM CustomEvent (fluid-info, fluid-command, fluid-error)
Widget requesting data or an action from the host and awaiting a typed responsewindow.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.

Script loading requirement

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
};
codeCause
BRIDGE_HANDLER_NOT_FOUNDNo handler has been registered for the requested method
BRIDGE_TIMEOUTThe handler did not resolve within 5000ms
BRIDGE_HANDLER_ERRORThe 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 }
FieldTypeDescription
codestringThe bonus code the user entered (trimmed)

Response payload

type FluidBridgeVerifyBonusCodeResponse = {
status: 'valid' | 'invalid';
bonus?: FluidBonusData;
autoOptedIn?: boolean;
};
FieldTypeRequiredDescription
status'valid' | 'invalid'YesWhether the code is valid
bonusFluidBonusDataWhen status is 'valid'The bonus details to display
autoOptedInbooleanNoControls 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.

FlowautoOptedInBehaviour
First-time depositor (no stored methods)false / absentDefault — bonus shown on the bonus selection step; user confirms before proceeding
First-time depositor (no stored methods)trueBonus selector card shown on step 1, bonus step skipped
Stored methodstrueDefault — works as before
Stored methodsfalse / absentBonus 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 }
FieldTypeDescription
codestringThe 6-digit authenticator code the user entered

Response payload

type FluidBridgeVerify2FAResponse =
| { status: 'valid' }
| { status: 'invalid' }
| { status: 'rate_limited'; retryAfterMs?: number };
FieldTypeRequiredDescription
status'valid' | 'invalid' | 'rate_limited'YesThe verification result
retryAfterMsnumberNoMilliseconds 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.