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

G2 - Orange BG 4.8 G2 Rating 2

Cut WISMO tickets by 60%,without adding headcount

Deflect WISMO tickets, automate delivery updates, and give agents full order context. Help CX and support teams cut volume and response time with ClickPost.

For CX & Support Teams

Trusted by 450+ global brands

The support tax

Your agents are answering questions the order should answer itself

WISMO floods the queue

WISMO floods the queue

Up to 60% of tickets are “where is my order” — pure status checks that never needed a person.
60% of tickets are pure status checks
Manual carrier lookups

Manual carrier lookups

Agents jump between UPS, FedEx, and USPS portals to piece together one shipment’s status.
3-4 portals checked per shipment
Returns back-and-forth

Returns back-and-forth

Size swaps and refunds turn into long email threads instead of a two-tap self-serve flow.
54% of returns kept as exchanges with self-serve
No single order view

No single order view

Order, shipment, and returns data live in different tools, so agents lack the full picture.
47% higher NPS with unified order context

How ClickPost helps

Answer less, resolve faster, from one platform

Three layers of deflection, so repetitive questions resolve themselves and agents get the full story on the ones that don’t.

Answer “where is it” before they ask

A branded tracking page with live, carrier-agnostic updates over SMS and email, so shoppers self-serve the answer on your storefront.
  • Real-time status across 500+ carriers
  • Proactive delay and exception alerts
  • Your logo, your domain, your upsells

Use case

A shopper checks their order on your branded page with live SMS updates, not the carrier’s site.

Business impact

Fewer WISMO tickets and more traffic on your own domain.
Frame 2147228189 (1)

Let shoppers fix it themselves

Address changes, size swaps, and exchanges happen in a guided flow, no ticket required, with refund-or-exchange decisions running on your policy.
  • 60-minute self-serve order edit window
  • Exchange-first returns keep the revenue
  • Automated policy rules per segment

Use case

A size-swap request resolves in a guided self-serve flow with no agent touch.

Business impact

Most edits and returns never reach the queue.
Frame 2147228189

Give agents the full order in one screen

When a ticket does need a human, the agent sees the order, live shipment, and returns history together, with AI-suggested next steps.
  • Order, tracking, and returns in one timeline
  • AI-suggested resolutions and macros
  • Syncs with Gorgias, Zendesk, and Kustomer

Use case

An agent opens a ticket and sees order, shipment, and returns together with a suggested fix.

Business impact

Faster handle time on the tickets that do need a person.
Frame 2147228190
60%
Fewer WISMO tickets
47%
Higher customer NPS
54%
Returns kept as exchanges
74%
Of shoppers expect live tracking

More on reducing support load

  • blogs
    Blogs

    Ship smarter, read first

    Practical guides on tracking, delivery, and returns from the teams solving post-purchase every day.

  • Ecommerce tools-1
    Ecommerce tools

    Free tools, real results

    Calculators, benchmarks, and utilities built to fix the post-purchase metrics that move revenue.

  • assessment
    Assessment

    Know where you stand

    Answer a few questions and see how your post-purchase experience stacks up against leading US retailers.

  • ebooks
    Ebooks

    Playbooks worth downloading

    Deep-dive guides on cutting WISMO tickets, streamlining returns, and turning one-time buyers into repeat customers.

  • ROi-calculator
    ROI calculator

    Do the math on growth

    See what faster returns, fewer delivery exceptions, and proactive tracking are worth to your revenue.

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 the revenue you keep by converting refunds into exchanges and store credit, plus the processing cost you save per return. Instead of measuring return rate, it measures retained revenue, which is the number that actually protects margin.

Your return rate, average order value, the split between refunds and exchanges today, and your cost to process one return. Defaults are provided, but this tab is most accurate when you enter your own return and processing numbers.

They are category-aware, since a healthy return rate for apparel looks nothing like one for electronics or beauty. The calculator starts from US category averages and lets you set your own rate, so the estimate reflects what you actually see, not a blended number.

The return flow leads with a size swap, color change, or store credit before it offers a refund, so the customer stays and the order value stays with you. A large share of would-be refunds convert to exchanges when the flow is built this way.

Yes, you set the rules. Policies decide which outcomes each customer sees, by product and segment, and you can offer bonus credit for exchanges or apply a fee on refund-heavy behavior. The flow only presents what your policy allows.

Payback comes from two places at once: revenue retained on each exchange and lower processing cost per return. The lift scales with your return volume and how refund-heavy you are today, so higher-return categories like apparel usually see the biggest gain.

Post-purchase touches every team.So does ClickPost

The same platform your support, ops, and growth teams share, each with the view and controls they need

One dashboard. Every carrier. Every shipment.

Real-time visibility across 600+ carriers, automated allocation, and exception management from a single control tower.

  • Automated carrier allocation
  • Unified exception management
  • Live multi-warehouse visibility
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

4.8 Ratting Stars - White
4.4 Rating Stars White 4.4

Cut WISMO by 60% Without
Adding Headcount

Deflect status tickets with proactive updates and self-serve order help, so agents handle exceptions instead of check-ins. Book a demo on your ticket volume.
. 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(); } })();