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:
- Hashes the
appUserId - Authenticates with the backend
- Retrieves publisher configuration
- Retrieves geolocation and panelist configuration
- Stores access tokens locally
- Enables silent token refresh
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
});
Fetching Featured Offers
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);
In v2.x renderOfferModal requires a full Offer object instead of an ID.
Retrieving Offer Links
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 = `
`;
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
| Area | v1.x | v2.0.0 |
|---|---|---|
| Architecture | Legacy JS | Preact + Zustand + TS |
| Auth | Basic | Token-based + refresh |
| Endpoints | Legacy | Native endpoints |
| Modal API | ID-based | Object-based |
| Persistence | None | LocalStorage |
| Offer Schema | Flat | Extended |
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);
You must pass the full Offer object returned by fetchOffers().
setSessionId
v1.x:
setSessionID("abc");
v2.x:
setSessionId("abc");
Method name is now camelCase.
Offer Description
v1.x:
description: "Play and win";
v2.x:
description: ["Play and win"];
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
setSessionIDinstead ofsetSessionId - ❌ Assuming
descriptionis a string - ❌ Ignoring token expiration
Troubleshooting
| Error | Description | Resolution |
|---|---|---|
Missing appUserId | SDK not initialized | Call init() first |
Access token expired | Token expired | Allow auto re-auth |
renderOfferModal requires an offer object | Invalid argument | Pass full offer |
Authentication failed | Invalid params | Verify 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.