Redemption Widget
The Redemption Widget lets a buyer act on the vouchers (rNFTs) they already hold. It shows their exchanges in the protocol, lets them redeem, cancel before redemption, raise disputes after redemption, and (for sellers) revoke or complete exchanges. The widget detects what state each exchange is in and surfaces only the actions that make sense.
Where it shines: when you sell vouchers on a different surface (your own storefront, the Boson dApp, an NFT marketplace, a metaverse, an in-game shop) but want one canonical place buyers come back to redeem. Drop the widget on your domain and your customers redeem there.
The hosted build is at widgets.bosonprotocol.io — under the hood it's a React app over @bosonprotocol/react-kit's <RedemptionWidget />.
Quickest embed: script + button
<script async type="text/javascript" src="https://widgets.bosonprotocol.io/scripts/boson-widgets.js"></script>
<button id="boson-redeem" data-config-id="production-137-0">Redeem</button>The loader script finds the button by its id, mounts the modal, and handles wallet connect. The buyer signs in with whatever EIP-1193 wallet they prefer; the widget filters to their committed exchanges automatically.
Optional Boson-branded styling
<head>
<link rel="stylesheet" href="https://widgets.bosonprotocol.io/styles.css">
</head>
<body>
<button id="boson-redeem" class="bosonButton" data-config-id="production-137-0">Show Redeem</button>
</body>Filtering and deep-linking
| Attribute | Effect |
|---|---|
data-config-id (required) | Deployment to query — see Networks → ConfigIds matrix. |
data-account | Restrict to a single buyer wallet (otherwise uses the connected wallet). |
data-exchange-id | Open directly to one exchange — buyer skips the selection screen. |
data-seller-ids (comma-list) or data-seller-id | Filter to exchanges from one or more sellers. |
data-signatures (comma-list) | Multi-seller domain-binding signatures — see WooCommerce → connect. |
data-look-and-feel | "modal" (default) or "regular" for inline rendering. |
data-widget-action | Force a starting screen — SELECT_EXCHANGE (default), EXCHANGE_DETAILS, REDEEM_FORM, CANCEL_FORM, CONFIRM_REDEEM. |
data-exchange-state | Pre-filter to COMMITTED / REDEEMED / DISPUTED / COMPLETED. |
data-show-redemption-overview | "true" (default) shows the overview pane on entry; "false" jumps straight to the action. |
Iframe form (no JS loader)
For sites that block third-party scripts:
<iframe
src="https://widgets.bosonprotocol.io/#/redeem?configId=production-137-0&exchangeId=80"
width="100%" height="800"
allow="clipboard-write; web-share">
</iframe>Every data-* attribute above maps 1:1 to a URL query param of the same name (without the data- prefix; camelCase: exchangeId, widgetAction, lookAndFeel, etc.).
Programmatic open
window.boson.openRedeem({
configId: "production-137-0",
exchangeId: "80",
widgetAction: "REDEEM_FORM",
})Capturing delivery info on your server
When a buyer redeems a physical product, the widget collects the delivery info. You have three options for where it goes:
1. XMTP to the seller (default)
The widget posts the info as an XMTP message keyed to the seller's wallet. The seller's fulfilment system reads it from their XMTP inbox. Zero infrastructure on your end.
Enable: data-send-delivery-info-through-xmtp="true".
2. POST to your endpoint
Your server receives the delivery info as a POST. Useful when you have an existing OMS / WMS / CRM.
<button id="boson-redeem"
data-config-id="production-137-0"
data-post-delivery-info-url="https://yourshop.example.com/api/boson/delivery"
data-post-delivery-info-headers='{"Authorization": "Bearer …"}'>
Redeem
</button>The widget POSTs { exchangeId, deliveryInfo, signature, … }. Validate the signature server-side before processing.
3. postMessage to the parent window
If the widget is embedded in your dApp and you want to handle delivery info entirely in the parent page:
data-target-origin="https://yourshop.example.com"The widget calls window.parent.postMessage({ exchangeId, deliveryInfo, eventTag }, targetOrigin). Useful for fully-custom checkout-style integrations.
Lifecycle callbacks (backend)
Three optional server endpoints fire at the redemption lifecycle:
| Attribute | When it fires |
|---|---|
data-post-redemption-submitted-url | Buyer's redeem transaction broadcast (not yet confirmed). |
data-post-redemption-confirmed-url | Redemption transaction confirmed on-chain. |
data-post-delivery-info-url | Delivery info collected (see above). |
Each has a paired *-headers attribute (JSON-encoded) for auth headers. Treat these as fire-and-forget — the widget doesn't retry, so handlers must be idempotent (key on exchangeId).
React Kit — when you want full control
If you'd rather skip the hosted build:
import { RedemptionWidget } from "@bosonprotocol/react-kit"
<RedemptionWidget
configId="production-137-0"
envName="production"
withReduxProvider
withWeb3React
withGlobalStyle
exchangeId="80"
widgetAction="REDEEM_FORM"
closeWidgetClick={() => { /* … */ }}
deliveryInfoHandler={(info) => { /* … */ }}
redemptionSubmittedHandler={(tx) => { /* … */ }}
redemptionConfirmedHandler={(tx) => { /* … */ }}
// … same props as the URL query params, plus a few extras …
/>See the widgets repo for the full prop catalogue.
Common gotchas
- Wrong chain. The buyer's wallet must be on the chain that matches
configId. The widget prompts a switch but some wallets reject silently. - CSP. Allow
widgets.bosonprotocol.ioinscript-src,frame-src, andconnect-src(the latter for postback URLs and XMTP). - Multi-seller storefronts need per-seller
signaturesto prove origin-binding — otherwise the widget refuses to render to protect buyers from spoofed sellers. - Delivery info handlers are not retried by the widget — your endpoint must be idempotent (dedupe on
exchangeId). Pair with on-chain event listeners (Build → Marketplace operators → Webhooks & events) as the backstop.
Next
- Build → Buyers → Redeem a voucher — what
redeemVoucheractually does. - Build → Buyers → Raise a dispute.
- Widgets → Embedding methods.
- Widgets → Postback URLs & deep links.