Touchpoints & Curated Lists
Fetch and display reward widgets, product collections, and promotional content in your Unity game
Beyond the built-in store, you can render reward widgets and product collections directly in your game UI using touchpoints and curated lists.
Touchpoints 2.0
Needs PlaySuper Unity SDK 4.5.0 or later, published to OpenUPM on 18 September 2026.
TouchpointV2Manager does not exist in 4.4.0, so the code on this page will not compile
against it. Check your version in Packages/manifest.json or in
Window > Package Manager > PlaySuper, and upgrade with
openupm add com.playsuper.unity@4.5.0.
On 4.5.0 exactly, read the call-to-action from tp.item.cta rather than tp.cta — the
typed model in that release carries it on the item. The API sends it in both places, so
either works today and tp.cta is the one to keep.
You can also call the endpoint directly from Unity or your backend:
GET https://api.playsuper.club/v2/touchpoints/serve?screen=home
x-api-key: YOUR_API_KEYThe request and response are documented in the
Touchpoints API Reference. It is the same endpoint this
manager wraps; you lose the typed model and OpenCta's attribution handling, nothing else.
TouchpointV2Manager and TouchpointManager are different systems, despite the similar
names. The TouchpointManager methods further down are the older node-based touchpoints: they
take a coinId and return a different shape. Upgrading to 4.5.0 changes nothing about them, and
nothing migrates on its own — an existing integration keeps working untouched until you port it
deliberately.
A touchpoint is an offer the studio places on one of their game's screens from the PlaySuper console (your game's Integration page in the Testing environment; see Console Setup) or through the PlaySuper for Claude connector. The game names the screen it is about to show; the server returns the artwork the studio chose and one concrete offer already composed into it — priced for this player, with the coin maths done. Your game draws two images and opens a URL.
Fetching a touchpoint
using PlaySuperUnity;
async void ShowHome()
{
var tp = await TouchpointV2Manager.Serve(TouchpointV2Manager.Screens.Home);
if (tp != null && tp.HasTouchpoint)
{
// tp.FrameImageUrl — the frame the studio picked
// tp.OfferImageUrl — the offer art, drawn in its centre
// tp.cta.label — the button copy (also on tp.item.cta for SDK 4.5.0)
ShowTouchpoint(tp);
}
}"Nothing to show" is not an error. A non-null result with HasTouchpoint false means the
studio has not configured this screen, or has but no catalogue item is available right now.
Both are ordinary states and the call succeeded. Only null means the request itself failed.
Treat an unconfigured screen as normal and render without a touchpoint.
asset and item are always both present or both absent — a frame with an empty centre reads as
a broken build — so HasTouchpoint is a single check rather than two.
Opening the offer
TouchpointV2Manager.OpenCta(tp);Pass the response rather than building a URL. The server has already appended campaign attribution
to cta.url, and OpenStore reads it back off — a hand-built URL silently loses attribution for
every touchpoint your game opens.
Parameters
| Parameter | Type | Description |
|---|---|---|
screen | string | Which screen is about to be shown. See TouchpointV2Manager.Screens. Only home is live today; an unrecognised screen returns a 400 naming the valid ones. |
type | string | Optional. product, coupon or gift-card. Needed only when the screen has more than one placement — an ambiguous call is rejected rather than picked for you. |
utmSource / utmMedium / utmCampaign | string | Optional. Appended to the CTA URL. |
await TouchpointV2Manager.Serve(
TouchpointV2Manager.Screens.Home,
TouchpointV2Manager.ItemType.Product,
utmSource: "level_end");Response
public class TouchpointV2Response
{
public string screen;
public string placementId; // null when no placement is configured
public TouchpointV2Asset asset; // the frame
public TouchpointV2Item item; // the offer inside it
public string servedAt;
public bool HasTouchpoint { get; } // asset != null && item != null
public string FrameImageUrl { get; }
public string OfferImageUrl { get; }
public bool IsPortrait { get; }
}| Field | Notes |
|---|---|
asset.imageUrl | The frame you draw. Not assetUrl. |
asset.orientation | PORTRAIT or LANDSCAPE. |
item.imageUrl | The offer art, drawn in the frame's centre. |
item.pricing | currency, mrp, price, priceAfterCoins, discountPct. Null for coupons — prices are nested here, not on item itself. |
item.offerText | Pre-formatted coupon line, e.g. "Flat 40% off up to ₹200". Display as given. |
item.coins | coinsRequired and coinDiscount. Null when this game's coin cannot be spent on the item. |
cta | label and url, at the response level — not on item — because a static touchpoint has no item to carry it. Hand the whole response to OpenCta rather than using the URL directly. Also mirrored on item.cta for SDK 4.5.0, which reads it there; new code should use tp.cta. |
Every figure is computed server-side for this player and this game's coin. The game does no pricing arithmetic.
Touchpoints
Touchpoints are visual widget configurations that control how rewards and promotions appear in your game. Use the TouchpointManager to fetch touchpoint data and render custom widgets.
This is the original node-based API, and it is still supported. New integrations should prefer Touchpoints 2.0 above, where the server composes the widget for you instead of returning a node tree to walk.
Fetching touchpoints
// Fetch by name (recommended for most use cases)
TouchpointResponse store = await TouchpointManager.GetTouchpointByName("flash-sale-card", "coin-uuid-abcd");
// Fetch by ID
TouchpointResponse offer = await TouchpointManager.GetTouchpointById(
"6f9a5737-0247-4a71-89eb-b5d02212f179",
"coin-uuid-abcd"
);
// List all active touchpoints
TouchpointListResponse list = await TouchpointManager.ListTouchpoints("coin-uuid-abcd");Processing touchpoint data
if (store?.nodes?.Length > 0)
{
TouchpointNode node = store.nodes[0];
Debug.Log($"Layout: {node.layoutHint}");
string bgImage = node.background?.images?[0];
string titleText = node.title?[0]?.text;
string titleColor = node.title?[0]?.color;
string ctaAction = node.cta?.action;
string ctaText = node.cta?.text;
bool timerEnabled = node.GetStyleHint<bool>("timerEnabled", false);
if (node.HasReward)
{
HydratedReward reward = node.GetReward();
string brandName = reward?.brandName;
}
}For the full data model, example responses, what dataType, layoutHint and cta.action
mean, and how these touchpoints are created for your game, see the
Touchpoints API Reference.
Curated lists
Curated lists are pre-configured collections of products or rewards set up by PlaySuper. Fetch them by name and display them in your game's UI.
Fetching a curated list
// Products
var products = await PlaySuperUnitySDK.Instance.GetCuratedList(
CuratedListType.Products,
"homepage_featured",
"your-coin-id"
);
// Rewards
var rewards = await PlaySuperUnitySDK.Instance.GetCuratedList(
CuratedListType.Rewards,
"daily_deals",
"your-coin-id"
);| Parameter | Type | Required | Description |
|---|---|---|---|
type | CuratedListType | Yes | CuratedListType.Products or .Rewards |
listName | string | Yes | Name of the curated list (provided by PlaySuper) |
coinId | string | Yes | Your game's coin ID |
version | string | No | API version for rewards (e.g., "2.0.0") |
Working with products
public async void LoadFeaturedProducts()
{
try
{
var response = await PlaySuperUnitySDK.Instance.GetCuratedList(
CuratedListType.Products,
"homepage_featured",
"your-coin-id"
);
foreach (var product in response.products)
{
Debug.Log($"Product: {product.name}");
Debug.Log($"Brand: {product.brandName}");
Debug.Log($"Image: {product.imageUrl}");
var sku = product.skus[0];
if (sku.playSuperPrice != null)
{
Debug.Log($"Price: {sku.playSuperPrice.discountedListingPrice}");
}
}
}
catch (Exception ex)
{
Debug.LogError($"Failed to load products: {ex.Message}");
}
}Key product fields:
| Field | Description |
|---|---|
name | Product display name |
brandName | Brand name |
imageUrl | Primary product image |
images | Additional product images |
description | HTML description |
plainTextDescription | Plain text description |
skus | Available variants with pricing |
optionTypes | Product options (size, color, etc.) |
ctaUrl | Deep link to view/purchase in store |
SKU pricing (sku.playSuperPrice):
| Field | Description |
|---|---|
listingPrice | Original price |
discountedListingPrice | Final price after discount |
discountPercent | Discount percentage |
Working with rewards
public async void LoadRewards()
{
try
{
var response = await PlaySuperUnitySDK.Instance.GetCuratedList(
CuratedListType.Rewards,
"gift_cards",
"your-coin-id"
);
foreach (var reward in response.rewards)
{
Debug.Log($"Title: {reward.metadata.campaignTitle}");
Debug.Log($"Brand: {reward.brandName}");
Debug.Log($"Image: {reward.metadata.campaignCoverImage}");
if (reward.price.Count > 0)
{
Debug.Log($"Price: {reward.price[0].amount} coins");
}
Debug.Log($"Available: {reward.inventory.availableQuantity}");
}
}
catch (Exception ex)
{
Debug.LogError($"Failed to load rewards: {ex.Message}");
}
}Key reward fields:
| Field | Description |
|---|---|
brandName | Brand name |
metadata.campaignTitle | Reward title |
metadata.campaignSubTitle | Reward subtitle |
metadata.campaignCoverImage | Cover image URL |
metadata.brandLogoImage | Brand logo URL |
metadata.termsAndConditions | Terms and conditions |
metadata.howToRedeem | Redemption instructions |
price[0].amount | Price in coins |
inventory.availableQuantity | Stock remaining |
ctaUrl | Deep link to store page |
Deep-linking to a product or reward
Both products and rewards include a ctaUrl that opens the store directly to that item:
PlaySuperUnitySDK.Instance.OpenStore(product.ctaUrl);
PlaySuperUnitySDK.Instance.OpenStore(reward.ctaUrl);
// With UTM tracking
PlaySuperUnitySDK.Instance.OpenStore(product.ctaUrl, "featured_banner");Contact your PlaySuper integration team to get curated list names and configure custom lists for your game.
Next, see the Complete Example for a full integration script, offline support, user properties, and troubleshooting.