PlaySuper LogoPlaySuper
API Reference

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.

SystemEndpointWhat you get backStatus
Touchpoints 2.0GET /v2/touchpoints/serveOne frame image, one offer image, one CTA, priced for this player. No layout to interpret.Recommended for new integrations
Touchpoints 1.0GET /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.

HeaderDescriptionRequired
x-api-keyYour Game API keyYes
AuthorizationBearer <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_KEY
curl "https://api.playsuper.club/v2/touchpoints/serve?screen=home&utm_source=level_end" \
  -H "x-api-key: YOUR_API_KEY"

Query parameters

ParameterTypeRequiredDescription
screenstringYesThe screen about to be shown. Only home is live today. An unknown value returns 400 naming the valid screens.
typestringNoWhich 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_campaignstringNoAppended 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

FieldNotes
placementIdnull when this game has no active placement for the screen. That is a normal state, not an error.
assetThe frame you draw. source is PRESET (PlaySuper library) or UPLOAD (the studio's own artwork). orientation is PORTRAIT or LANDSCAPE.
asset.imageUrlThe frame image. The field is imageUrl, not assetUrl.
item.typePRODUCT, COUPON or GIFT_CARD.
item.imageUrlThe offer art. Draw it in the centre of the frame.
item.pricingRupee 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.offerTextCoupons only. A pre-formatted headline such as "Flat 40% off up to ₹200". Display it as given.
item.coinscoinsRequired 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.ctalabel 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

StatusCause
400Unknown screen (the message lists the valid values), or the screen has several placements and type was omitted
401Missing 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 parameterTypeRequiredDescription
coinIdstringYesCoin 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}
ParameterInTypeRequiredDescription
namepathstringYesUnique touchpoint name
coinIdquerystringYesCoin used for pricing calculations

Get touchpoint by ID

GET /touchpoints/{id}?coinId={coinId}
ParameterInTypeRequiredDescription
idpathstring (UUID)YesTouchpoint UUID
coinIdquerystringYesCoin 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
}
HelperReturnsDescription
HasRewardboolTrue if the node carries hydrated reward data
HasProductboolTrue if the node carries hydrated product data
GetData<T>(key, defaultValue)TRead a value from the hydrated data object
GetStyleHint<T>(key, defaultValue)TRead a value from styleHint
GetReward()HydratedRewardTyped reward data
GetProduct()HydratedProductTyped product data

Enumerations and conventions

dataType says where a node's content comes from.

ValueMeaningWhat to render
ASSETPurely visual. No linked reward or product. This is the default.The node's own background, images, title, badge and cta exactly as given.
STATICPoints 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.
DYNAMICConfigured 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 to PlaySuperUnitySDK.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:/shop values in the FTUE example below are such game-internal routes; the navigate: prefix is a convention for that example, not a PlaySuper scheme.
  • For DYNAMIC nodes the server generates action from 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

FieldAccess pattern
Background imagenode.background?.images?[0]
Primary imagesnode.images?[0], node.images?[1]
Badge textnode.badge?.text
Badge backgroundnode.badge?.backgroundImage
CTA actionnode.cta?.action
CTA textnode.cta?.text
Layout hintnode.layoutHint
Style hint (bool)node.GetStyleHint<bool>("key", false)
Style hint (string)node.GetStyleHint<string>("key", null)
Title textnode.title?[0]?.text
Title colornode.title?[0]?.color
Nested nodesnode.nodes
Popup nodenode.popup
Reward datanode.GetReward()
Product datanode.GetProduct()

Error handling

REST

StatusDescription
400Invalid request
401Invalid or missing API key
404Touchpoint not found
500Server 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
}
ErrorCauseSolution
API key not setSDK not initializedCall PlaySuperUnitySDK.Initialize("YOUR_API_KEY") before any touchpoint call
name is requiredEmpty touchpoint nameProvide the touchpoint name PlaySuper gave you
coinId is requiredEmpty coin IDProvide the coin ID from the console
404 Not FoundTouchpoint does not exist on this gameCheck the name with PlaySuper; names are case sensitive
401 UnauthorizedInvalid API key, or a test key against productionCheck the key and the environment it belongs to

Best practices

  1. Cache touchpoints. Call ListTouchpoints at startup and cache what you use often.
  2. Handle null safely. Every optional field can be missing. Use ?. throughout.
  3. Sort by displayOrder before rendering siblings.
  4. Preload images asynchronously and cache them.
  5. Map layoutHint to templates you agreed on, with a generic fallback.
  6. Check popup before 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.