Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Recipe — Digital + physical bundle · Boson Protocol
Skip to content

Recipe: Digital + physical bundle

A bundle is a single offer that represents multiple items. The buyer commits once; redemption delivers all items.

// Build BUNDLE metadata directly per the schemathere's no
// bundleMetadataFactory export. See ReferenceMetadata schemas for the
// full shape (required top-level fields: schemaUrl, type, uuid, name,
// description, externalUrl, licenseUrl, image, attributes, items, seller).
const uuid = crypto.randomUUID()
const metadata = {
  schemaUrl: "https://schema.org/Product",
  type: "BUNDLE" as const,
  uuid,
  name: "Hoodie + Discord access",
  description: "A limited hoodie and one year of Discord access.",
  externalUrl: `https://example.com/${uuid}`,
  licenseUrl: `https://example.com/${uuid}/license`,
  image: "https://cdn.example.com/bundle.jpg",
  attributes: [
    { traitType: "Type", value: "Bundle" },
    { traitType: "Items", value: "2" },
  ],
  items: [
    { type: "ITEM_PRODUCT_V1", productV1: { /* full PRODUCT_V1 itemsee ReferenceMetadata schemas */ } },
    { type: "ITEM_NFT", nft: { name: "Discord access pass", description: "1 year of access to our Discord.", image: "https://cdn.example.com/pass.png" } },
  ],
  seller: { defaultVersion: 1, name: "Your brand", contactLinks: [{ tag: "email", url: "mailto:hi@example.com" }] },
}
 
const metadataUri = await sdk.storeMetadata(metadata)
await sdk.createOffer({ /* … */, metadataUri, metadataHash: metadataUri.replace(/^ipfs:\/\//, "") })

At redemption

When the buyer redeems, your fulfillment system delivers both items:

  • Physical: ship the hoodie via your normal logistics.
  • Digital: send the Discord invite via email / XMTP / postback.

Boson doesn't separate the two — they're a single exchange with a single redemption. If something goes wrong with one item, the dispute is over the whole bundle.

Related