@colada_app/web-sdk
v0.3.0
Published
Colada attribution SDK for the browser
Readme
@colada_app/web-sdk
Attribution for the browser. Resolves which ad campaign brought a visitor to your site, and reports conversions back so the ad platforms can optimise on them.
Under 7KB gzipped, no dependencies.
Install
npm install @colada_app/web-sdkOr a script tag, no build step:
<script src="https://cdn.jsdelivr.net/npm/@colada_app/[email protected]/dist/colada.min.js"></script>Pin the version, as above. A branded cdn.coladaapp.io/web-sdk/v0.3.0/colada.min.js
will replace this once its DNS is in place; until then that host does not resolve.
Use
import { colada } from "@colada_app/web-sdk";
await colada.init({ publicTenantKey: "pk_live_…" });
// On every page load, not just after login — see below.
if (user) colada.identify(user.id);
colada.track("Purchase", { amount: 249, currency: "SAR", orderId: order.id });Four methods is the whole integration. init() handles the rest.
Routing on the campaign
const attr = colada.getAttribution();
if (attr?.matched && attr.customParams.attributionStoreId) {
router.push(`/store/${attr.customParams.attributionStoreId}`);
}getAttribution() is synchronous and returns a frozen snapshot, so it is safe to call from a
render. It returns null until the attribution resolves — which is not the same as "no
campaign". An unattributed visitor comes back as a snapshot with matched: false.
Don't block your render on init(). Use the callback:
const off = colada.onAttribution((attr) => { /* re-render */ });identify() — the most important call you make
Device storage cannot cross a browser boundary. A visitor who taps your ad inside Instagram is in Instagram's own storage jar; the same person in Safari an hour later is, to any SDK, a different device. No API bridges them, on any platform.
identify() is what does. We resolve the campaign by your user id, so once you tell us who
someone is, their attribution follows them across browsers, in-app WebViews, new devices, and
Safari clearing local storage.
It is also required for events: track() without it is dropped, because an event with no user
cannot be attributed to anyone.
Call it once — and clear it on logout
The user id is persisted, so a page load does not detach events from the person. Call
identify() after login or sign-up; later pages restore it automatically.
await colada.init({ publicTenantKey: "pk_live_…" });
colada.identify(user.id); // after login or sign-upThis makes clearUser() mandatory on logout. A persisted id on a shared browser means one
person logs out, the next browses, and their purchase is attributed to the first — silent
misattribution that nothing downstream can detect.
colada.clearUser(); // every logout pathWire it into every path that ends a session, not just the logout button — token expiry and
server-side session invalidation are the ones people miss. clearUser() flushes anything the
outgoing user has queued before dropping the identity, so their pending events cannot go
out under whoever logs in next. Your deviceId is untouched: it identifies the browser, not
the person.
If you would rather the SDK never held an identity across page loads, call clearUser() on
unload and identify() on every page — the behaviour is then equivalent to not persisting.
Pair it with a Login event
identify() sends nothing by itself. If you call it after a login, follow it with the event:
colada.identify(user.id);
colada.track("Login");Our API binds the attribution to a user on Login and CompleteRegistration. Without one of
those, binding waits for whatever event happens next.
It is not authentication
identify() takes your own opaque user identifier. It does not log anyone in, and you should
not pass an email, a phone number, or a session token.
Events
Download · AddToCart · PlaceAnOrder · CompleteRegistration · Search · Purchase ·
Subscribe · InitiateCheckout · ViewContent · Login · ViewCheckout · ViewSubscription
Anything else is rejected. Pass metadata.amount as a number on revenue events — a
purchase without one still counts as a conversion but contributes nothing to revenue, and the
SDK warns when that happens.
Retries are safe. Every event carries a stable id, purchases are keyed on your orderId, and
events that fail while offline are queued and replayed when the network returns.
track() needs identify() first — an event with no user cannot be attributed to anyone, so it
is dropped with a console warning rather than sent. Nothing is queued for later, because holding
it would attribute this visitor's activity to whoever logs in next. Since the identity is
restored on page load, this normally only affects a visitor who has never identified at all.
The returned deliveryStatus reports what our API did with the event, not confirmation that an
ad platform accepted it. Treat ok: true as "we received and recorded it".
Options
| Option | Default | |
|---|---|---|
| publicTenantKey | — | required. Safe in browser source |
| apiBase | Colada's API | you almost certainly do not need this. Local dev, staging, or a first-party proxy on your own domain. Must be https unless localhost — init() refuses anything else rather than failing quietly |
| cookieDomain | off | set to ".yoursite.com" to share one visitor across subdomains |
| stripParam | false | remove colada_cid from the visible URL after capture |
| debug | false | log every decision; also throws on an unknown event name |
| timeoutMs | 10000 | per-request timeout |
What it collects
A random id we generate and store on your origin, the operating system, and the campaign parameters on the landing URL. No fingerprinting — no canvas, fonts, or hardware probing. No reading of your page content, forms, or cookies. The visitor's IP is seen by our API the same way it sees any request; the SDK never reads or sends it.
colada.debug() returns everything the SDK currently knows, which answers most integration
questions on its own.
Support
Nothing here throws in production. A failed call returns a result you can inspect rather than breaking your page.
