Touchpoints API Reference
REST and Unity SDK reference for PlaySuper touchpoints, the reward widgets you render inside your own game UI
A touchpoint is a reward widget that lives on one of your game's screens instead of inside the store. Your game asks PlaySuper what to show, receives artwork plus an offer, draws it, and opens the store when the player taps it.
There are two touchpoint systems. Both are served from the same API and authenticated the same way.
| System | Endpoint | What you get back | Status |
|---|---|---|---|
| Touchpoints 2.0 | GET /v2/touchpoints/serve | One frame image, one offer image, one CTA, priced for this player. No layout to interpret. | Recommended for new integrations |
| Touchpoints 1.0 | GET /touchpoints/... | A tree of nodes with images, text, badges and hydrated reward or product data. You render the layout. | Supported, no new features |
New integration? Use Touchpoints 2.0. The node tree API further down is still served for games already built on it, but new touchpoints are configured as 2.0 placements from the console.
Authentication
Every touchpoint endpoint takes your Game API key in the x-api-key header. The key identifies the game and the environment (a test key is served by dev.playsuper.club, a live key by api.playsuper.club). See API keys for the key formats.
| Header | Description | Required |
|---|---|---|
x-api-key | Your Game API key | Yes |
Authorization | Bearer <player access token>. Optional. When present, 1.0 reward and product data is hydrated for that player. | No |
In Unity, the SDK adds these headers for you once it is initialized:
// Once, at game start. The key is required; there is no parameterless overload.
PlaySuperUnitySDK.Initialize("YOUR_API_KEY");
// The SDK attaches the player token automatically after LoginFederatedByStudio.Touchpoints 2.0
The studio picks a frame for a screen in the console. At runtime the game names the screen it is about to show, and the server returns that frame plus one concrete offer already priced for this player and this game's coin. The game draws two images and opens one URL.
Serve a screen's touchpoint
GET /v2/touchpoints/serve?screen=home
x-api-key: YOUR_API_KEYcurl "https://api.playsuper.club/v2/touchpoints/serve?screen=home&utm_source=level_end" \
-H "x-api-key: YOUR_API_KEY"Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
screen | string | Yes | The screen about to be shown. Only home is live today. An unknown value returns 400 naming the valid screens. |
type | string | No | Which offer kind to serve: product, coupon or gift-card. Omit it when the screen has one placement. If the screen has more than one placement and type is missing, the call is rejected with 400 rather than picking for you. |
utm_source, utm_medium, utm_campaign | string | No | Appended to the CTA URL so the store visit is attributed to this touchpoint. |
Response 200 (the data payload of the standard response envelope):
{
"screen": "home",
"placementId": "9b1d7d2e-4c3a-4f7e-a2c1-0d8f1e6b2a11",
"asset": {
"source": "PRESET",
"imageUrl": "https://storage.googleapis.com/playsuper-touchpoints/assets/home/frame-01.png",
"orientation": "PORTRAIT",
"width": 720,
"height": 1280
},
"item": {
"type": "PRODUCT",
"id": "prod_8f3a",
"title": "boAt Airdopes 141",
"subtitle": "True wireless earbuds",
"brandName": "boAt",
"imageUrl": "https://storage.googleapis.com/playsuper-touchpoints/items/prod_8f3a.png",
"pricing": {
"currency": "INR",
"mrp": 1999,
"price": 1299,
"priceAfterCoins": 999,
"discountPct": 35
},
"coins": {
"coinId": "YOUR_COIN_ID",
"coinName": "Gold Coin",
"coinImageUrl": "https://assets.playsuper.club/coins/gold.png",
"coinsRequired": 300,
"coinDiscount": 300
},
"cta": {
"label": "Claim now",
"url": "https://store.playsuper.club/gcommerce/products/prod_8f3a?utm_source=level_end"
}
},
"servedAt": "2026-09-16T09:12:44.000Z"
}Response fields
| Field | Notes |
|---|---|
placementId | null when this game has no active placement for the screen. That is a normal state, not an error. |
asset | The frame you draw. source is PRESET (PlaySuper library) or UPLOAD (the studio's own artwork). orientation is PORTRAIT or LANDSCAPE. |
asset.imageUrl | The frame image. The field is imageUrl, not assetUrl. |
item.type | PRODUCT, COUPON or GIFT_CARD. |
item.imageUrl | The offer art. Draw it in the centre of the frame. |
item.pricing | Rupee figures: mrp (struck through), price (cash before coins), priceAfterCoins, discountPct. null for coupons and for stale price snapshots. Render without a price in that case. |
item.offerText | Coupons only. A pre-formatted headline such as "Flat 40% off up to ₹200". Display it as given. |
item.coins | coinsRequired and the rupee coinDiscount they buy. Omitted when this game has no linked coin or the coin cannot pay for any of the discount. Never rendered as "0 coins". |
item.cta | label is the button copy. url is an absolute store URL with your UTM parameters already appended. Open it with the SDK's OpenStore or in your WebView. |
"Nothing to show" is not an error. asset and item are always both present or both null. Both are null when the studio has not configured this screen, or has but no catalogue item is available right now. The call still returns 200. Render the screen without a touchpoint and move on. Only a non-2xx status means the request failed.
Every figure is computed server-side for this player and this game's coin. The game does no pricing arithmetic.
Errors
| Status | Cause |
|---|---|
400 | Unknown screen (the message lists the valid values), or the screen has several placements and type was omitted |
401 | Missing or invalid API key, or a key for the other environment |
Unity
TouchpointV2Manager wraps this endpoint with a typed response and an OpenCta helper that preserves attribution. It ships in SDK 4.5.0, which is not yet published on OpenUPM (the latest published version is 4.4.0). Until then call the endpoint directly as shown above. See Touchpoints & Curated Lists for the Unity API.
How touchpoints are created
You cannot create touchpoints through the runtime API. The endpoints on this page read them.
- Touchpoints 2.0 placements are configured by the studio. In the PlaySuper Console, open your game's Integration page while in the Testing environment and pick a frame for the Home screen. Placements made in sandbox are copied to production when you go live. The PlaySuper for Claude connector does the same from a conversation: upload your screens, accept the placements the model proposes, and they are live.
- Touchpoints 1.0 node trees are authored by PlaySuper for your game. Send the layouts you need and the reward or product references to engineering@playsuper.club, and you will receive the touchpoint names to fetch. The names in the examples below (
flash-sale-card,refer-and-earn,FTUE) are illustrations, not touchpoints that exist on your game by default.
Touchpoints 1.0 (node tree)
Each 1.0 touchpoint contains a tree of nodes with visual assets, text and optional linked data (rewards or products). Your game walks the tree and renders each node with a template of its own.
REST endpoints
List all touchpoints
Retrieves all active touchpoints for your game with hydrated node data.
GET /touchpoints?coinId={coinId}| Query parameter | Type | Required | Description |
|---|---|---|---|
coinId | string | Yes | Coin used for pricing calculations |
Get touchpoint by name
Retrieves a single touchpoint by its unique name. This is the primary runtime call for 1.0 games.
GET /touchpoints/name/{name}?coinId={coinId}| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
name | path | string | Yes | Unique touchpoint name |
coinId | query | string | Yes | Coin used for pricing calculations |
Get touchpoint by ID
GET /touchpoints/{id}?coinId={coinId}| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string (UUID) | Yes | Touchpoint UUID |
coinId | query | string | Yes | Coin used for pricing calculations |
Omitting coinId does not return an error. The touchpoint comes back with unhydrated nodes (no reward or product data), so always pass it.
Unity SDK methods
All 1.0 methods are static members of TouchpointManager in the PlaySuperUnity namespace. Each returns null when the request fails.
GetTouchpointByName
public static async Task<TouchpointResponse> GetTouchpointByName(string name, string coinId)TouchpointResponse store = await TouchpointManager.GetTouchpointByName("flash-sale-card", "coin-uuid-abcd");
if (store != null)
{
Debug.Log($"Loaded: {store.name} with {store.nodes?.Length ?? 0} nodes");
}GetTouchpointById
public static async Task<TouchpointResponse> GetTouchpointById(string id, string coinId)TouchpointResponse offer = await TouchpointManager.GetTouchpointById(
"6f9a5737-0247-4a71-89eb-b5d02212f179",
"coin-uuid-abcd"
);ListTouchpoints
public static async Task<TouchpointListResponse> ListTouchpoints(string coinId)TouchpointListResponse list = await TouchpointManager.ListTouchpoints("coin-uuid-abcd");
if (list != null)
{
Debug.Log($"Found {list.total} touchpoints");
foreach (var tp in list.touchpoints)
{
Debug.Log($"- {tp.name} (ID: {tp.id})");
}
}Data types
TouchpointResponse
public class TouchpointResponse
{
public string id; // Touchpoint UUID
public string gameId; // Game UUID
public string name; // Unique touchpoint name
public string updatedAt; // ISO 8601 timestamp of the last update
public TouchpointNode[] nodes; // Root nodes
}TouchpointListResponse
public class TouchpointListResponse
{
public TouchpointResponse[] touchpoints;
public int total;
}TouchpointNode
public class TouchpointNode
{
// Identity
public string id; // Node UUID
public int displayOrder; // Sort order among siblings
// Visual assets
public BackgroundConfig background;
public string[] images; // Primary content images
public BackgroundConfig overlay;
public string[] additionalAssets;
// Text
public TextItem[] title;
public TextItem[] subtitle;
// Interactive elements
public BadgeConfig badge;
public CtaConfig cta;
// Data references
public string dataType; // ASSET | STATIC | DYNAMIC, see below
public string rewardId; // Linked reward ID
public string productId; // Linked product ID
// Layout
public string layoutHint; // Template name, see below
public JObject styleHint; // Free-form styling metadata
// Hydrated data
public JToken data; // Populated reward or product data
// Nesting
public TouchpointNode[] nodes; // Child nodes
public TouchpointNode popup; // Node shown when this one is tapped
}| Helper | Returns | Description |
|---|---|---|
HasReward | bool | True if the node carries hydrated reward data |
HasProduct | bool | True if the node carries hydrated product data |
GetData<T>(key, defaultValue) | T | Read a value from the hydrated data object |
GetStyleHint<T>(key, defaultValue) | T | Read a value from styleHint |
GetReward() | HydratedReward | Typed reward data |
GetProduct() | HydratedProduct | Typed product data |
Enumerations and conventions
dataType says where a node's content comes from.
| Value | Meaning | What to render |
|---|---|---|
ASSET | Purely visual. No linked reward or product. This is the default. | The node's own background, images, title, badge and cta exactly as given. |
STATIC | Points at a specific reward or product through rewardId or productId (on the node or on its children). | The node's visuals, plus the hydrated data (GetReward() or GetProduct()) for price, brand and stock. data is only filled when coinId was passed. |
DYNAMIC | Configured with a query instead of fixed IDs. At request time the server replaces the node with a flat list of sibling nodes, one per matching item, each marked DYNAMIC with its own hydrated data and a generated cta.action. | Treat the returned siblings as a list or carousel. Never expect the original node back. |
layoutHint names the UI template the touchpoint author intended, for example flash-sale-card, banner-card, popup-card, container or card. It is a free-form string agreed between you and PlaySuper when the touchpoint is authored, not a closed list. Map each value you agreed on to a template in your game and fall back to a generic card for anything else.
cta.action is opened when the player taps the node. PlaySuper does not interpret it.
- A store URL (
https://store.playsuper.club/...) should be passed toPlaySuperUnitySDK.Instance.OpenStore(action)so the player lands on that page with their session. - Any other value is a deep link into your own game, agreed when the touchpoint was authored. The
navigate:/shopvalues in the FTUE example below are such game-internal routes; thenavigate:prefix is a convention for that example, not a PlaySuper scheme. - For
DYNAMICnodes the server generatesactionfrom the configured URL prefix and the item's ID.
BackgroundConfig
public class BackgroundConfig
{
public string color; // e.g. "#1A1A2E"
public string gradient; // e.g. "linear-gradient(...)"
public string[] images; // Background image URLs
public float? opacity; // 0 to 1
}TextItem
public class TextItem
{
public string text;
public string color;
public float? fontSize;
public string fontWeight; // e.g. "bold"
public string icon; // Icon URL
}BadgeConfig
public class BadgeConfig
{
public string text;
public string backgroundImage;
public string icon;
public JObject style;
}CtaConfig
public class CtaConfig
{
public string text; // Button text
public string action; // URL or game deep link, see conventions above
public string backgroundImage;
public string frontImage;
public JObject style;
}HydratedReward
public class HydratedReward
{
public string id;
public string brandId;
public string brandName;
public string organizationId;
public bool isActive;
public string brandRedirectionLink;
public RewardMetadata metadata;
public InventoryInfo inventory;
public PriceInfo[] price;
}RewardMetadata
public class RewardMetadata
{
public string brandName;
public string[] brandCategory;
public string brandLogoImage;
public string campaignTitle;
public string campaignSubTitle;
public string campaignCoverImage;
public string campaignDetails;
public string termsAndConditions;
public string howToRedeem;
public string brandRedirectionLink;
public bool? couponExpiryDateExists;
public string couponExpiryDate;
public string type;
}HydratedProduct
public class HydratedProduct
{
public string id;
public string type;
public string productId;
public string name;
public string brandName;
public string imageUrl;
public string heroImageUrl;
public string category;
public float? rating;
public float? listingPrice; // Original price
public float? discountedListingPrice; // Final price after discount
public float? mrp;
public float? discountPercent;
public float? coinSpread;
public float? coinRequiredForDiscount;
public float? coinsRequiredForMaxDiscount;
}InventoryInfo and PriceInfo
public class InventoryInfo
{
public int availableQuantity;
public string type;
}
public class PriceInfo
{
public float amount;
public string coinId;
}Example touchpoints
1. Flash sale card (ASSET)
A promotional banner with a countdown timer. Pure visual assets, no linked rewards.
{
"id": "uuid",
"gameId": "uuid",
"name": "flash-sale-card",
"nodes": [
{
"id": "uuid",
"displayOrder": 0,
"dataType": "ASSET",
"layoutHint": "flash-sale-card",
"background": {
"images": ["https://.../{gameId}/flash-sale-card/nodes/0/bg.png"]
},
"images": [
"https://.../{gameId}/flash-sale-card/nodes/0/product.png",
"https://.../{gameId}/flash-sale-card/nodes/0/flash-sale-logo.png"
],
"badge": {
"text": "Up to 85%",
"backgroundImage": "https://.../{gameId}/flash-sale-card/nodes/0/badge.png"
},
"cta": {
"action": "https://store.playsuper.club/gcommerce"
},
"styleHint": {
"timerEnabled": true
}
}
]
}2. Refer and earn (STATIC with popup)
Reward banners that open a popup when tapped.
{
"id": "6f9a5737-0247-4a71-89eb-b5d02212f179",
"name": "refer-and-earn",
"nodes": [
{
"id": "0f171ae3-da25-4f09-b200-b3c318123e8f",
"displayOrder": 0,
"dataType": "STATIC",
"images": ["https://.../refer-and-earn/nodes/0/img.png"],
"rewardId": "6ab8709b-4be9-4b84-b69b-7944e6d69b30",
"layoutHint": "banner-card",
"popup": {
"id": "508c3f3b-074e-4c39-8c58-2a249218ecb6",
"displayOrder": 0,
"images": ["https://.../refer-and-earn/nodes/0/popup/img.png"],
"title": [{ "text": "Woohoo!", "color": "#FFFFFF" }],
"subtitle": [{ "text": "Get your coupon", "color": "#FFFFFF" }],
"cta": {
"text": "Get it for FREE",
"action": "https://store.playsuper.club/rewards/..."
},
"layoutHint": "popup-card",
"data": {
/* HydratedReward */
}
},
"data": {
/* HydratedReward */
}
}
]
}3. FTUE onboarding flow (nested nodes)
A container with child cards. The navigate: actions are game-internal routes agreed for this touchpoint.
{
"id": "uuid",
"name": "FTUE",
"nodes": [
{
"id": "container-uuid",
"displayOrder": 0,
"dataType": "ASSET",
"layoutHint": "container",
"background": { "images": ["https://.../FTUE/nodes/0/bg.png"] },
"title": [
{
"text": "Win Matches & Get Discount!",
"color": "#FFFFFF",
"fontSize": 20,
"fontWeight": "bold"
}
],
"cta": {
"text": "Shop Now",
"action": "navigate:/shop",
"backgroundImage": "https://.../FTUE/nodes/0/cta.png"
},
"nodes": [
{
"id": "card-1-uuid",
"displayOrder": 0,
"layoutHint": "card",
"images": ["https://.../FTUE/nodes/0/nodes/0/img.png"],
"cta": { "text": "Play Game", "action": "navigate:/game" }
},
{
"id": "card-2-uuid",
"displayOrder": 1,
"layoutHint": "card",
"images": ["https://.../FTUE/nodes/0/nodes/1/img.png"],
"cta": { "text": "Earn Gems", "action": "navigate:/gems" }
}
]
}
]
}4. Exclusive deal widget (layered assets)
{
"id": "uuid",
"name": "home-exclusive-deal-widget",
"nodes": [
{
"id": "uuid",
"displayOrder": 0,
"dataType": "ASSET",
"layoutHint": "card",
"background": { "images": ["https://.../nodes/0/bg.png"] },
"images": ["https://.../nodes/0/img.png"],
"overlay": {
"images": [
"https://.../nodes/0/overlay-1.png",
"https://.../nodes/0/overlay-2.png"
]
},
"badge": { "backgroundImage": "https://.../nodes/0/badge.png" },
"styleHint": {
"aspectRatio": "1:1",
"cornerRadius": 12
}
}
]
}Unity usage example
using UnityEngine;
using PlaySuperUnity;
public class FlashSaleExample : MonoBehaviour
{
async void Start()
{
TouchpointResponse flashSale = await TouchpointManager.GetTouchpointByName(
"flash-sale-card",
"coin-uuid-abcd"
);
if (flashSale?.nodes?.Length > 0)
{
TouchpointNode node = flashSale.nodes[0];
// Basic fields
string nodeId = node.id;
int order = node.displayOrder;
string layout = node.layoutHint; // "flash-sale-card"
// Background and primary images
string bgImage = node.background?.images?[0];
string productImage = node.images?[0];
string logoImage = node.images?[1];
// Badge
string badgeText = node.badge?.text; // "Up to 85%"
string badgeBgImage = node.badge?.backgroundImage;
// CTA
string ctaAction = node.cta?.action; // store URL
string ctaText = node.cta?.text;
// Style hints
bool timerEnabled = node.GetStyleHint<bool>("timerEnabled", false);
float cornerRadius = node.GetStyleHint<float>("cornerRadius", 0);
string aspectRatio = node.GetStyleHint<string>("aspectRatio", null);
// Overlay, title and subtitle when present
string overlayImage = node.overlay?.images?[0];
string titleText = node.title?[0]?.text;
string titleColor = node.title?[0]?.color;
float? titleFontSize = node.title?[0]?.fontSize;
// Nested nodes
if (node.nodes?.Length > 0)
{
foreach (var childNode in node.nodes)
{
// Access child node fields the same way
}
}
// Popup
if (node.popup != null)
{
string popupTitle = node.popup.title?[0]?.text;
string popupCtaText = node.popup.cta?.text;
}
// Reward data (STATIC or DYNAMIC nodes with a rewardId)
if (node.HasReward)
{
HydratedReward reward = node.GetReward();
string brandName = reward?.brandName;
string campaignTitle = reward?.metadata?.campaignTitle;
float? price = reward?.price?[0]?.amount;
}
// Product data (STATIC or DYNAMIC nodes with a productId)
if (node.HasProduct)
{
HydratedProduct product = node.GetProduct();
string productName = product?.name;
float? productPrice = product?.discountedListingPrice;
}
}
}
}Field access quick reference
| Field | Access pattern |
|---|---|
| Background image | node.background?.images?[0] |
| Primary images | node.images?[0], node.images?[1] |
| Badge text | node.badge?.text |
| Badge background | node.badge?.backgroundImage |
| CTA action | node.cta?.action |
| CTA text | node.cta?.text |
| Layout hint | node.layoutHint |
| Style hint (bool) | node.GetStyleHint<bool>("key", false) |
| Style hint (string) | node.GetStyleHint<string>("key", null) |
| Title text | node.title?[0]?.text |
| Title color | node.title?[0]?.color |
| Nested nodes | node.nodes |
| Popup node | node.popup |
| Reward data | node.GetReward() |
| Product data | node.GetProduct() |
Error handling
REST
| Status | Description |
|---|---|
| 400 | Invalid request |
| 401 | Invalid or missing API key |
| 404 | Touchpoint not found |
| 500 | Server error |
{
"statusCode": 404,
"message": "Touchpoint \"invalid-name\" not found for game \"game-uuid\"",
"error": "Not Found"
}Unity
public async Task SafeLoadTouchpoint()
{
try
{
TouchpointResponse tp = await TouchpointManager.GetTouchpointByName(
"my-touchpoint",
"coin-uuid-abcd"
);
if (tp == null)
{
Debug.LogWarning("Touchpoint not found or request failed");
ShowFallbackUI();
return;
}
if (tp.nodes == null || tp.nodes.Length == 0)
{
Debug.LogWarning("Touchpoint has no nodes");
ShowFallbackUI();
return;
}
await RenderTouchpoint(tp);
}
catch (Exception e)
{
Debug.LogError($"Failed to load touchpoint: {e.Message}");
ShowFallbackUI();
}
}
private void ShowFallbackUI()
{
// Show default or cached content
}| Error | Cause | Solution |
|---|---|---|
API key not set | SDK not initialized | Call PlaySuperUnitySDK.Initialize("YOUR_API_KEY") before any touchpoint call |
name is required | Empty touchpoint name | Provide the touchpoint name PlaySuper gave you |
coinId is required | Empty coin ID | Provide the coin ID from the console |
404 Not Found | Touchpoint does not exist on this game | Check the name with PlaySuper; names are case sensitive |
401 Unauthorized | Invalid API key, or a test key against production | Check the key and the environment it belongs to |
Best practices
- Cache touchpoints. Call
ListTouchpointsat startup and cache what you use often. - Handle null safely. Every optional field can be missing. Use
?.throughout. - Sort by
displayOrderbefore rendering siblings. - Preload images asynchronously and cache them.
- Map
layoutHintto templates you agreed on, with a generic fallback. - Check
popupbefore navigating on tap.
Visual documentation
An interactive guide shows how each 1.0 API field maps to a rendered element. Hover a JSON field to highlight the matching part of the widget. It covers four examples: a pure asset touchpoint, products with static assets, a dynamic product touchpoint, and FTUE nested cards.
Interactive guide
Related guides
- Touchpoints & Curated Lists (Unity): rendering touchpoints from the Unity SDK
- API Reference: players, coins, rewards and API keys
- Webhooks: server-side notifications for coin transactions
- Console Setup: where touchpoint placements are configured