Skip to content

Data Objects

Fluid widgets accept structured data as stringified JSON attributes. These types are available in the @fluidpayments/types npm package.

Object Widget Quick Deposit Virtual
User Data Yes Yes Yes
Bonus Data Yes Yes —
Virtual Currencies & Balance — — Yes
Packs — — Yes

Used for resolving AI-suggested Deposit Amounts via machine learning.

type FluidUserData = {
age?: number;
affiliateId?: string;
depositCount?: number;
daysSinceRegistration?: number;
fullName?: string;
kycStatus?: FluidKycStatus; // 'verified' | 'unverified' | 'processing'
lifetimeDeposit?: number;
previousDeposit?: number;
tags?: string[];
};

All properties are optional. Pass as a stringified JSON attribute:

widget.setAttribute('user-data', JSON.stringify({
age: 30,
depositCount: 5,
fullName: 'John Doe',
kycStatus: 'verified',
lifetimeDeposit: 500,
previousDeposit: 100,
tags: ['vip']
}));
const bonusData = [{
code: "DepositBonus",
title: "Deposit Bonus",
description: "100% up to 200 EUR + 10 Mega Spins",
logoUrl: "https://example.com/bonus-img.png",
maxBonus: 200,
maxBonusPercentage: 150,
minDeposit: 10,
maxDeposit: 250,
minDepositCount: 2,
maxDepositCount: 10,
minDaysSinceRegistration: 25,
maxDaysSinceRegistration: 180,
selected: true,
termsAndConditions: "<p>Welcome Offer T&Cs...</p>",
success: {
description: "Your bonus has been added to your account."
},
paymentMethodFilter: {
applyTo: [
{ providerType: "CREDITCARD" },
{ providerType: "WEBREDIRECT", service: "BLIXTPAY" }
]
}
}];
widget.setAttribute('bonuses', JSON.stringify(bonusData));
Property Description
code Bonus code identifier. Required
title / description Display text. Both required
logoUrl Bonus image (84x84px, 168x168px retina). If it’s absent or can’t be loaded (for example a 404), the bonus image placeholder set in the Theme is shown instead
maxBonus Maximum claimable bonus amount
maxBonusPercentage Percentage of deposit amount (e.g. 150 = 150%). Requires maxBonus
minDeposit / maxDeposit Optional deposit amount boundaries
minDepositCount / maxDepositCount Optional range for the user’s number of past deposits, checked against depositCount in User Data
minDaysSinceRegistration / maxDaysSinceRegistration Optional range for the days since the user registered, checked against daysSinceRegistration in User Data
selected Pre-selects the bonus. Set it on one bonus only — see Preselecting a bonus
variant Optional. "abandon" or "regularAndAbandon" offers the bonus in the modal shown when the user tries to leave — see Deposit Abandon Modal
termsAndConditions HTML string
paymentMethodFilter Controls which methods receive the bonus — see Payment method filter
success.description Custom message shown on the deposit success screen in place of the bonus title and description. When omitted, the success screen shows the title and description. Fluid Cashier only: the inline Quick Deposit success screen doesn’t show it

A bonus that sets minDepositCount or maxDepositCount is only offered when user-data includes depositCount; the same goes for the registration range and daysSinceRegistration.

A bonus that fails validation (for example a missing code, title or description) is left out of the list, and a fluid-error event reports why.

Without a paymentMethodFilter, the bonus is available with every payment method. With one, set either of these lists (not both — a bonus that sets both is invalid and left out):

  • applyTo: the bonus applies only to the listed payment methods.
  • excludeFrom: the bonus applies to every payment method except the listed ones.

Each entry names a method by providerType and, when the method has one, its service. providerType on its own is enough for a method without a service. Write both values exactly as they appear in the payment provider configuration.

maxBonusPercentage multiplies the deposited amount. Example: 150% of €50 = €75 bonus (capped at maxBonus). If maxBonus exists without maxBonusPercentage, 100% is assumed.

Set selected: true on a single bonus in the array to pre-select it for the user, for example when the user arrives from a specific bonus offer. The preselected bonus must be part of the bonuses array. If several bonuses are marked selected, only the first one is preselected.

bonuses is an observed attribute, so you can change the preselection after the wallet has loaded by setting the attribute again.

Preselected bonus Example of a preselected bonus

The virtual-currencies attribute lists the virtual currencies the user holds, as a stringified VirtualCurrencies object:

type VirtualCurrencies = {
version: 2;
currencies: VirtualCurrency[]; // every currency, including the primary and secondary
primaryId: string;
secondaryId: string;
};
type VirtualCurrency = {
id: string;
name: string;
code: string;
symbol: string;
exchangeRateToFiatCurrency?: number;
icon?: string;
conversionOffset?: number;
};
Field Description
primaryId id of the currency the user plays with (e.g. Gold Coins)
secondaryId id of the redeemable currency (e.g. Sweeps Coins)
id Stable identifier used to reference the currency from primaryId, secondaryId and pack items. Never shown to users, and must be unique
name Currency name, e.g. "Gold Coins"
code Display ticker, e.g. "GC"
symbol Currency symbol, also used as the icon’s alt text
exchangeRateToFiatCurrency Units of this currency per one unit of the FIAT currency, used to convert custom amounts. Required for the primary and secondary currencies, optional for any extra currencies in the catalogue
icon URL of the currency icon. If omitted, the primary and secondary currencies fall back to the Sweepstakes currency icons set in the Theme
conversionOffset Amount added to the result when a FIAT amount is converted into this currency

If primaryId or secondaryId doesn’t match a currency id, two currencies share an id, or the primary or secondary currency has no exchangeRateToFiatCurrency, the widget emits invalid-init-attributes.

The balance attribute carries the user’s current balances, as a stringified VirtualBalance object (not a plain number as on <fluid-widget>):

type VirtualBalance = {
version: 2;
primaryBalance: number;
secondaryBalance: number;
lockedSecondaryBalance?: number;
};

Packs render from two pools (regular, promoted) and three promotional slots (special, upsell, abort). The regular and promoted pools together form the pack browsing area, with promoted packs listed first. The browsing experience is the same for the first purchase and for later ones.

Anatomy of a Sweepstakes pack Anatomy of a Sweepstakes pack

type PackSet = {
version: 2;
categories?: Category[];
regular: Pack[];
promoted: Pack[];
promotedSpecial?: Pack;
promotedUpsell?: Pack;
promotedOnAbort?: Pack;
};
type Pack = {
version: 2;
packId: string;
variant: 'regular' | 'green' | 'red' | 'yellow' | 'promotion';
value: {
price: number;
previousPrice?: number; // when set, the current price is shown in the accent colour
items: { currencyId: string; amount: number }[];
};
categoryIds?: string[];
logoUrl: string;
logoAlt?: string;
label?: string;
info?: string;
tagged?: boolean;
selected?: boolean;
expirationDate?: string;
discount?: string;
};
type Category = { id: string; name: string; selected?: boolean };
Field Description
packId Unique identifier of the pack
variant 'regular' | 'green' | 'red' | 'yellow' | 'promotion'. Sets only the badge colour — see How packs are displayed
value.price Price of the pack in the FIAT currency
value.previousPrice Optional previous price in FIAT, shown struck through to signal a discount
value.items The currencies the pack grants, one or more { currencyId, amount } entries. currencyId references a VirtualCurrency id
categoryIds Optional ids of the categories the pack belongs to — see Pack categories
logoUrl URL of the pack image — see Pack image dimensions
logoAlt Optional alt text for the pack image
label Optional badge text, e.g. "Best value" on a green pack
info Optional additional info line, e.g. "+ 5,000 purchases this week"
tagged Optional. When true, the pack carries a “Hot” tag
selected Optional. Preselects the pack when the purchase flow opens. Has no effect on promotedOnAbort, or when the cashier opens to Quick Buy
expirationDate Optional expiry date in ISO 8601 format. The special and upsell packs are hidden once it has passed
discount Optional discount text, e.g. "-15%". Used only by promotedUpsell

A pack whose items reference an unknown currency id, or that has no items, makes the whole pack set invalid: the widget shows no packs and emits invalid-init-attributes. The deprecated priceHighlighted field is still accepted but ignored.

  • variant only sets the badge colour. The badge text comes from label; on regular and promoted packs the badge appears only for green, red and yellow packs that have a label.
  • previousPrice paints the current price in the accent (red) colour automatically.
  • discount is rendered only by the promoted upsell, as a second red badge next to label.
  • tagged adds a “Hot” tag to draw attention to the pack.
Pool Purpose
regular Shown after promoted packs in the browsing area
promoted Shown first in the browsing area
promotedSpecial Main promotional pack under a “Special pack” heading in the browsing area. Without categories it sits above the pack grid; with categories, right after the first category row
promotedUpsell Shown at the bottom of the payment method selection on a first-time purchase, and on the first step of a subsequent purchase. It appears only when it costs more than the pack the user has picked
promotedOnAbort Modal pack shown when the user closes the wallet during the purchase journey

The three promotional slots and categories are optional: leave out the ones you don’t want to show.

const packs = {
version: 2,
categories: [
{ id: 'starter', name: 'Starter' },
{ id: 'premium', name: 'Premium' }
],
regular: [
{
version: 2,
packId: 'pack-regular-01',
logoUrl: 'https://example.com/packs/pack-4.99.png',
variant: 'regular',
categoryIds: ['starter'],
value: {
price: 4.99,
items: [
{ currencyId: 'gc', amount: 5000 },
{ currencyId: 'sc', amount: 5 }
]
}
},
{
version: 2,
packId: 'pack-regular-02',
logoUrl: 'https://example.com/packs/pack-9.99.png',
variant: 'regular',
categoryIds: ['starter'],
value: {
price: 9.99,
items: [
{ currencyId: 'gc', amount: 10000 },
{ currencyId: 'sc', amount: 10 }
]
}
}
],
promoted: [
{
version: 2,
packId: 'pack-red-01',
logoUrl: 'https://example.com/packs/pack-red.png',
variant: 'red', // badge colour only
label: '-25%', // badge text
categoryIds: ['starter'],
value: {
price: 49.99,
previousPrice: 64.99, // paints the current price in the accent colour
items: [
{ currencyId: 'gc', amount: 65000 },
{ currencyId: 'sc', amount: 65 }
]
}
},
{
version: 2,
packId: 'pack-yellow-01',
logoUrl: 'https://example.com/packs/pack-yellow.png',
variant: 'yellow',
label: 'Exclusive',
categoryIds: ['premium'],
tagged: true,
value: {
price: 79.99,
items: [
{ currencyId: 'gc', amount: 100000 },
{ currencyId: 'sc', amount: 100 }
]
}
},
{
version: 2,
packId: 'pack-green-01',
logoUrl: 'https://example.com/packs/pack-green.png',
variant: 'green',
label: 'Best value',
categoryIds: ['premium'],
value: {
price: 9.99,
items: [
{ currencyId: 'gc', amount: 15000 },
{ currencyId: 'sc', amount: 15 }
]
}
}
],
promotedSpecial: {
version: 2,
packId: 'pack-promotion-01',
logoUrl: 'https://example.com/packs/special-promotion.png',
variant: 'promotion',
label: 'Special promotion',
info: '+ 5,000 purchases this week',
expirationDate: '2026-12-31T23:59:59Z',
value: {
price: 74.99,
previousPrice: 99.99,
items: [
{ currencyId: 'gc', amount: 10000 },
{ currencyId: 'sc', amount: 5 }
]
}
},
promotedUpsell: {
version: 2,
packId: 'pack-promotion-upsell-01',
logoUrl: 'https://example.com/packs/upsell-promotion.png',
variant: 'promotion',
label: 'Exclusive Offer',
expirationDate: '2026-12-31T23:59:59Z',
discount: '-15%', // rendered only here, as a second red badge next to label
value: {
price: 89.99,
previousPrice: 99.99,
items: [
{ currencyId: 'gc', amount: 10000 },
{ currencyId: 'sc', amount: 100 }
]
}
},
promotedOnAbort: {
version: 2,
packId: 'pack-promotion-abort-01',
logoUrl: 'https://example.com/packs/abort-promotion.png',
variant: 'promotion',
label: 'Special promotion',
info: '+ 5,000 purchases this week',
expirationDate: '2026-12-31T23:59:59Z',
value: {
price: 89.99,
previousPrice: 99.99,
items: [
{ currencyId: 'gc', amount: 10000 },
{ currencyId: 'sc', amount: 100 }
]
}
}
};
virtual.setAttribute('packs', JSON.stringify(packs));
Type Standard Retina
Regular, promoted, upsell 154x77px 308x154px
Special, abort 328x164px 656x328px

The retina size is recommended for a sharp display on mobile devices.

Declaring categories turns the browsing area into category navigation: a swipeable row of pills, starting with a fixed Featured pill, then a Custom amount pill when custom amounts are enabled for your operator, then one pill per category. When the row overflows, scroll arrows appear, and the pills can be navigated with the keyboard.

  • Category pills follow the order of the categories array.
  • Featured is the default view: it shows every category as a horizontal row of packs, in pill order.
  • Selecting a category pill shows that category’s packs as a grid. Every list shows promoted packs first, then regular packs, each in array order.
  • A pack can list several categories in categoryIds and appears in each of them.
  • Packs with no categoryIds, or only unknown ids, are collected in a trailing Other group, so every pack stays purchasable.
  • Categories without packs are hidden.
  • Custom amount replaces the pack area with the amount input field.
  • The Featured, Custom amount and Other labels are configurable in your content set.

Without categories, the widget shows a single grid of promoted and regular packs behind a Packs / Custom amount switch. When custom amounts are disabled for your operator, the switch is removed and the grid is shown on its own.

The three promotional slots (promotedSpecial, promotedUpsell, promotedOnAbort) are never grouped into a category and ignore categoryIds.

Set selected: true on an entry of categories to open the cashier on that category instead of Featured, for example when the player opens the cashier from a specific currency on your site. No pack is selected, so the player still picks one from the category.

  • Only the first category marked selected is used.
  • If that category is unknown or has no packs, the cashier opens on Featured.
  • A pack marked selected takes precedence: the cashier preselects that pack and ignores the category.
  • The preselection applies when the purchase step opens. Set it on the packs attribute before opening the cashier.
  • Opening the cashier to Quick Buy (transaction="quick-deposit") ignores the preselection, as it does for a preselected pack.

The virtual-currencies, balance and packs payloads are versioned. Payloads without a version field are read as the v1 shape, so existing integrations keep working unchanged; new integrations should send version: 2. A virtual-currencies payload in the v2 shape that leaves out version: 2 is rejected. If you only need the previous type definitions, stay on the 1.x line of the @fluidpayments/types package.

When migrating a v1 integration to version: 2, the fields map as follows:

v1 field v2 equivalent
virtual-currencies.depositCurrency A currency in currencies[], referenced by primaryId
virtual-currencies.withdrawalCurrency A currency in currencies[], referenced by secondaryId
Pack value.deposit / value.withdrawal Entries in pack value.items[] ({ currencyId, amount })
balance.depositCurrencyBalance balance.primaryBalance
balance.withdrawalCurrencyBalance balance.secondaryBalance
balance.lockedWithdrawalCurrencyBalance balance.lockedSecondaryBalance

In v2 every currency has a stable id (v1 had none), and primaryId, secondaryId and each pack item’s currencyId reference currencies by that id, not by code.

Within a pack, label replaces the previous title property, and the green, red and yellow variants replace best_value, discount and exclusive. Packs that still use the old names keep working, because Fluid converts them on input, but new integrations should use the new names. The conversion keeps two side effects of the old variants:

  • exclusive packs become yellow. They still get priceHighlighted: true, but that flag is now ignored, so the price is shown in the accent colour only when the pack has a previousPrice.
  • discount packs that carry a discount text but no label get label set to the discount text, so the badge keeps its text.

Unlike the objects above, this one isn’t passed as an attribute: it’s the argument to fluid.init() for Programmatic initialisation.

type FluidInitParams = {
operatorId: number;
userId: string;
sessionId: string;
locale: string;
countryCode: string;
currencyCode: string;
bonuses?: FluidBonusData;
userData?: FluidUserData;
};

The fields match the identity attributes, with three differences: operatorId is a number, and the country and currency are countryCode and currencyCode. bonuses takes the same data as the bonuses attribute (see Bonus Data), and userData a FluidUserData object — both as objects, not stringified JSON.