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

Post-purchase for US home and decor brands

Post-Purchase built for How Home Brands Sell

Manage bulky shipments, delivery updates, and returns for home and decor brands. Lower WISMO tickets and improve delivery experience with ClickPost.

Home and Decor - Banner
4.8
Rating Stars - Grey

Trusted by 450+ global brands

EXPLORE THE PLATFORM

How ClickPost works
for Home Decor post-purchase
Why home and decor is different

Why home and decor is different

Bulky, breakable orders, long delivery windows, and expensive damage claims.
Returns WISMO Damage
Four pillars of post-purchase

Four pillars of post-purchase

Branded tracking, exchange-first returns, delivery visibility, and analytics.
Tracking Exchanges Analytics
Revenue levers

Revenue levers

Order editing, returns protection, and post-purchase upsell.
Order edits Upsell Protection

WHY HOME AND DECOR IS DIFFERENT

Home and decor ships the heaviest risk in retail after checkout.

Furniture and decor are large, fragile, and slow to arrive, so damage in transit and long delivery windows drive both returns and anxious buyers.

Damage sends it back

Fragile and oversized parcels take a beating in transit. A cracked lamp or a scratched table becomes a refund and a freight bill, not just a lost sale.

Long delivery windows spike WISMO

Big items ship slow and often need appointments. The long wait floods support with "where is my order" tickets, right when your team has the least room.
~15%
of furniture and large-item orders are returned, and each one is expensive to move.
most
of home returns are driven by damage, defects, or "not as pictured."
30–65%
of a bulky item's value can be lost to return freight, inspection, and restocking.
80%+
of shoppers track a large or fragile order closely until it arrives.

Return-rate benchmarks from National Retail Federation, Statista, and Shopify category research

HOW CLICKPOST HELPS

Own the whole journey from doorstep to the way back.

ClickPost runs tracking, returns, exchanges, and carrier decisions from one platform, so every post-purchase moment works for retention instead of against it.

Keep shoppers on your brand through a long delivery window

Proactive updates by email and SMS at every milestone, plus a branded tracking page that looks like your store. Fewer "where is my order" tickets on slow, high-value deliveries.
  • Mobile-first branded tracking page
  • Milestone alerts by email, SMS, and push
  • Estimated delivery dates that set expectations

Use case

A furniture brand sends proactive SMS at every milestone on a two-week delivery, so the buyer is not left guessing.

Business impact

Up to 60% lower WISMO queries and calmer buyers on the brand's own domain.
Frame 2147228189 (1)

Turn a damaged delivery into a replacement, not a refund

A self-service returns portal handles damage and defect cases and offers a replacement, exchange, or store credit, so revenue stays inside the brand.
  • Branded self-service returns portal
  • Damage-aware replacement and credit prompts
  • Return tracking so shoppers stop chasing status

Use case

A lamp arrives cracked. The portal books a replacement in a few taps instead of issuing a refund, and the sale is kept.

Business impact

More returns kept as replacements or credit, protecting margin on costly items.
Frame 2147228189

Catch delayed and mishandled freight before it becomes a claim

Home ships bulky, fragile parcels and freight across UPS, USPS, FedEx, and regional and LTL carriers. ClickPost tracks every parcel in one place and flags delayed and stuck shipments early.
  • Unified tracking across parcel and freight carriers
  • Early alerts on delayed and stuck shipments
  • Accurate delivery dates that hold up at peak

Use case

A brand catches a stuck freight shipment in the control tower and reroutes it before the customer files a damage claim.

Business impact

Fewer damage claims and refunds and higher NPS.
Frame 2147228190

Feed damage and return data back into packaging and product

See why items come back by SKU, reason, and carrier, then act on it. Return data becomes a signal for your packaging, carrier mix, and product pages.
  • Return reason and damage reporting
  • Carrier and freight performance visibility
  • Signals your ops and merchandising teams can use

Use case

Analytics show a vase breaks in transit on one carrier. The team changes packaging and carrier, and damage returns fall.

Business impact

Lower damage rate and clear data for packaging and carrier decisions.
Frame 2147228191
BEYOND TRACKING AND RETURNS

Keep more of every order, and grow it.

Home shoppers wait through long windows, worry about damage, and keep opening the order page. ClickPost adds three revenue moves built for exactly that behavior.

BEFORE IT SHIPS · ORDER EDITING

Let shoppers fix the order before it ships

Customers edit variant, quantity, and address themselves from the order page, inside a window you set, so a wrong finish or address gets fixed before a bulky item leaves the warehouse.
  • Self-serve variant, quantity, and address edits
  • Address validation pre-dispatch
  • Merchant-set edit window with 3PL sync
30% fewer order-change tickets
GROW AOV · POST-PURCHASE UPSELL

Sell the rest of the room while attention is high

ClickPost surfaces AI-picked complementary pieces on tracking and order pages and inside the returns flow, so one purchase becomes a bigger basket.
  • AI reorder prompts on tracking pages
  • Complementary pieces during returns flow
  • Upsell revenue that lifts AOV
+AOV from post-purchase recommendations

GETTING STARTED

Live on ClickPost without rebuilding your stack

div.cp-roi__hvis (21)

Connect your store and carriers

Plug ClickPost into Shopify or your commerce platform and link your US carriers through a single API.
div.cp-roi__hvis (22)

Brand tracking and returns

Match the tracking page, returns portal, and notifications to your look and voice. Set exchange rules once.
div.cp-roi__hvis (23)

Watch WISMO and refunds drop

Proactive updates cut support load and exchange prompts hold revenue. Analytics show what is working.
THE LEADER IN POST-PURCHASE

Powering 600+ leading retail brands

From the first tracking scan to the final return, ClickPost powers everything that happens after checkout.

  • 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
  • Birkenstock - Testimonial 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
  • 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
  • GOPURE - Slider BG

    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
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

4.8 Ratting Stars - White
4.4 Rating Stars White 4.4

Give Bulky Deliveries a Branded,
Trackable Experience

Brand the wait on oversized deliveries and settle damage claims faster, all from one dashboard. Walk through it on your delivery flow.
. 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(); } })();