and runs during HTML parse, but hubspotutk is set by
js.hs-analytics.net, measured loading at ~834ms. On a first visit the cookie
does not exist yet, so a single read returns nothing and the visitor gets no
external ID at all — and a first visit is exactly what an ad click is. So
poll briefly until HubSpot sets it. The SDK loads lazily (~4.5s) and replays
a queue, so an init queued a second or two in still lands before any events
are transmitted. */
(function attachExternalId() {
var attempts = 0;
var MAX_ATTEMPTS = 40; /* 40 x 200ms = 8s, then give up quietly */
var timer = null;
function tryAttach() {
var raw = getExternalId();
if (!raw) {
if (++attempts >= MAX_ATTEMPTS && timer) clearInterval(timer);
return;
}
if (timer) clearInterval(timer);
sha256Hex(raw).then(function (hash) {
oaiq("init", { user: { external_id_sha256: hash } });
}).catch(function () {});
}
tryAttach(); /* returning visitor: immediate */
if (!getExternalId()) timer = setInterval(tryAttach, 200);
})();
/* ---------------------------------------------------------------------------
2. Load the SDK off the critical path
Whichever comes first: the main thread going idle after load, or the first
sign of real user intent. No preconnect on purpose — a TLS handshake during
LCP for a script we deliberately deferred is a net loss.
Measured on clickpost.ai: load event 3614ms, SDK fetched 4508ms.
------------------------------------------------------------------------ */
var loaded = false;
var INTENT = ["pointerdown", "keydown", "scroll"];
function loadSdk() {
if (loaded) return;
loaded = true;
INTENT.forEach(function (t) { removeEventListener(t, loadSdk, { passive: true }); });
var s = document.createElement("script");
s.src = SDK;
s.async = true;
s.fetchPriority = "low";
document.head.appendChild(s);
}
INTENT.forEach(function (t) {
addEventListener(t, loadSdk, { passive: true, once: true });
});
addEventListener("load", function () {
window.requestIdleCallback
? requestIdleCallback(loadSdk, { timeout: 2500 })
: setTimeout(loadSdk, 1500);
}, { once: true });
/* ---------------------------------------------------------------------------
3. Conversion id
sha256(lowercase(email) + "|" + formId + "|" + ISO-hour), first 20 hex,
"lead_" prefix.
- email lowercased/trimmed so casing does not fork the id
- formId keeps two different forms in the same hour from colliding
- the hour bucket is only the server's fallback; the id computed here is
sent to the relay verbatim, so the two paths cannot disagree even when
a submit straddles an hour boundary
Ads Manager's own sample omits event_id. We send it so the pixel event and
the relay event collapse into one conversion instead of counting twice.
------------------------------------------------------------------------ */
function buildEventId(email, formId) {
var seed = [
String(email || "").trim().toLowerCase(),
String(formId || ""),
new Date().toISOString().slice(0, 13)
].join("|");
return crypto.subtle.digest("SHA-256", new TextEncoder().encode(seed)).then(function (buf) {
var hex = Array.prototype.map.call(new Uint8Array(buf), function (b) {
return b.toString(16).padStart(2, "0");
}).join("");
return "lead_" + hex.slice(0, 20);
});
}
/* Guards a re-fire from the history.pushState that book-a-demo-core.js runs
after submit, and from back-navigation onto the same state. */
function firedOnce(id) {
try {
if (sessionStorage.getItem("oaiq_" + id)) return true;
sessionStorage.setItem("oaiq_" + id, "1");
return false;
} catch (e) {
return false; /* private mode / blocked storage: fire rather than lose it */
}
}
/* Path 2. keepalive lets the request finish even if the page navigates away.
Failures are swallowed deliberately: a reporting call must never surface an
error to someone who just submitted a form.
The relay hashes the email server-side into user.emails_sha256 and adds the
caller's IP and user agent, so this event carries the same match signals the
pixel gets for free. */
function sendToRelay(email, formId, eventType, eventId) {
try {
var payload = {
token: RELAY_TOKEN,
email: email,
form_id: formId || "",
event_type: eventType,
event_id: eventId, /* verbatim, so both paths always match */
source_url: location.href
};
var oppref = getOppref();
if (oppref) payload.oppref = oppref;
var externalId = getExternalId();
if (externalId) payload.external_id = externalId;
fetch(RELAY_URL, {
method: "POST",
keepalive: true,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
}).catch(function () {});
} catch (e) {}
}
/* Capture the email while the form is still on the page. onFormSubmitted does
not reliably carry submissionValues, and by then the form may be removed. */
var lastEmail = "";
document.addEventListener("submit", function (e) {
var f = e.target;
if (!f || !f.querySelector) return;
var i = f.querySelector('input[name="email"], input[type="email"]');
if (i && i.value) lastEmail = i.value;
}, true);
/* Every HubSpot form on the site reports through this one message event, so no
module or theme JS needs editing to add coverage. */
window.addEventListener("message", function (e) {
var d = e.data;
if (!d || d.type !== "hsFormCallback" || d.eventName !== "onFormSubmitted") return;
var email = (d.data && d.data.submissionValues && d.data.submissionValues.email) || lastEmail;
if (!email) return; /* without an email there is no stable id to dedupe on */
var eventType = EVENT_BY_FORM[d.id] || EVENT_DEFAULT;
buildEventId(email, d.id).then(function (id) {
if (firedOnce(id)) return;
oaiq("measure", eventType, { type: DATA_TYPE }, { event_id: id }); /* path 1 */
sendToRelay(email, d.id, eventType, id); /* path 2 */
});
});
})();
I appreciate how Clickpost provides real-time updates on the status and location of my shipments. It's reassuring to know exactly where my packages are at all times.
ClickPost Integration is seamless. It has helped us with tracking, courier allocation and returns management. We also appreciate the accuracy and smart allocation through the EDD app.
The world’s most ambitious D2C brands trust ClickPost
Scalable tech
AI led optimizations to keep operations lean and scalable
Exceptional support
High-response time, dedicated customer success managers
Proven results
WISMO reductions, cost optimizations proven with global leaders
All the integrations you need, one powerful platform
Frequently asked questions
How does ClickPost help D2C brands turn order tracking into repeat purchases?
ClickPost turns the tracking experience into a sales channel with branded and accurate tracking experience (page+updates), personalized product recommendations, ongoing promotions, and feedback collection. Proactive notifications (WhatsApp, SMS, email) keep customers informed, reducing WISMO, building the trust that drives repeat purchases.
How does ClickPost's AI carrier allocation improve delivery speed and reduce logistics cost?
Our AI analyzes real-time carrier performance across your carrier network and automatically routes each order to whoever's most optimised as per your business parameters - for that specific pincode. Brands typically see 16-20% cost reduction, faster deliveries and predictability.
What capabilities does ClickPost offer for returns and exchanges, and how does it help increase profitability?
ClickPost nudges customers toward exchanges instead of refunds at the return request stage, helping you save the sale. Automated approval workflows, exchange reconciliation eliminate manual work while giving customers a seamless, on-brand experience.
How does ClickPost reduce WISMO queries and operational workload for D2C teams?
Proactive notifications and self-service tracking pages cut "where's my order?" queries by 60-80%. Our AI voice agent Parth handles delivery exceptions automatically, rescheduling failed deliveries and reattempts without human intervention.
Which carriers and storefronts does ClickPost integrate with, and how easy is the implementation?
ClickPost integrates with 500+ carriers globally and all major storefronts (Shopify, WooCommerce, Magento, plus OMS and WMS). It also helps brands to go live with new carrier integrations within 24 hours.
We use cookies to keep the site working, understand usage, and improve your experience. Learn more
Strictly Necessary
Always active for essential site performance and security.
Performance & Analytics
Enables anonymous usage analysis to refine our services.
Targeting & Advertising
Used to present relevant promotions and advertising.
.
window.disableHubSpotCookieBanner = true;
function killNative() {
var n = document.getElementById("hs-eu-cookie-confirmation");
if (n && n.parentNode) { n.parentNode.removeChild(n); return true; }
return false;
}
killNative();
if (window.MutationObserver) {
var mo = new MutationObserver(function () { killNative(); });
mo.observe(document.documentElement, { childList: true, subtree: true });
setTimeout(function () { mo.disconnect(); }, 15000);
}
var COOKIE = "cp_cookie_consent";
var EU = ["at","be","bg","hr","cy","cz","dk","ee","fi","fr","de","gr","hu","ie","it","lv","lt","lu","mt","nl","pl","pt","ro","sk","si","es","se","gb","uk","is","li","no","ch"];
function readCookie(nm) {
var m = document.cookie.match(new RegExp("(?:^|; )" + nm + "=([^;]*)"));
return m ? decodeURIComponent(m[1]) : null;
}
function writeCookie(nm, val, days) {
var d = new Date();
d.setTime(d.getTime() + (days * 864e5));
document.cookie = nm + "=" + encodeURIComponent(val) +
";expires=" + d.toUTCString() + ";path=/;SameSite=Lax" +
(location.protocol === "https:" ? ";Secure" : "");
}
function stored() {
var raw = readCookie(COOKIE);
if (!raw) return null;
try {
var o = JSON.parse(raw);
if (typeof o.analytics === "boolean") return o;
} catch (e) {}
return null;
}
function apply(state, revoke) {
window._hsp = window._hsp || [];
window._hsp.push(["setHubSpotConsent", {
analytics: !!state.analytics,
advertisement: !!state.advertisement,
functionality: !!state.functionality
}]);
if (revoke && !state.analytics && !state.advertisement && !state.functionality) {
window._hsp.push(["revokeCookieConsent"]);
}
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "cp_cookie_consent",
cp_consent_analytics: !!state.analytics,
cp_consent_advertisement: !!state.advertisement,
cp_consent_functionality: !!state.functionality
});
document.documentElement.setAttribute("data-cp-consent", "set");
}
function inScope(banner) {
if (banner.getAttribute("data-scope") !== "eu_only") return true;
var geo = (document.body.getAttribute("data-geo_location") || "").toLowerCase();
if (!geo) return true;
return EU.indexOf(geo) !== -1;
}
function init(banner) {
var days = parseInt(banner.getAttribute("data-days"), 10) || 180;
var prefs = banner.querySelector("[data-cpcb-prefs]");
var compact = banner.querySelector('[data-cpcb-actions="compact"]');
var expanded = banner.querySelector('[data-cpcb-actions="expanded"]');
var toggles = banner.querySelectorAll("[data-cpcb-toggle]");
function show() {
banner.hidden = false;
requestAnimationFrame(function () { banner.classList.add("is-visible"); });
}
function hide() {
banner.classList.remove("is-visible");
setTimeout(function () { banner.hidden = true; }, 300);
}
function collapse() {
if (!prefs) return;
prefs.hidden = true;
if (expanded) expanded.hidden = true;
if (compact) compact.hidden = false;
}
function expand() {
if (!prefs) return;
prefs.hidden = false;
if (compact) compact.hidden = true;
if (expanded) expanded.hidden = false;
}
function paint(state) {
Array.prototype.forEach.call(toggles, function (t) {
var key = t.getAttribute("data-cpcb-toggle");
if (key === "necessary") { t.setAttribute("aria-checked", "true"); return; }
t.setAttribute("aria-checked", state[key] ? "true" : "false");
});
}
function fromToggles() {
var state = { analytics: false, advertisement: false, functionality: false };
Array.prototype.forEach.call(toggles, function (t) {
var key = t.getAttribute("data-cpcb-toggle");
if (key in state) state[key] = t.getAttribute("aria-checked") === "true";
});
return state;
}
function commit(state, revoke) {
writeCookie(COOKIE, JSON.stringify({
analytics: !!state.analytics,
advertisement: !!state.advertisement,
functionality: !!state.functionality,
v: 1,
ts: Date.now()
}), days);
apply(state, revoke);
hide();
}
Array.prototype.forEach.call(toggles, function (t) {
if (t.disabled) return;
t.addEventListener("click", function () {
t.setAttribute("aria-checked", t.getAttribute("aria-checked") === "true" ? "false" : "true");
});
});
banner.addEventListener("click", function (e) {
if (!e.target || !e.target.closest) return;
var btn = e.target.closest("[data-cpcb-action]");
if (!btn) return;
var action = btn.getAttribute("data-cpcb-action");
if (action === "customise") { expand(); return; }
if (action === "accept") { commit({ analytics: true, advertisement: true, functionality: true }, false); return; }
if (action === "reject") { commit({ analytics: false, advertisement: false, functionality: false }, true); return; }
if (action === "save") { commit(fromToggles(), true); return; }
});
var existing = stored();
if (existing) {
apply(existing, false);
paint(existing);
collapse();
} else if (!inScope(banner)) {
// Out of the consent-required region: implied consent, banner never shown.
commitSilently();
} else {
collapse();
show();
}
function commitSilently() {
var all = { analytics: true, advertisement: true, functionality: true };
writeCookie(COOKIE, JSON.stringify({ analytics: true, advertisement: true, functionality: true, v: 1, ts: Date.now(), implied: true }), days);
apply(all, false);
}
// Public re-open hook for a footer "Cookie settings" link.
window.cpCookieBanner = window.cpCookieBanner || {};
window.cpCookieBanner.open = function () {
var s = stored() || { analytics: false, advertisement: false, functionality: false };
paint(s);
expand();
show();
};
}
function boot() {
var banners = document.querySelectorAll("[data-cpcb]");
if (!banners.length) return;
init(banners[0]);
document.addEventListener("click", function (e) {
if (!e.target || !e.target.closest) return;
var trigger = e.target.closest("[data-cp-cookie-settings]");
if (!trigger) return;
e.preventDefault();
if (window.cpCookieBanner && window.cpCookieBanner.open) window.cpCookieBanner.open();
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
})();