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 */ }); }); })();

91ÊÓƵ

Assess Your Post-Purchase Brand Readiness Start Assessment

ROI Calculator · Tracking

See what proactive
tracking wins back

Branded tracking, proactive notifications and failed-delivery
prevention. See the revenue you win back and the support cost you take out

1. Tell us about your business

Defaults are typical benchmarks. Change any value to fit your business.

2. Fine-tune the impact assumptions

Defaults are ClickPost platform benchmarks. Leave them for a conservative estimate, or match your own data.

How it works

Cut support tickets and
failed deliveries in four steps

ClickPost works the whole delivery leg — from the branded tracking page to failed-delivery recovery — so fewer orders stall and fewer shoppers write in.

div.cp-roi__hvis

Give every order a branded page

One link carries your brand and live status. Shoppers stop writing in to ask where their parcel is.

div.cp-roi__hvis (1)

Update them before they worry

A nudge on SMS, WhatsApp or email at each milestone answers the question before it gets asked.

div.cp-roi__hvis (2)

Catch exceptions early

Delivery-exception alerts flag a failed attempt or a bad address while the shipment can still be saved.

div.cp-roi__hvis (3)

Sell from the tracking page

That page pulls repeat visits. Put recommendations on it and route shoppers back to the store.

Cost anatomy

Eight factors that determine
post-purchase experience cost

Knowing what drives your delivery cost tells
you where a fix pays off first.
Frame 1618873241 (4)

WISMO ticket volume

Each "where is my order" ticket carries a loaded agent cost. Most of them never needed to exist.

Frame 1618873241 (5)

Delivery exceptions

A missed attempt sets off re-attempts, calls and, often, a return. The cost stacks quietly.

Frame 1618873241 (8)

Orders sent back before delivery

An order that bounces before it is delivered pays both shipping legs and earns nothing back. It is often the biggest hidden line.

Frame 1618873242 (1)

Carrier performance gaps

A weak carrier on a lane shows up twice: first as tickets, then as returns.

Frame 1618873241 (6)

Delivery-date accuracy

A vague or broken promise breeds anxiety, and anxiety breeds tickets and cancellations.

Frame 2147228122 (1)

Notification coverage

Anyone you cannot reach is someone who opens a ticket instead. Contactability caps the whole model.

Frame 1618873241 (7)

Tracking-page engagement

A plain carrier page throws away the repeat-visit traffic a branded one would earn from.

Frame 2147228122 (2)

Repeat-purchase capture

Traffic that lands on tracking and leaves without a nudge is revenue you simply skipped.

The math

ROI calculation formulas

Transparent methodology. These are the formulas the calculator runs. The
benchmark constants inside them come from ClickPost platform data, and you can
change every rate in the advanced panel.
Step 1
Revenue from notifications
N × AOV × 0.0035 × (engage / 60%)

Incremental orders from proactive status updates, scaled by how many shoppers you reach.

Step 2
Revenue from tracking pages
N × AOV × 0.33 × 0.1667 × 0.4359 × wcr × (0.7 + 0.5 × engage)

Repeat orders from shoppers who click back to the store from a branded tracking page.

Step 3
Failed-delivery recovery
N × fail × recovery × AOV

Orders saved when a delivery exception is resolved before it turns into a return.

Step 4
WISMO cost saved
tickets × wismo × 0.5 × cost

Support load removed, on the assumption proactive tracking clears about half of WISMO tickets.

Total ROI
notifications + tracking pages + recovery + WISMO saved
N = annual orders · AOV = average order value · wcr = website conversion rate · fail = failed-delivery rate · recovery = failed-delivery recovery lift · engage = notification engagement

Powering post-purchase for 600+ brands worldwide

  • GOPURE

    ClickPost helped us create a return experience that fit our business instead of forcing us to fit the platform. They customized our workflows without additional costs or developer support. The automation they built for our returns, freed up 5 hours a week.

    Sandy Vangyi
    Sandy Vangyi
    Director of CX, GOPURE
  • BG

    We’ve been using Clickpost and it’s genuinely been super helpful. The post-purchase upsells have been a solid bonus for incremental revenue.

    Samarth Sindhi
    Samarth Sindhi
    Chief Executive Officer, MarsGHC
  • VanillaPura - BG

    Great experience and fantastic support from ClickPost. This immediately cut our top post-purchase support tickets, making ROI easy to quantify. Plus, the customization features and upsell tools help increase AOV. Highly recommend.

    Paul Fulton
    Paul Fulton
    VanillaPura
High Performer  Mid Market - Winter 2026
Highest User Adoption - Mid Market - Winter 2026
Momentum Leader - Winter 2026
User Loves Us
High Performer  Mid Market - Winter 2026
Highest User Adoption - Mid Market - Winter 2026
Momentum Leader - Winter 2026
User Loves Us

4.8 ★

out of 5 stars on G2.com

Users Love Us - G2 Winter 2026
Momentum Leader - Winter 2026
Highest User Adoption - Winter 2026
High Performer Mid-Market - Winter 2026
Users Love Us - G2 Winter 2026
Momentum Leader - Winter 2026
Highest User Adoption - Winter 2026
High Performer Mid-Market - Winter 2026
Users Love Us - G2 Winter 2026
Momentum Leader - Winter 2026
Highest User Adoption - Winter 2026
High Performer Mid-Market - Winter 2026
Users Love Us - G2 Winter 2026
Momentum Leader - Winter 2026
Highest User Adoption - Winter 2026
High Performer Mid-Market - Winter 2026

4.8 ★

out of 5 stars on G2.com

5B+

shipments automated per year

Post-purchase that plugs into
your existing stack

  • 600+ Carrier Partners
  • 75+ Communication Gateways
  • 50+ Storefronts, OMS, WMS
  • Shopify
  • Magento
  • Klaviyo
  • Attentive
  • Shipbob
  • Shiphero
  • UPS
  • USPS

It estimates two things: the support cost you save by deflecting WISMO tickets, and the repeat and upsell revenue a branded tracking page earns back. You enter your order and support numbers, and it returns an annual dollar figure for both.

Monthly orders, your average order value, the share of support tickets that are order-status questions, and your cost per ticket. If you don't have exact figures, the calculator starts with conservative industry defaults you can replace with your own.

They come from published ecommerce support and post-purchase benchmarks, set toward the low end so the output reads as a floor, not a best case. Every default is editable, so you can swap in your own data and watch the number move.

Most brands remove more than half of their order-status tickets by pairing a branded tracking page with proactive delay alerts and self-serve order edits, so customers get the answer before they open a ticket. The calculator lets you set your own deflection rate instead of assuming a ceiling.

Yes. Shoppers check tracking several times per order, and that traffic converts better than your main site, so the calculator counts the repeat purchases and upsell revenue a branded tracking page drives, on top of the ticket savings.

Most US brands launch branded tracking and self-serve returns within a few weeks using prebuilt Shopify and carrier integrations, so payback often lands within the first quarter once WISMO drops and tracking-page revenue starts. Your exact timing depends on order volume and current ticket load.

. 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(); } })();