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ÊÓƵ

See where your logistics stands on automation Start assessment

Personalized Returns Policies that Protect Your Revenue

54% Returns Converted to Exchanges
39% Higher Value Exchanges
40% Revenue Retained via Store Credits
G2 - Orange BG 4.8 G2 Rating
Returns_Hero section_light

Trusted by 450+ global brands

PUMA - SVG - 50
Oriflame - SVG - 50
Adidas - SVG - 50
Herbalife - SVG - 50
Birkenstock - SVG - 50
Sotheby - SVG - 50
The Body Shop - SVG - 50
  • Birkenstock - Case Study Image
    ClickPost’s team were experts in their systems and integrated seamlessly with ours. They proactively resolved every issue, ensuring a smooth experience.
    Sonali Das

    Sonali Das

     D2C Tech and Product Head, Birkenstock

  • Oriflame - Case Study Image
    With ClickPost’s real time tracking, customers receive timely updates, significantly reducing WISMO Queries and helping us improve our post purchase experience.
    Rachmat Trisnasaputra

    Rachmat T

    Rachmat T, Operations Director, Oriflame Indonesia

Stop losing revenue to one-size-fits-all returns

Let ClickPost’s AI segment customers so you apply smarter policies.

Card Icon 1
Giving every customer the same 30-day return window?
Card Icon 2
Missing chances to convert returns into exchanges?
Card Icon 3
Serial returners abusing your generous policies?
Card Icon 4
Processing every return request manually?
Card Icon 5
No idea which products or customers drive returns?
Card Icon 6
Delayed refunds damaging customer trust?

Returns policies that adapt to customers.

Not the other way around

Segment customers, apply smarter rules - Banner

Segment customers, apply smarter rules

  • fi_7111111 Identify customer segments by lifetime value, purchase history, return behavior
  • fi_12789044 VIPs get premium policies, first-timers get standard terms
  • fi_10840987 Serial returners get restricted policies that protect margins
Push exchanges, not refunds - Banner

Push exchanges, not refunds

  • fi_12917048 AI-powered exchange nudges based on return reasons
  • fi_8245073 One-click size/color swaps across your entire catalog
  • fi_2169874 Store credit incentives to keep money in your ecosystem
Automate from request to resolution - Banner

Automate from request to resolution

  • fi_10382157 Branded self-serve returns portal for a seamless experience
  • fi_11284680 Real-time updates across email, SMS, WhatsApp
  • fi_13944255 Refunds triggered at your chosen milestone

Returns experiences that keep customers coming back

fi_14632787

LTV boosting policy personalizations

Segment customers with our AI. Offer personalized returns for each segment.
LTV boosting policy personalizations - Mobile Banner
fi_4073181

Optimize for your business

Use powerful returns analytics. Uncover hidden patterns, identify areas for improvement.
Optimize for your business - Mobile - Banner
LTV boosting policy personalizations  - Banner
Optimize for your business - Banner

Leading carriers, shopfronts, WMS, ERP, OMS

Shopify - Fit
Increff - Fit
SAP
Vinculum - Fit
Shiphero - Fit
Woo Commerce - Fit
Dukaan - Fit
Magento - Fit
Capillary - Fit
UPS - Fit
Delhivery - Fit
Bluedart - Fit
4.8 Ratting Stars - White
4.4 Rating Stars White 4.4

Returns that Drive Retention

Discover how the most loved brands build customer loyalty with ClickPost
users-love-us G2 2026 JAN 1 Momentum Leader Jan 2026 1 Highest User Adoption Jan 2026 1 High Performer Mid Market G2 2026 JAN 1

Frequently asked questions

What is ClickPost Returns & Exchange?

ClickPost Returns & Exchange is an automated returns management platform for ecommerce brands. It provides a branded self-service returns portal, instant exchange workflows, multi-carrier return label generation, and real-time reverse logistics tracking — helping brands reduce return costs and recover revenue through exchanges and store credit.

How does ClickPost compare to Loop Returns and AfterShip Returns?

ClickPost Returns offers similar core capabilities to Loop Returns and AfterShip Returns — branded self-service portals, exchange incentives, and automated workflows — but is designed for ecommerce brands across India and global markets. Unlike Loop (Shopify-only) or AfterShip, ClickPost integrates with 350+ carriers, supports Shopify, WooCommerce, Magento, and custom platforms, and includes end-to-end post-purchase visibility from forward shipping through reverse logistics in a single dashboard.

Does ClickPost support instant exchanges and store credit?

Yes. ClickPost Returns & Exchange supports instant exchange workflows that allow customers to select replacement items before returning the original. It also supports store credit offers as an incentive to reduce cash refunds, helping brands recover revenue that would otherwise be lost to returns.

Which ecommerce platforms does ClickPost Returns integrate with?

ClickPost Returns & Exchange integrates with Shopify, WooCommerce, Magento, and custom-built ecommerce platforms via API. It also connects with 350+ logistics carriers for return label generation and shipment tracking.

Can ClickPost handle reverse logistics tracking?

Yes. ClickPost provides real-time reverse logistics tracking across all return shipments. Brands can monitor return status, receive exception alerts, and keep customers informed via automated notifications on WhatsApp, SMS, and email throughout the return journey.

Is ClickPost Returns suitable for enterprise ecommerce brands?

Yes. ClickPost Returns & Exchange is built to handle high return volumes at enterprise scale. It includes a customizable rules engine for return policies, multi-warehouse and multi-seller support, advanced analytics, and dedicated enterprise integrations — making it suitable for large D2C brands, marketplaces, and omnichannel retailers.

What is the best returns management software for ecommerce brands in India?

ClickPost Returns & Exchange is one of the leading returns management platforms for ecommerce brands in India. It supports all major Indian logistics carriers, offers a branded returns portal, automates return approvals, and helps brands drive exchanges and store credit to minimize refund losses — making it a strong alternative to global tools like Loop Returns, AfterShip Returns, and ReturnPrime for India-first or Asia-Pacific focused businesses.
. 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(); } })();