Customer story · Lead attribution
The consultant already knows what you read
ProLend is a private-lending business in South Africa. Someone lands on the site, reads about local prime-linked and offshore structures, models an indicative return on the calculator, and eventually submits an enquiry — at which point a human consultant picks up the phone. Crossdeck's job here is not to measure subscription revenue. It is to make sure that call is not a cold one.
- SDK@cross-deck/web 1.14.0
- StackVanilla site, no build step
- ConsentOpt-out, by decision
- DestinationPipedrive
What a lead form actually tells you
A name, an email, a phone number, and maybe a free-text box. That is the whole of it. Everything that would make the conversation useful — which structure they were comparing, what numbers they put into the calculator, how long they spent deciding, whether this was their first visit or their fifth — happened before the form and is thrown away by it.
So the consultant opens the lead and starts from nothing. They ask the questions the person has already answered by their behaviour, and the first few minutes of a call about someone's money are spent re-establishing what the website already knew.
The gap is not a data problem in the CRM. It is that the anonymous visitor and the named lead are two different records in almost every stack, and nothing joins them. That join is the whole of what Crossdeck does here.
A vanilla site, no build step
ProLend's public pages are hand-written HTML with classic scripts. There is no bundler, no React,
and therefore no place to put the <CrossdeckProvider> the docs reach for first.
The SDK is loaded as an ES module straight from a CDN and initialised once — which is the
documented equivalent, not a workaround.
import { Crossdeck } from "https://esm.sh/@cross-deck/[email protected]";
Crossdeck.init({
appId: "app_web_...",
publicKey: "cd_pub_live_...",
environment: "production",
cookieDomain: "auto",
});
init() is synchronous and idempotent, and auto-tracks page views and session
start/end — so a static site gets a complete analytics baseline without a single lifecycle
call written by hand.
One person, not two
ProLend's consultants have their own subdomains — a prospect may first arrive on
wes.pro-lend.co.za from a consultant's own link, and only later land on the apex
domain to read the general material. Under default cookie scoping those are two separate anonymous
visitors, and the journey the consultant eventually reads is missing its own beginning.
cookieDomain: "auto" scopes identity to the registrable domain, so the subdomain
visit and the apex visit resolve to one person. It is a single line, and it is the
line that makes the replay honest rather than merely present.
Consent, deliberately opt-out
The Crossdeck SDK ships a consentBanner option on init(). ProLend does
not use it — on purpose. That flag is consent-first: it withholds analytics until
somebody makes a choice, and most people never make a choice. The result would be a consultant
opening a lead to find the first-touch journey missing precisely for the visitors who behaved most
normally.
So they mount the same branded Crossdeck widget themselves, and analytics run from the first page
view. A decline is not cosmetic: it lands on Crossdeck.consent(), the SDK's own
enforcement socket, and collection stops. The choice is stored and re-applied on every subsequent
page before anything else runs.
import { mountConsentBanner } from "https://esm.sh/@cross-deck/[email protected]/consent";
mountConsentBanner({
policyUrl: "https://pro-lend.co.za/privacy",
categories: {
analytics: {
label: "How you use this site",
description:
"Pages you read and what you model on the calculator, so the " +
"consultant you speak to already understands what you are after.",
},
},
onChange(state) {
// A decline must actually stop the tracking, not just close a bar.
Crossdeck.consent({
analytics: !!state.analytics,
marketing: !!state.marketing,
});
},
});
The consent widget is a separate entry point, so its bytes download only because that import exists. Global Privacy Control is honoured by the SDK regardless of any of this.
Two things are worth separating here. The posture — opt-out rather than
opt-in — is ProLend's call, made against South Africa's POPIA, a personal-information regime
rather than an ePrivacy-style prior-consent one, and against the fact that what is held before an
enquiry is an anonymous id. The enforcement is not a call anybody makes: it is the
same code path either way. Crossdeck's position is that the switch should be yours and the wiring
should not be. If your jurisdiction or your own policy says consent-first, the
consentBanner flag gives you exactly that in one option.
Anonymous, then named — without losing the first half
Every visitor is anonymous until the enquiry form is submitted. At that moment
identify() fires, and the entire anonymous history — the first referral, the
pages read, the calculator runs, across the consultant subdomain and the apex both —
back-links onto the real person. The consultant does not get a record that starts at the form. They
get the whole thing.
// Back-link the whole anonymous journey to a real person.
window.cdIdentify = async (userId, options) =>
await Crossdeck.identify(String(userId), options || {});
// The SDK's current anonymous device id — persisted onto the lead so the
// dashboard can match a list row (anon:…) back to the client without a
// per-row API lookup, which wouldn't scale across many consultants.
window.cdAnonId = () => Crossdeck.diagnostics().anonymousId || "";
// Canonical Crossdeck customer id (cdcust_…) — the id the Pipedrive
// connector expects on the mapped Person custom field.
window.cdCustomerId = () => Crossdeck.diagnostics().crossdeckCustomerId || "";
The detail that makes this scale: the anonymous id is written onto the lead record at capture time, so a consultant dashboard listing a hundred leads resolves every row locally. Nothing about that list costs an API call per row.
The same file also mirrors Firebase auth on the pages that have it — sign-in calls
identify() with id, email and traits; sign-out calls reset(). Public
marketing pages stay anonymous, and the admin side carries the signed-in user. One install covers
both.
Into the CRM the consultants already live in
Nobody was going to ask a lending consultant to learn a second console. The identity work only pays
off if it arrives where the deal is already worked, which is Pipedrive.
cdCustomerId() returns the canonical cdcust_… id, which is stamped onto a
Person custom field. Crossdeck's read-only
Pipedrive connector matches exactly on that
field, so a person in the CRM and a person in the journey are provably the same record rather than
a fuzzy email guess.
What it produced
A consultant opening a lead can see what that person actually read before they made contact: which structures they compared, what they modelled, how they arrived, and how long they thought about it.
The difference between a cold meeting and an informed one.
— the whole point of the install
That is a marketing and distribution outcome, not a product-analytics one. It is worth being precise about what is claimed: this is a live production install exercising identity, consent, events and a CRM connector, and the change it makes is to the quality of a human sales conversation. Crossdeck has not run a controlled measurement of conversion lift here, and this page does not claim one.
Why this install is worth reading
- No build step, full SDK. An ES module import from a CDN is a first-class install, not a degraded one. Static sites are not second-class citizens.
- The consent switch is real in both directions. ProLend chose opt-out and the enforcement still works. Someone choosing opt-in gets the same enforcement from one flag.
- Identity is the product here. There is no subscription to measure. The value is entirely in one person being one person across a subdomain, an apex, an anonymous session and a named lead.
- It ends in a CRM, not a dashboard. The people who benefit never open Crossdeck. The data goes to where they already work.
The same install, documented
Every piece of this is written up. Nothing here required a private arrangement.
Your visitors are already anonymous somewhere
The journey exists whether or not anything is joining it to a name. Crossdeck is free until your app earns real money.