@datra/engage-sdk
v0.2.0
Published
Official JavaScript and TypeScript SDK for Datra Engage in-app messages, promotions, coupons, and customer engagement tracking.
Maintainers
Readme
Datra Engage SDK helps web apps, mobile apps, React Native apps and embedded client experiences show personalized in-app messages powered by Datra.
Use it when marketing and product teams need to show the right message to the right customer at the right moment without hardcoding every campaign into the application.
Datra Engage is part of the Datra Customer Engagement Platform.
Links
- Website: datra.uz
- API documentation: api.datra.uz/redoc
- npm: @datra/engage-sdk
- GitHub: desli-team/datra-engage-sdk
What is Datra Engage?
Datra Engage is the delivery layer between your app and Datra.
It helps your application receive and render:
- banners
- popups
- coupon cards
- bottom sheets
- stories
- in-app messages linked to push campaigns
- personalized campaign messages
- gamification entry points
Your app keeps full control over the UI.
Datra decides what should be shown.
Your app decides how it should look.
Supported message types
The SDK supports the same message types as Datra Engage API:
| Type | Typical use |
| --- | --- |
| BANNER | Home screen promo, category offer, campaign strip |
| POPUP | Important one-time offer or announcement |
| BOTTOM_SHEET | Cart, checkout or coupon reminder |
| COUPON_CARD | Active coupon, gift or personal offer |
| INLINE_BLOCK | Embedded block inside profile, checkout or loyalty screens |
| CAROUSEL | Multiple offers in one placement |
| STORY | Story-like marketing or gamification entry point |
What this SDK does
The SDK can:
- identify a customer
- resolve messages for a screen or placement
- send screen view events
- track message impressions
- track clicks
- track dismiss events
- track conversions
- work safely in non-blocking UI flows
- retry temporary failed requests
- queue failed tracking events
- deduplicate UI tracking events
What this SDK does not do
For security reasons, this SDK does not:
- apply promotions
- redeem loyalty points
- create orders
- change customer balances
- mutate customer data
- confirm discounts
- validate final checkout logic
All sensitive operations must happen on your backend through Datra Core.
Frontend shows the offer.
Backend confirms the offer.
Installation
npm install @datra/engage-sdkQuick Start
import { DatraEngage } from "@datra/engage-sdk";
const engage = new DatraEngage({
baseUrl: "https://api.datra.uz",
publicKey: "pk_live_xxx",
customerExternalId: "customer_123",
platform: "ios",
appVersion: "1.5.0",
locale: "ru",
defaultContext: {
branchExternalId: "branch_1",
},
});
const { messages } = await engage.screenViewed("home", {
placement: "home_top_banner",
context: {
cartTotal: 120000,
},
});
await engage.trackShown(messages, {
idempotencyKeyPrefix: "home",
});Basic Concept
Datra Engage works around three ideas:
1. Screen
Where the customer is now.
Examples:
"home"
"cart"
"checkout"
"profile"
"order_status"2. Placement
The exact place where a message can appear.
Examples:
"home_top_banner"
"cart_bottom_sheet"
"checkout_coupon_card"
"profile_reward_block"3. Message
The campaign content returned by Datra.
Examples:
"BANNER"
"POPUP"
"COUPON_CARD"
"BOTTOM_SHEET"
"STORY"Render Messages
Datra Engage returns structured data.
Your app renders it using your own native components.
for (const message of messages) {
if (message.type === "BANNER") {
renderBanner({
title: message.title,
body: message.body,
imageUrl: message.imageUrl,
cta: message.cta,
onPress: async () => {
await engage.messageClickedSafe(message.decisionId);
handleDatraAction(message.action);
},
});
}
}Example message:
{
"decisionId": "decision_123",
"type": "BANNER",
"title": "Получите подарок",
"body": "Сделайте заказ от 100 000 сум и получите напиток бесплатно",
"imageUrl": "https://cdn.datra.uz/campaigns/free-drink.png",
"cta": {
"text": "Заказать"
},
"action": {
"type": "OPEN_PROMOTION",
"promotionId": "promotion_123"
}
}Tracking
Tracking helps Datra measure campaign performance.
You can track:
- shown
- clicked
- dismissed
- converted
Strict tracking
Use strict tracking when your app needs to know about failures.
await engage.messageClicked("decision_id", {
idempotencyKey: "click-event-id",
});
await engage.messageDismissed("decision_id");
await engage.messageConverted("decision_id", {
metadata: {
orderExternalId: "order_123",
},
});Safe tracking
Use safe tracking for non-blocking UI events.
Safe methods never throw.
await engage.messageShownSafe("decision_id");
await engage.messageClickedSafe("decision_id");
await engage.messageDismissedSafe("decision_id");Safe methods return:
{
accepted: boolean;
response?: unknown;
error?: unknown;
queued?: boolean;
deduped?: boolean;
}Action Contract
Messages can include an action object. The SDK does not execute actions automatically because each app owns its navigation.
Recommended action types:
"OPEN_SCREEN"
"OPEN_PROMOTION"
"OPEN_COUPON"
"OPEN_PRODUCT"
"OPEN_CART"
"OPEN_URL"
"OPEN_STORY"
"OPEN_MECHANIC"Use the action handler helper to keep app navigation consistent:
import { createDatraEngageActionHandler } from "@datra/engage-sdk";
const handleDatraAction = createDatraEngageActionHandler({
onOpenPromotion: async (promotionId) => {
navigation.navigate("Promotion", { promotionId });
},
onOpenCoupon: async (couponId) => {
navigation.navigate("Coupon", { couponId });
},
onOpenCart: async () => {
navigation.navigate("Cart");
},
onUnhandledAction: async (action) => {
console.warn("Unhandled Datra action", action);
},
});Offline Queue and Dedupe
Safe tracking can queue temporary failures and flush them later.
import { DatraEngage, createWebStorageAdapter } from "@datra/engage-sdk";
const storage = createWebStorageAdapter(window.localStorage, "my-app:");
const engage = new DatraEngage({
baseUrl: "https://api.datra.uz",
publicKey: "pk_live_xxx",
eventQueue: {
enabled: true,
storage,
},
dedupe: {
enabled: true,
storage,
},
});
await engage.flushEventQueue();
await engage.messageShownSafe("decision_id");For React Native, pass an adapter backed by your storage library, for example AsyncStorage:
const asyncStorageAdapter = {
getItem: (key: string) => AsyncStorage.getItem(key),
setItem: (key: string, value: string) => AsyncStorage.setItem(key, value),
removeItem: (key: string) => AsyncStorage.removeItem(key),
};Dedupe is enabled by default for safe tracking. Event queue is opt-in so apps can choose where queued events are stored.
Identity Updates
Use identify when the active customer changes.
identify updates the SDK identity locally and uses it for subsequent resolve and tracking calls.
engage.identify({
customerExternalId: "customer_456",
platform: "android",
appVersion: "1.6.0",
context: {
branchExternalId: "branch_2",
},
});React Native and Expo
import { DatraEngage } from "@datra/engage-sdk";
import { DatraEngageProvider, useEngageMessages } from "@datra/engage-sdk/react";
const engage = new DatraEngage({
baseUrl: "https://api.datra.uz",
publicKey: "pk_live_xxx",
platform: "ios",
appVersion: "1.5.0",
});
export function App() {
return (
<DatraEngageProvider client={engage}>
<HomeScreen customerExternalId="customer_123" />
</DatraEngageProvider>
);
}
function HomeScreen({ customerExternalId }: { customerExternalId: string }) {
const { messages, isLoading, refresh } = useEngageMessages("home", {
placement: "home_top_banner",
customerExternalId,
trackShown: {
idempotencyKeyPrefix: `home:${customerExternalId}`,
},
});
if (isLoading || messages.length === 0) {
return null;
}
return renderInAppMessage(messages[0], { refresh });
}React is an optional peer dependency. The core SDK does not import React unless you use @datra/engage-sdk/react.
More examples:
examples/react-native-home-screen.tsxexamples/web-home.tsexamples/hoty-dogy-action-handler.ts
Example Flow
A customer opens the app.
const { messages } = await engage.screenViewed("home");Datra returns a personalized campaign.
renderInAppMessage(messages[0]);The app tracks that it was shown.
await engage.messageShownSafe(messages[0].decisionId);The customer clicks the message.
await engage.messageClickedSafe(messages[0].decisionId);The app opens the offer, product, coupon or checkout screen.
handleDatraAction(messages[0].action);The backend confirms the promotion through Datra Core.
Recommended Integration Pattern
Datra Engage should be non-blocking.
If Datra Engage is unavailable, your app should continue working normally.
Recommended behavior:
- app opens normally
- catalog works normally
- cart works normally
- checkout works normally
- only marketing messages are hidden
Example:
try {
const { messages } = await engage.screenViewed("home");
showMessages(messages);
} catch {
showMessages([]);
}Timeout and Retry
Requests time out after 5 seconds by default and retry once on temporary failures.
const engage = new DatraEngage({
baseUrl: "https://api.datra.uz",
publicKey: "pk_live_xxx",
timeoutMs: 3000,
retry: {
retries: 2,
retryDelayMs: 200,
maxRetryDelayMs: 1000,
},
onError: (error, context) => {
console.warn("Datra Engage request failed", {
path: context.path,
attempt: context.attempt,
retryable: context.retryable,
error,
});
},
});Disable retries:
const engage = new DatraEngage({
baseUrl: "https://api.datra.uz",
publicKey: "pk_live_xxx",
retry: 0,
});API Contract
The SDK calls:
POST /api/v1/engage/messages/resolve
POST /api/v1/engage/eventsAuthorization uses the public SDK key:
x-datra-public-key: pk_live_xxxBy default, the SDK adds /api/v1 to baseUrl. You can override this with apiPrefix.
Security Model
Use a public SDK key in frontend applications.
publicKey: "pk_live_xxx"Never use a secret key inside a mobile app or browser.
Sensitive actions must be handled by your backend:
- order creation
- promotion application
- coupon redemption
- loyalty point redemption
- bonus balance changes
Package Scripts
npm run build
npm run typecheck
npm testBrand Principle
Datra Engage is not just a message delivery SDK.
It is a bridge between customer data and real customer action.
Use it to turn app screens into personalized engagement moments.
