Product hydration lets you update product information at runtime before it is displayed to the user. This is useful for syncing prices, availability, descriptions, and variants with your own backend.
The Firework SDK supports two hydration modes:
Mode
Trigger
Scope
Listener
Video-level
A video or ivestream with products starts playing
Products from a single video/livestream
setOnProductHydrationListener
Feed-level
A page of feed data is loaded
Products aggregated from all videos in the page
setOnFeedProductHydrationListener
Both modes use the same ProductHydrator API to modify products. The sections below explain each mode and the full hydration API.
Hydration Level
Feed-Level Product Hydration
Note: Feed-level hydration currently only supports short videos. Livestream content is not supported.
Feed-level hydration batches all products from a loaded page of feed data into a single callback, instead of issuing one callback per video. Products are aggregated across all videos in the page and deduplicated by product ID.
This is the recommended approach for feed views (e.g. FwPlayerDeckView) where multiple videos are loaded at once, as it reduces the number of hydration requests from N (one per video) to 1 (one per page).
Setup
Register the listener via FireworkSdk.shopping.setOnFeedProductHydrationListener:
Aggregated and deduplicated products from all videos in the loaded page.
hydrator
ProductHydrator
Builder used to modify product fields. Same API as video-level hydration.
Unlike video-level hydration, the callback does not include a videoInfo parameter because the products span multiple videos.
❗ You must call hydrator.completeHydration() when finished. Without this call, updated values will not be applied.
Behavior
Applies to all view types: Feed-level hydration is a global listener. Once set, it takes effect for all SDK view components (FwPlayerDeckView, FwVideoFeedView, FwStoryBlockView).
Automatic per-item disabling (DeckView inline only): When a feed-level listener is set, per-item hydration is automatically disabled only for FwPlayerDeckView inline cards. It does not disable per-item hydration for fullscreen playback (whether launched from FwPlayerDeckView or FwVideoFeedView) or FwStoryBlockView.
Coexistence: Setting both setOnFeedProductHydrationListener and setOnProductHydrationListener at the same time is technically supported. If both are set, feed-level handles DeckView inline hydration while video-level handles fullscreen and StoryBlock. However, this is not recommended — see Best Practices below.
Timeout: The SDK waits up to 10 seconds for completeHydration() to be called. If the timeout is exceeded or an exception occurs, the original (non-hydrated) products are displayed.
Pagination: Each page load triggers a separate hydration callback. Products that were already hydrated in a previous page are not requested again.
Video-Level Product Hydration
Video-level hydration is triggered each time a video with associated products starts playing. The SDK calls your listener with the products for that specific video.
Register the listener via FireworkSdk.shopping.setOnProductHydrationListener:
Callback Parameters
Parameter
Type
Description
products
List<Product>
Products associated with the video that is about to play.
hydrator
ProductHydrator
Builder used to modify product fields.
videoInfo
VideoInfo
Information about the video that requested hydration.
❗ You must call hydrator.completeHydration() when finished. Without this call, updated values will not be applied.
❗ All code examples below only show the hydrate {} block for brevity. Refer to the example above for the full listener setup pattern. Remember to always call hydrator.completeHydration() after all hydrations are done.
Best Practices
For the simplest and most predictable integration, set only one hydration listener — either feed-level or video-level — rather than both.
Recommendation
When to use
Feed-level only (setOnFeedProductHydrationListener)
Your app uses FwPlayerDeckView and you want to reduce hydration requests from N-per-page to 1-per-page.
Video-level only (setOnProductHydrationListener)
You need per-video videoInfo in the callback, or your app primarily uses video and also livestream.
Setting a single listener avoids confusion about which listener handles which context, simplifies debugging, and ensures consistent hydration logic across all surfaces.
Product Hydration
Use ProductBuilder methods inside the hydrate {} block to modify product-level properties. These methods apply to both video-level and feed-level hydration.
Basic Attributes
Method
Description
name(name: String)
Sets or updates the product name.
description(description: String)
Sets or updates the product description.
subtitle(subtitle: String?)
Sets or updates the product subtitle. Pass null to clear.
currency(currency: String)
Sets the product-level currency code string (e.g. "USD", "EUR").
isAvailable(isAvailable: Boolean)
Sets the availability status of the product.
Main Product Image
Method
Description
mainProductImage(url: String)
Sets or updates the main product image URL. If a main image already exists, updates its URL; otherwise creates a new image entry.
Display Control
Method
Description
isDisabled(isDisabled: Boolean)
Sets the disabled status of the product. A disabled product is treated as unavailable.
hidePrice(hidePrice: Boolean)
Controls whether the product price is hidden from display.
hidden(hidden: Boolean?)
Sets whether the product should be hidden entirely. Pass null to use the default behavior.
CTA (Call-To-Action) Customization
Method
Description
customCTATitle(title: String?)
Sets a custom CTA button title. Pass null to clear.
customCTATitleTranslation(title: String?)
Sets a translated CTA title for localization. Pass null to clear.
customCTAUrl(url: String?)
Sets the URL the CTA button links to. Pass null to clear.
customCTATarget(target: String?)
Sets the CTA target identifier. Pass null to clear.
hidePrimaryCTA(hide: Boolean?)
Controls whether the primary CTA button is hidden. Pass null to use the default.
Variant Management
Add a new variant
Appends a new variant to the existing list of product variants. Image management is handled automatically.
Remove a variant
Removes a variant by its unique identifier. If the variant has an associated image, the image is also removed from the product's image lists.
Clear all existing variants
Removes all variants from the product, including their associated images.
❗ You must add at least one variant after clearing all existing variants.
Replace existing variants with new variants
Replaces the entire set of variants at once. This clears existing variants internally and sets the new list, which is useful for refreshing variant offerings.
Modify existing variants
Locates a variant by its unique identifier and applies modifications through a ProductVariantBuilder block. If no variant with the given ID is found, the call is a no-op.
Important Notes
allowedVariantOptions
Sets the list of attribute names that are permissible for product variants. This ensures that all product variants conform to the expected attributes such as color, size, material, etc.
❗ Call this method before adding new variants when using clearVariants() or replaceVariants() to ensure compatibility.
completeHydration()
Must be called after all hydrate() calls are finished. Without this call, the SDK will not apply any hydrated values.
hydrator.hydrate(product.id) {
allowedVariantOptions(listOf("color", "size"))
// Now add variants that have "color" and "size" options
addVariant(...)
}
interface ProductHydrator {
fun hydrate(
productId: String,
builderBlock: ProductBuilder.() -> ProductBuilder,
)
fun completeHydration(): List<Product>
class ProductBuilder {
// -- Basic Attributes --
fun name(name: String): ProductBuilder
fun description(description: String): ProductBuilder
fun subtitle(subtitle: String?): ProductBuilder
fun currency(currency: String): ProductBuilder
fun isAvailable(isAvailable: Boolean): ProductBuilder
// -- Main Product Image --
fun mainProductImage(url: String): ProductBuilder
// -- Display Control --
fun isDisabled(isDisabled: Boolean): ProductBuilder
fun hidePrice(hidePrice: Boolean): ProductBuilder
fun hidden(hidden: Boolean?): ProductBuilder
// -- CTA Customization --
fun customCTATitle(title: String?): ProductBuilder
fun customCTATitleTranslation(title: String?): ProductBuilder
fun hidePrimaryCTA(hide: Boolean?): ProductBuilder
fun customCTATarget(target: String?): ProductBuilder
fun customCTAUrl(url: String?): ProductBuilder
// -- Variant Management --
fun addVariant(variant: ProductUnit): ProductBuilder
fun removeVariant(id: String): ProductBuilder
fun clearVariants(): ProductBuilder
fun replaceVariants(variants: List<ProductUnit>): ProductBuilder
fun allowedVariantOptions(options: List<String>): ProductBuilder
fun variant(
id: String,
builderBlock: ProductVariantBuilder.() -> ProductVariantBuilder,
): ProductBuilder
}
class ProductVariantBuilder {
fun name(name: String): ProductVariantBuilder
fun price(price: Double): ProductVariantBuilder
fun originalPrice(price: Double): ProductVariantBuilder
fun currency(currencyCode: CurrencyCode): ProductVariantBuilder
fun url(url: String): ProductVariantBuilder
fun imageUrl(url: String): ProductVariantBuilder
fun isAvailable(isAvailable: Boolean): ProductVariantBuilder
fun unitOptions(options: List<ProductUnitOption>): ProductVariantBuilder
}
}