Catalog ManagementReal-time Catalog

Offer Templates

Send template and slot names with each offer so rewards, buy buttons, and text land in the template slots meant for them.

When your offer templates name their slots, send the slot names with each offer, so each reward, each buy button and each piece of text lands in the slot meant for it. Without them, the web shop fills a template's slots by position: rewards in contents order, and text from the offer's fixed fields such as name. Position breaks when a template has more slots than the fixed fields, or when a new template version orders its slots differently.

All four fields are optional. A response without them stays valid: the web shop shows the offer in your shop's default template for its type and fills the slots by position.

FieldWhereValue
templateKeyA product, a pack, a free item or an info item. On a pack, set it on the pack; its items have no templateKey.The template that shows the offer, including its version, for example starter-offer-v2. 1 to 128 characters.
layoutKeyEach reward: an entry in contents of a product, a pack item, a free item or an info item, and an entry in items of a chain linkThe reward slot this reward fills. 1 to 128 characters.
buyButton.layoutKeyThe buyButton in attributes of a product or of a pack itemThe buy button slot this offer's or price point's buy button sits in. 1 to 128 characters.
textSlotsA product, a pack, a pack item, a free item or an info itemAn object that maps each text slot name to a LocalizableText, for example Title or RibbonText. Keys up to 128 characters, at most 50 entries.

Agree your templates, their versions and their slot names with your Stash engineering partner. Stash compares every templateKey, layoutKey, buyButton.layoutKey and textSlots key exactly as sent: case-sensitive, untrimmed, and with no prefix matching. Title and title are different slots, and starter-offer-v2 and starter-offer-v3 are different templates.

product using named slots
{
  "product": {
    "productId": "starter_bundle",
    "price": { "priceId": "starter_bundle_499" },
    "name": { "defaultText": "Starter Bundle" },
    "templateKey": "starter-offer-v2",
    "textSlots": {
      "Title": { "defaultText": "Spring Starter Bundle", "localizationKey": "offer.starter.title" },
      "RibbonText": { "defaultText": "Best value", "localizationKey": "offer.ribbon.best_value" }
    },
    "contents": [
      { "name": { "defaultText": "Coins" }, "quantity": "5000", "layoutKey": "TopLeftReward" },
      { "name": { "defaultText": "Gems" }, "quantity": "50", "layoutKey": "BottomReward" }
    ]
  }
}

Here the offer shows in version 2 of the starter-offer template. Its Title slot shows "Spring Starter Bundle", not the name, and each reward fills the slot its layoutKey names.

Template choice

A templateKey that is missing, empty, longer than 128 characters, or not one of your shop's templates is ignored: the offer shows in your shop's default template for its type. A template that names no reward slots ignores layoutKey, one that names no buy button slots ignores buyButton.layoutKey, and one that names no text slots ignores textSlots.

Reward slots

  • Keyed rewards first. A reward whose layoutKey names a reward slot of the template fills that slot. When several rewards name the same slot, the first in contents fills it.
  • Then the rest, in order. Every other reward fills the remaining reward slots in contents order: a reward without a layoutKey, one whose layoutKey names no slot of the template, and a later reward that names an already filled slot. Every reward is still shown.
  • Empty or long keys. An empty layoutKey, or one longer than 128 characters, counts as no key.
  • Rewards and slots that do not match up. With more rewards than reward slots, the template shows the extra rewards as it does for any offer that overflows it. With fewer, the unfilled slots stay empty.

Buy button slots

A template can give each buy button a slot of its own, separate from the slots its rewards fill, for example one buy button slot per price point of a pack. Name it in the buy button's layoutKey, in attributes.buyButton of the product or the pack item.

  • The label stays the price. text is optional on a buy button. Send buyButton with only layoutKey to name the slot and keep the button's default label, the price.
  • Buy buttons only. layoutKey is read on a buy button. On an info button it is ignored.
  • Empty or long keys. An empty layoutKey, or one longer than 128 characters, counts as no key.
  • Buttons without a matching slot. When a buy button has no layoutKey, has one that names no buy button slot of the template, or names the same slot as another buy button of the offer, the template decides where that button sits.
  • Display only. The slot never changes what is bought, charged or sent to ConfirmPayment.

Text slots

  • Slot text wins. A text slot shows its textSlots value. Without one, it shows the fixed field the template fills that slot from, for example name for a title slot. Stash defines that field for each template. A slot with no value in either place stays empty, with no placeholder text.
  • Localization. Each textSlots value is a LocalizableText, localized like every other text field (see Localization). When its localizationKey has no translation for the player's language, the slot shows the entry's defaultText. When that is empty too, the slot shows its fixed field. Both cases are reported in CATALOG_TEXT_FALLBACK when catalog diagnostics are on for your shop.
  • Ignored entries. An entry whose key is empty, longer than 128 characters or not a text slot of the template is ignored, and so is an entry whose text is empty or resolves to empty. The offer still shows. Text of only spaces counts as a value.
  • Limit. Stash keeps at most 50 entries with text, the first 50 by slot name in byte order.
  • Repeated names. Send each slot name once. If a name repeats within one textSlots object, Stash uses its first value and the rest of the catalog still loads.
  • The offer card only. textSlots changes only the offer card. Checkout and receipts show the fixed fields, so always send name (title on an info item).

Slots on purchase packs

Set templateKey and textSlots on the pack, once for the whole offer. A price point (an item in the pack's items) can add its own textSlots:

  • For a slot set on both, the price point's value is used for that price point. The pack's entries fill every other slot.
  • A price point entry that is ignored, for example an empty text or a key over 128 characters, leaves the pack's entry for that slot.
  • Of the combined entries, Stash keeps the first 50 by slot name in byte order.

A pack has no buy button of its own: each price point names its buy button slot in its own attributes.buyButton. Here each price point's rewards fill a Resources_* slot and its buy button sits in a SKU_* slot:

pack with a buy button slot per price point
{
  "pack": {
    "id": "three_price_offer",
    "templateKey": "three-price-offer-v1",
    "items": [
      {
        "productId": "offer_tier_1",
        "price": { "priceId": "offer_tier_1_499" },
        "name": { "defaultText": "Small" },
        "contents": [{ "name": { "defaultText": "Coins" }, "quantity": "1000", "layoutKey": "Resources_Left" }],
        "attributes": { "buyButton": { "layoutKey": "SKU_Left" } }
      },
      {
        "productId": "offer_tier_2",
        "price": { "priceId": "offer_tier_2_999" },
        "name": { "defaultText": "Medium" },
        "contents": [{ "name": { "defaultText": "Coins" }, "quantity": "2500", "layoutKey": "Resources_Middle" }],
        "attributes": { "buyButton": { "layoutKey": "SKU_Center" } }
      },
      {
        "productId": "offer_tier_3",
        "price": { "priceId": "offer_tier_3_1999" },
        "name": { "defaultText": "Large" },
        "contents": [{ "name": { "defaultText": "Coins" }, "quantity": "6000", "layoutKey": "Resources_Right" }],
        "attributes": { "buyButton": { "layoutKey": "SKU_Right" } }
      }
    ]
  }
}

These fields change only how the offer card shows the offer. What Stash sends to ConfirmPayment, and what your backend grants, do not change.

Slot names belong to a template version. When you move an offer to a new version in templateKey, send that version's slot names. A name the version does not have falls back as above: the reward fills a remaining slot in contents order, and the text slot shows its fixed field.

How is this guide?

On this page