Skip to main content

Offers Player API JavaScript SDK v2.0.0 Integration Guide

This guide explains how to integrate the Dynata Offers Player API JavaScript SDK v2.0.0 into your website to display and manage offers using the new native endpoint architecture.

Version 2.0.0 of the SDK contains improved authentication, state management, and performance. It is, however, a major version release and therefore introduces breaking changes. Please review the migration guide below before upgrading.


Getting Started

Include the SDK Script

Add the appropriate script to your site’s HTML:

QA Environment:
<script src="https://sdk.surveyb.dev/2.0.0/sdk.min.js"></script>

PROD Environment:
<script src="https://sdk.surveyb.in/2.0.0/sdk.min.js"></script>

Initialization

Before calling any SDK methods, initialize the SDK.

await window.dynataOffersSdk.init({
params: "<URL_PARAMS_STRING>",
appUserId: "<UNIQUE_USER_IDENTIFIER>"
});

Parameters

  • params – Unique integration identifier (signed URL params from backend).
  • appUserId – Unique user identifier. This value is hashed internally using SHA-256 before being sent to the API.

Initialization Flow (v2.0)

During initialization, the SDK now performs the following steps automatically:

  1. Hashes the appUserId
  2. Authenticates with the backend
  3. Retrieves publisher configuration
  4. Retrieves geolocation and panelist configuration
  5. Stores access tokens locally
  6. Enables silent token refresh
info

Because of this, init() must always be awaited.


Theme Customization (Optional)

You may customize the SDK theme globally.

window.dynataOffersSdk.configureTheme({
primary: "#FF5733",
secondary: "#4CAF50"
});

This sets CSS variables on the page:

--primary-color
--secondary-color

These are applied to all SDK UI components.


Fetching Offers

const offers = await window.dynataOffersSdk.fetchOffers({
os: ["Android", "iOS", "Desktop"], // optional: array of device types
limit: 20, // optional
offset: 0, // optional
includeGoals: true // optional default = true
});
const featuredOffers = await window.dynataOffersSdk.fetchFeaturedOffers({
os: ["Android", "iOS", "Desktop"], // optional: array of device types
limit: 20, // optional
offset: 0, // optional
includeGoals: true // optional default = true
});

Fetch In-Progress Offers

Offers that the user has already started.

const inProgressOffers = await window.dynataOffersSdk.fetchOffersInProgress();

Fetch Banner Ads

Fetch promotional banner ads for the current user.

const bannerAds = await window.dynataOffersSdk.fetchBannerAds({
os: ["Android", "iOS", "Desktop"] // optional: array of device types
});

Offer Object Format

Normal and in-progress offers share the same response structure. The difference is based on which endpoint is used and the user’s interaction state, not the object format.


{
id: 62927,
title: "Solitaire Stash: Win Real Cash",
reward: 2.43,
rewardString: "$2.43",
thumbnailUrl: "https://example.com/thumbnail.jpg",
heroImageUrl: "https://example.com/hero.jpg",
description: [
"Play and win real cash",
"Complete levels to earn rewards"
],
instructions: ["Install the app", "Complete 5 games"],
requirements: ["Android 10+"],
tags: ["featured", "puzzle"],
platforms: ["Phone", "Tablet"],
operatingSystems: ["Android", "iOS"],
featuredRank: 1,
bannerAd: {
header: "Limited Time Bonus",
buttonText: "Continue",
description: "Finish remaining goals to earn rewards"
},
categories: ["Gaming"],
promotion: {
multiplier: 2.0,
originalReward: 1.21,
originalRewardString: "$1.21"
},
standardGoals: [
{
id: 410521,
title: "Complete 5 games",
description: "Complete 5 games",
reward: 1.43,
rewardString: "$1.43",
isCompleted: false,
sortOrder: 1,
attributionWindowMinutes: 800,
completeByUtc: "2026-02-10T14:37:22Z",
promotion: null
}
],
purchaseGoals: [
{
id: 410741,
title: "Purchase Starter Pack",
description: "Buy the starter pack",
reward: 3.43,
rewardString: "$3.43",
isCompleted: false,
sortOrder: 1,
attributionWindowMinutes: 1400,
completeByUtc: "2026-02-15T10:00:00Z",
promotion: null
}
],
attributionWindowMinutes: 1400,
attemptedAt: "2026-02-06T13:24:39.692Z",
completeByUtc: "2026-02-20T00:00:00Z"
}

Promotion Object

When active, the promotion field contains:

{
multiplier: 2.0,
originalReward: 1.21,
originalRewardString: "$1.21"
}

When promotion is null, no promotion is active.


Goal Object Format

{
id: 410521,
title: "Complete 5 games",
description: "Complete 5 games",
reward: 1.43,
rewardString: "$1.43",
isCompleted: false,
isPending: false,
sortOrder: 1,
attributionWindowMinutes: 800,
completeByUtc: "2025-09-11T14:37:22Z",
promotion: null
}

Rendering an Offer Modal

Render any fetched offer in a done-for-you modal that handles display of offer details, QR code, and offer navigation.

Render Available Offer

window.dynataOffersSdk.renderOfferModal(offer);

Render In-Progress Offer

window.dynataOffersSdk.renderOfferModal(offer, true);
info

In v2.x renderOfferModal requires a full Offer object instead of an ID.

You can retrieve a deep link and QR code for an offer. This allows you to send users directly to the offer without the need to render the done-for-you modal

const { link, qrCode } = await window.dynataOffersSdk.getOfferLink(offer.id);

Returned object:

{
link: "https://example.com/deeplink",
qrCode: "data:image/png;base64,..."
}

Custom Session Tracking

You may attach a custom session identifier.

window.dynataOffersSdk.setSessionId("test-session-id");

This value is sent with offer attempts and returned in S2S callbacks as SessionId.


Example Integration Flow with Tiles

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Offer Wall</title>
<style>
body {
font-family: sans-serif;
margin: 20px;
}
.offers-grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.offer-tile {
width: 200px;
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
cursor: pointer;
}
.offer-img {
width: 100%;
height: 120px;
object-fit: cover;
}
</style>
</head>
<body>
<h2>Available Offers</h2>
<div id="offers" class="offers-grid"></div>

<script src="https://sdk.surveyb.in/2.0.0/sdk.min.js"></script>
<script>
async function setupOffers() {
await window.dynataOffersSdk.init({
params: "<SIGNED_PARAMS_FROM_BACKEND>",
appUserId: "user@example.com"
});

const offers = await window.dynataOffersSdk.fetchOffers();

const container = document.getElementById("offers");

offers.forEach((offer) => {
const tile = document.createElement("div");
tile.className = "offer-tile";

tile.onclick = () => {
window.dynataOffersSdk.renderOfferModal(offer);
};

tile.innerHTML = `
<img class="offer-img" src="${offer.thumbnailUrl}" />
<div>${offer.title}</div>
<div>${offer.rewardString}</div>
`;

container.appendChild(tile);
});
}

setupOffers();
</script>
</body>
</html>

Migrating from v1.x to v2.0.0

Version 2.0.0 introduces breaking changes and is not backward compatible.

Major Changes

Areav1.xv2.0.0
ArchitectureLegacy JSPreact + Zustand + TS
AuthBasicToken-based + refresh
EndpointsLegacyNative endpoints
Modal APIID-basedObject-based
PersistenceNoneLocalStorage
Offer SchemaFlatExtended

API Changes

Fetching Offers

In v2.0.0, the featured parameter has been removed from fetchOffers. Featured offers are now retrieved through the dedicated fetchFeaturedOffers() method.

renderOfferModal

v1.x:

renderOfferModal(offerId);

v2.x:

renderOfferModal(offerObject);
info

You must pass the full Offer object returned by fetchOffers().


setSessionId

v1.x:

setSessionID("abc");

v2.x:

setSessionId("abc");
info

Method name is now camelCase.


Offer Description

v1.x:

description: "Play and win";

v2.x:

description: ["Play and win"];
info

Always handle description as an array.


Migration Example

v1.x

const offers = await sdk.fetchOffers();
sdk.renderOfferModal(offers[0].id);

v2.x

const offers = await sdk.fetchOffers();
sdk.renderOfferModal(offers[0]);

Common Migration Issues

  • ❌ Not awaiting init()
  • ❌ Passing offer IDs to renderOfferModal
  • ❌ Using setSessionID instead of setSessionId
  • ❌ Assuming description is a string
  • ❌ Ignoring token expiration

Troubleshooting

ErrorDescriptionResolution
Missing appUserIdSDK not initializedCall init() first
Access token expiredToken expiredAllow auto re-auth
renderOfferModal requires an offer objectInvalid argumentPass full offer
Authentication failedInvalid paramsVerify backend signature

Storage and Persistence

v2.x stores limited session data in localStorage under:

sdk-storage

Stored fields include:

  • linkParams
  • appUserId (hashed)
  • accessToken
  • tokenTimestamp
  • theme

This enables session restoration and silent re-authentication.