Skip to content

Web SDK

The Web SDK (@tolinku/web-sdk) provides event tracking, smart banners, in-app messages, referral management, and deferred deep link claiming for browser and Node.js environments.

Terminal window
npm install @tolinku/web-sdk
import { Tolinku } from '@tolinku/web-sdk';
const tolinku = new Tolinku({
apiKey: 'tolk_pub_your_key',
baseUrl: 'https://your-app.tolinku.com' // optional
});

The baseUrl defaults to https://api.tolinku.com. Set it to your Appspace’s domain if you use a custom domain.

Set a user ID for analytics attribution and segment targeting:

tolinku.setUserId('user_123');
// Clear the user ID (e.g. on logout)
tolinku.setUserId(null);

Track custom events:

// Simple event
tolinku.track('custom.page_view');
// Event with properties
tolinku.track('custom.purchase', {
campaign: 'spring-sale',
source: 'email',
user_id: 'user_123'
});

Events are batched automatically (10 events or every 5 seconds) and sent via navigator.sendBeacon on page unload to avoid losing data.

You can force an immediate flush:

await tolinku.flush();

Track purchases, cart activity, and product events via tolinku.ecommerce:

// Set user ID for attribution
tolinku.setUserId('user_123');
// Track a purchase
await tolinku.ecommerce.purchase({
transaction_id: 'order_456',
revenue: 49.99,
currency: 'USD',
items: [
{ item_id: 'sku_1', item_name: 'T-Shirt', price: 24.99, quantity: 2 }
]
});
// Track product views
await tolinku.ecommerce.viewItem({
items: [{ item_id: 'sku_1', item_name: 'T-Shirt', price: 24.99 }]
});
// Track add to cart
await tolinku.ecommerce.addToCart({
items: [{ item_id: 'sku_1', quantity: 1 }]
});
// Track checkout flow
await tolinku.ecommerce.beginCheckout({});
await tolinku.ecommerce.addPaymentInfo();
// Search and product interactions
await tolinku.ecommerce.search({ search_term: 'red shoes' });
await tolinku.ecommerce.rate({ item_id: 'sku_1', rating: 4.5, max_rating: 5 });

Ecommerce events are batched separately from custom events (10 events or 5-second timer) and sent to the ecommerce batch endpoint. The SDK automatically manages a cart ID across cart events and clears it after purchase.

Display a smart app banner on your web page:

// Show the highest-priority active banner
await tolinku.showBanner();
// Filter by label
await tolinku.showBanner({ label: 'summer-promo' });
// Dismiss the current banner
tolinku.dismissBanner();

showBanner() accepts the same configuration as banner.js, just as a typed options object instead of data attributes:

await tolinku.showBanner({
style: 'floating', // or 'pinned' | 'stacked'
animation: 'pop',
theme: 'dark',
position: 'top',
anchor: '#site-header', // stacked mode only — explicit injection point
// Banner shape
bg: '#3B82F6',
border: undefined,
radius: 16,
margin: 12,
shadow: 'lg',
// Title
titleColor: '#ffffff',
titleSize: 14,
titleWeight: 600,
// Body
bodyColor: '#dbeafe',
bodySize: 12,
bodyWeight: 400,
// CTA
ctaBg: '#ffffff',
ctaColor: '#3B82F6',
ctaSize: 13,
ctaWeight: 600,
ctaRadius: 100,
// Icon
iconSize: 40,
iconRadius: 10,
// Toggles
hideIcon: false,
hideClose: false,
hideBody: false,
// Custom CSS hook
customClass: 'my-banner',
// Filter and timing
label: 'summer-promo',
delay: 0,
});

All options are optional; omit any you don’t need. See the banner script reference for value ranges, defaults, and the resolution chain.

Display in-app messages:

// Show the highest-priority undismissed message
await tolinku.showMessage();
// Filter by trigger
await tolinku.showMessage({ trigger: 'on_open' });
// Dismiss the current message
tolinku.dismissMessage();

Messages are rendered as a DOM modal overlay.

Access referral methods via tolinku.referrals:

// Create a referral code
const { referral_code, referral_url } = await tolinku.referrals.create({
userId: 'user_123',
userName: 'Jane Doe'
});
// Look up a referral
const info = await tolinku.referrals.get('ABC123');
// Link a referred user (status stays pending until reward milestone is reached)
await tolinku.referrals.complete({
code: 'ABC123',
referredUserId: 'user_456'
});
// Update milestone (completes the referral if it matches the reward milestone)
await tolinku.referrals.milestone({
code: 'ABC123',
milestone: 'first_purchase'
});
// Claim reward (after granting it in your system)
await tolinku.referrals.claimReward('ABC123');
// Get leaderboard
const { leaderboard } = await tolinku.referrals.leaderboard(10);

Mostly this does not come up on the open web, where the browser follows a link to Tolinku and lands on the page it resolves to. It comes up constantly inside a Capacitor or Cordova app, where the operating system hands the web layer the URL that was tapped and the routing happens in JavaScript.

A link arrives as the URL that was tapped, exactly as it was written. That is enough while the URL is readable, but every route also has a short link, and a short link is the same route written as a code:

https://links.example.com/s7k2p9q/4821

Nothing in that URL says which route it is, and nothing on the device can work it out. Short links are what the dashboard’s copy button gives you and what a QR code carries, so your app will receive them whether or not you chose to share them.

links.resolve asks Tolinku and answers with the route, the token and the canonical path. A readable URL resolves to itself, so resolve every incoming link rather than trying to spot the short ones. It returns nothing rather than throwing when it cannot reach us, so your own handling stays the fallback.

const link = await tolinku.links.resolve(url);
const path = link?.deep_link_path ?? new URL(url).pathname;
// path -> "/merchant/abc123", and link.token -> "abc123"

token saves you working out which segment it is, which the URL alone does not tell you when a route’s prefix places its token mid-path.

Recover the link a visitor tapped before they had your app, once.

const link = await tolinku.deferred.claimDeferredLink({
appspaceId: '64f0a1b2c3d4e5f60718',
});
if (link) {
// Route to link.deep_link_path
}

There is no install referrer in a browser, so this is device signal matching: timezone, language, screen size and pixel ratio are compared against what the landing page recorded when the link was tapped. Matching is probabilistic and the window is short.

On Android the same call tries the Play referrer first, which is deterministic. That difference is in the SDKs, not in your code: the call is the same name with the same arguments on every platform.

A claim is consumed the first time it succeeds. claimDeferredLink remembers that it asked, in localStorage, so calling it again costs nothing.

Only a settled answer is remembered. “Nothing waiting for this device” counts, because no amount of asking will change it. A dropped request, or a 403 from an appspaceId you are about to correct, does not.

If storage is unavailable, as in a private window, the claim is attempted rather than skipped: an extra request costs less than an install nobody can attribute.

Both are still available and unchanged. They ask every time they are called and do no remembering.

const byToken = await tolinku.deferred.claimByToken(token);
const bySignals = await tolinku.deferred.claimBySignals({ appspaceId });

For single-page applications, clean up when unmounting:

tolinku.destroy();

This flushes any queued events, removes DOM elements (banners, messages), and cancels pending requests.