Client-Side Tracking with Google Tag Manager
How does it work?
When a user clicks on one of your products on moebel.de, meubles.fr, meubelo.nl, moebel24.at, moebel24.ch, or mobi24.es, our server generates a unique clickID (moeclid) which is appended as a query parameter to your landingpage URL. With the integration of our client-side script, the clickID is written into the local storage for 90 days. Our server does not get any requests from you unless the user converts.
If the user makes a purchase on your site and has an existing moeclid value in the local storage of the browser, a conversion event is sent to our server including the clickID and further purchase-related data.
Prerequisites
Before you proceed with this integration, make sure the following conditions are met:
-
You have received your Partner ID or dedicated Sales Tracking Key.
-
You have set up a Google Tag Manager web container.
-
Your have configured UA/GA4 eCommerce tracking.
Integration
The client-side salestracking for Google Tag Manager consists of two tags - the base code tag and the conversion tag.
1. Base Code
The base code tag writes the appended moeclid parameter value of the landing page URL into the local storage of the browser. The trigger of this tag is configured in a way that the tag only fires if the user reached your site through one of our portals. It doesn’t affect your traffic from other marketing channels.
-
Add a new tag and select “Custom HTML” as tag type
-
Copy the code snippet of the initialization script and paste it. This script is same for all portals.

All Portals / Countries
<script>
"use strict";
!function(){"use strict";var t;JSON.parse({}.VITE_ENDPOINTS||"{}"),(t=function(){var t=window.location.search.replace("?","").split("&").find((function(t){return t.startsWith("moeclid")}));return t&&t.split("=")[1]||null}())&&function(t){var n={date:(new Date).toISOString(),clickId:t};localStorage.setItem("MOEBEL_CLICKOUT_ID",JSON.stringify(n))}(t)}();
</script>
-
Create a new “Page View” trigger where the URL of the page contains “moeclid=”. This way, the base code tag is only fired when a user reached your site through a moebel.de product click.

-
Add the trigger to the tag and save the tag.
Since local storage cannot be shared across domains/subdomains, saving the clickID (moeclid) to use at checkout will not work if the actual checkout page is on a different domain/subdomain than the product page the user was sent from us. |
2. Conversion
The conversion tag enables sending us a purchase event at the time the user converts. If there is no clickID from the initialization script in the local storage of the browser, calling sales function will do nothing. Moreover, this script removes obsolete clickIDs (older than 90 days) from local storage.
-
Add a new tag and select “Custom HTML” as tag type
-
Copy the code snippet of the conversion script and paste it. Please be aware that the MARKET variable in script must be adjusted for your market. (moebel.de - de, meubles.fr - fr, moebel24.at - at, moebel24.ch - ch, mobi24.es - es)

All Portals / Countries
<script>
"use strict";
var PARTNER_KEY = "INSERT-YOUR-PARTNER-KEY"; // REPLACE ME
var MARKET = "de"; // REPLACE ME "de","fr","nl","at","ch","es"
function _regeneratorRuntime(){/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */_regeneratorRuntime=function(){return t};var t={},e=Object.prototype,r=e.hasOwnProperty,n=Object.defineProperty||function(t,e,r){t[e]=r.value},o="function"==typeof Symbol?Symbol:{},i=o.iterator||"@@iterator",a=o.asyncIterator||"@@asyncIterator",c=o.toStringTag||"@@toStringTag";function u(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{u({},"")}catch(t){u=function(t,e,r){return t[e]=r}}function s(t,e,r,o){var i=e&&e.prototype instanceof p?e:p,a=Object.create(i.prototype),c=new S(o||[]);return n(a,"_invoke",{value:_(t,r,c)}),a}function f(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=s;var l={};function p(){}function y(){}function d(){}var h={};u(h,i,(function(){return this}));var m=Object.getPrototypeOf,v=m&&m(m(x([])));v&&v!==e&&r.call(v,i)&&(h=v);var g=d.prototype=p.prototype=Object.create(h);function b(t){["next","throw","return"].forEach((function(e){u(t,e,(function(t){return this._invoke(e,t)}))}))}function w(t,e){function o(n,i,a,c){var u=f(t[n],t,i);if("throw"!==u.type){var s=u.arg,l=s.value;return l&&"object"==_typeof(l)&&r.call(l,"__await")?e.resolve(l.__await).then((function(t){o("next",t,a,c)}),(function(t){o("throw",t,a,c)})):e.resolve(l).then((function(t){s.value=t,a(s)}),(function(t){return o("throw",t,a,c)}))}c(u.arg)}var i;n(this,"_invoke",{value:function(t,r){function n(){return new e((function(e,n){o(t,r,e,n)}))}return i=i?i.then(n,n):n()}})}function _(t,e,r){var n="suspendedStart";return function(o,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===o)throw i;return N()}for(r.method=o,r.arg=i;;){var a=r.delegate;if(a){var c=O(a,r);if(c){if(c===l)continue;return c}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var u=f(t,e,r);if("normal"===u.type){if(n=r.done?"completed":"suspendedYield",u.arg===l)continue;return{value:u.arg,done:r.done}}"throw"===u.type&&(n="completed",r.method="throw",r.arg=u.arg)}}}function O(t,e){var r=e.method,n=t.iterator[r];if(void 0===n)return e.delegate=null,"throw"===r&&t.iterator.return&&(e.method="return",e.arg=void 0,O(t,e),"throw"===e.method)||"return"!==r&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+r+"' method")),l;var o=f(n,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,l;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,l):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,l)}function E(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function j(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function S(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(E,this),this.reset(!0)}function x(t){if(t){var e=t[i];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,o=function e(){for(;++n<t.length;)if(r.call(t,n))return e.value=t[n],e.done=!1,e;return e.value=void 0,e.done=!0,e};return o.next=o}}return{next:N}}function N(){return{value:void 0,done:!0}}return y.prototype=d,n(g,"constructor",{value:d,configurable:!0}),n(d,"constructor",{value:y,configurable:!0}),y.displayName=u(d,c,"GeneratorFunction"),t.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===y||"GeneratorFunction"===(e.displayName||e.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,d):(t.__proto__=d,u(t,c,"GeneratorFunction")),t.prototype=Object.create(g),t},t.awrap=function(t){return{__await:t}},b(w.prototype),u(w.prototype,a,(function(){return this})),t.AsyncIterator=w,t.async=function(e,r,n,o,i){void 0===i&&(i=Promise);var a=new w(s(e,r,n,o),i);return t.isGeneratorFunction(r)?a:a.next().then((function(t){return t.done?t.value:a.next()}))},b(g),u(g,c,"Generator"),u(g,i,(function(){return this})),u(g,"toString",(function(){return"[object Generator]"})),t.keys=function(t){var e=Object(t),r=[];for(var n in e)r.push(n);return r.reverse(),function t(){for(;r.length;){var n=r.pop();if(n in e)return t.value=n,t.done=!1,t}return t.done=!0,t}},t.values=x,S.prototype={constructor:S,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method="next",this.arg=void 0,this.tryEntries.forEach(j),!t)for(var e in this)"t"===e.charAt(0)&&r.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=void 0)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var e=this;function n(r,n){return a.type="throw",a.arg=t,e.next=r,n&&(e.method="next",e.arg=void 0),!!n}for(var o=this.tryEntries.length-1;o>=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var c=r.call(i,"catchLoc"),u=r.call(i,"finallyLoc");if(c&&u){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!u)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,e){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev<o.finallyLoc){var i=o;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=e&&e<=i.finallyLoc&&(i=null);var a=i?i.completion:{};return a.type=t,a.arg=e,i?(this.method="next",this.next=i.finallyLoc,l):this.complete(a)},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&e&&(this.next=e),l},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),j(r),l}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;j(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:x(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),l}},t}function asyncGeneratorStep(t,e,r,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void r(t)}c.done?e(u):Promise.resolve(u).then(n,o)}function _asyncToGenerator(t){return function(){var e=this,r=arguments;return new Promise((function(n,o){var i=t.apply(e,r);function a(t){asyncGeneratorStep(i,n,o,a,c,"next",t)}function c(t){asyncGeneratorStep(i,n,o,a,c,"throw",t)}a(void 0)}))}}function ownKeys(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function _objectSpread(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?ownKeys(Object(r),!0).forEach((function(e){_defineProperty(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):ownKeys(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function _defineProperty(t,e,r){return(e=_toPropertyKey(e))in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function _toPropertyKey(t){var e=_toPrimitive(t,"string");return"symbol"===_typeof(e)?e:String(e)}function _toPrimitive(t,e){if("object"!==_typeof(t)||null===t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var n=r.call(t,e||"default");if("object"!==_typeof(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}function _typeof(t){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},_typeof(t)}!function(){var t="MOEBEL_CLICKOUT_ID",e="data-partner-key",r="data-market",n=JSON.parse('{"at":"https://redirect.moebel24.at/api/1.0/moebel/at/sales","ch":"https://redirect.moebel24.ch/api/1.0/moebel/ch/sales","de":"https://redirect.moebel.de/api/1.0/moebel/de/sales","fr":"https://redirect.meubles.fr/api/1.0/moebel/fr/sales","nl":"https://redirect.meubelo.nl/api/1.0/moebel/nl/sales","es":"https://redirect.mobi24.es/api/1.0/moebel/es/sales"}');function o(t){return isNaN(t)||!isFinite(t)?new Error("Bad numeric value ".concat(t)):null}function i(t){return o(t)?o(t):Number.isInteger(t)?null:new Error("Bad integer value ".concat(t))}function a(t){switch(_typeof(t)){case"number":return o(t)||t;case"string":return o(parseFloat(t))||parseFloat(t);default:return new Error("Can not convert ".concat(_typeof(t)," to number (float)"))}}function c(t){switch(_typeof(t)){case"string":return t;case"number":return o(t)||t.toString();default:return new Error("Can not convert ".concat(_typeof(t)," to string"))}}function u(t){var e=_objectSpread({},t),r=a(t.price);r instanceof Error&&console.error("Item price conversion error: ".concat(r.message)),e.price=r;var n=function(t){switch(_typeof(t)){case"number":return i(t)||t;case"string":return i(parseFloat(t))||parseInt(t,10);default:return new Error("Can not convert ".concat(_typeof(t)," to number (float)"))}}(t.quantity);if(n instanceof Error&&console.error("Item quantity conversion error: ".concat(n.message)),e.quantity=n,-1===Object.keys(t).indexOf("item_category")&&-1===Object.keys(t).indexOf("category")||(delete e.item_category,e.category=-1!==Object.keys(t).indexOf("item_category")?t.item_category:t.category),t.id){var o=c(t.id);if(o instanceof Error)return console.error("Item id can not be converted to string: ".concat(o.message)),o;e.id=o}if(t.item_id){var u=c(t.item_id);if(u instanceof Error)return console.error("Item item_id can not be converted to string: ".concat(u.message)),u;e.id=u}return e}function s(t){console.error("Received sales object: ".concat(JSON.stringify(t)))}var f=function(o){return function(i){if(!i)return console.error("No sales object provided"),void s(i);var f;try{f=function(t){var o=PARTNER_KEY;if(!o)throw new Error("".concat(e," not defined on push script"));var i="".concat(o).replace(/ /g,"").split(","),a=t.getAttribute(r)?"".concat(t.getAttribute(r)).replace(/ /g,"").toLowerCase().split(","):[MARKET];return a.forEach((function(t){if(-1===Object.keys(n).indexOf(t))throw new Error("Unsupported value in ".concat(r,": ").concat(t))})),{partnerKeys:i,useMarkets:a}}(o)}catch(t){return void console.error(t)}var l,p=function(t){try{for(var e=_objectSpread({},t),r=0,n=["total","shipping"];r<n.length;r++){var o=n[r],i=a(t[o]);if(i instanceof Error)return console.error("Can not convert ".concat(o," to number ").concat(i.message)),i;e[o]=i}if(e.orderId){var s=c(t.orderId);if(s instanceof Error)return console.error("Can not convert orderId to string ".concat(s.message)),s;e.orderId=s}if(t.items&&Array.isArray(t.items)){var f=t.items.map(u),l=f.find((function(t){return t instanceof Error}));if(l)return console.error("Can not convert items: ".concat(l.message)),l;e.items=f}return e}catch(t){return t}}(i);if(p instanceof Error)return console.error("Error sanitizing sale ".concat(p.message)),void s(i);if(function(t){if(!t)return console.error("No sales object provided to the API"),!1;if(!function(t){return t&&Array.isArray(t)&&0!==t.length?!t.some((function(t){return!function(t){if(!t)return console.error("Sales object item ".concat(t," should be object instead")),!1;if(Object.keys(t).filter((function(t){return"category"===t||"item_category"===t})).map((function(e){return t[e]})).filter((function(t){return"string"!=typeof t})).length)return console.error("item.category OR item.item_category must be a string if present"),!1;var e=t.category||t.item_category||"";if(Object.keys(t).find((function(t){return"category"===t||"item_category"===t}))&&"string"!=typeof e)return console.error("Sales object item ".concat(JSON.stringify(t)," item.category OR item.item_category is missing")),!1;var r=t.item_id||t.id;return!r||"string"!=typeof r&&a(r)instanceof Error?(console.error("Sales object item ".concat(JSON.stringify(t)," is missing item.id OR item.item_id field, or it is not a non-empty string/number")),!1):Number.isInteger(t.quantity)?!(Number.isNaN(t.price)||!Number.isFinite(t.price))||(console.error("Sales object item price for item ".concat(JSON.stringify(t)," is not a number")),!1):(console.error("Sales object item quantity for item ".concat(JSON.stringify(t)," is not an integer number")),!1)}(t)})):(console.error("Items must be a non-empty array, got ".concat(JSON.stringify(t)," instead")),!1)}(t.items))return!1;if(t.hasOwnProperty("currency")&&"string"!=typeof t.currency)return console.error("Currency shold be string"),!1;for(var e=0,r=["total","shipping"];e<r.length;e++){var n=r[e];if(Number.isNaN(t[n])||!Number.isFinite(t[n]))return console.error("Sales object ".concat(n," does not exist or is not a number")),!1}return!0}(l=p)){var y=function(){var e=localStorage.getItem(t);if(!e)return null;try{var r=JSON.parse(e);if(!(null==r?void 0:r.clickId)||!(null==r?void 0:r.date)||Number.isNaN(new Date(null==r?void 0:r.date).getTime()))return console.error("Click id has corrupted data"),null;var n=new Date(r.date),o=new Date;return n.getTime()+7776e6<o.getTime()?(console.log("Deleting expired click id"),localStorage.removeItem(t),null):r}catch(t){return console.error(t),null}}();if(y){var d=[];return f.partnerKeys.forEach((function(t){f.useMarkets.forEach((function(e){return d.push((r=_asyncToGenerator(_regeneratorRuntime().mark((function t(e,r,o){var i;return _regeneratorRuntime().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,i=n[e],t.next=4,fetch("".concat(i,"?key=").concat(o),{method:"POST",headers:{"Content-type":"application/json"},mode:"no-cors",body:JSON.stringify(r)});case 4:t.next=9;break;case 6:t.prev=6,t.t0=t.catch(0),console.error("Error submitting sale to API: ".concat(t.t0.message));case 9:case"end":return t.stop()}}),t,null,[[0,6]])}))),function(t,e,n){return r.apply(this,arguments)})(e,function(t,e){var r={currency:t.currency||"EUR",items:t.items.map((function(t){var e=t.category,r=t.id,n=t.item_id,o=t.item_category;return _objectSpread({item_id:r||n,price:t.price,quantity:t.quantity},e||o||""===e||""===o?{item_category:e||o||""}:{})})),moeclid:e,value:t.total,shipping:t.shipping,type:"c2s"};return t.orderId&&(r.order_id=t.orderId),r}(l,y.clickId),t));var r}))})),d}}else s(i)}};PARTNER_KEY?function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"MOEBEL_SALES",n={sale:f(e)};t[r]=n}(window,document.currentScript):console.error("Can not define moebel clickout API - partner key missing")}();
MOEBEL_SALES.sale({
total: {{ecommerce.value}}, // Can be float or integer, required
shipping: {{ecommerce.shipping}}, // Can be float or integer, required
currency: "EUR", // Optional, default: EUR
orderId: "{{ecommerce.transaction_id}}", // Optional
items: {{ecommerce.items}} // Array of Items, required
});
</script>
-
Enter your partner key (provided by your account manager) in line 3
-
Enter your market in line 4, should be one of "de","fr","nl","at","ch","es".
-
Add all required variables in the sendSale() function. If you have UA/GA4 eCommerce tracking enabled, all required variables should already be set up with the “purchase” event.
A description of all parameters with example values you can find in the table below:
parameter | description | mandatory field | data type | example | comment |
---|---|---|---|---|---|
total |
gross total basket value, without shipping costs, including tax |
yes |
float |
1499.97 |
2 digits, "." as decimal delimiter, no thousand delimiters |
shipping |
shipping costs |
yes |
float |
29.99 |
2 digits, "." as decimal delimiter, no thousand delimiters |
items |
array of unique items in order. elements: - - - - |
yes |
array |
[{ "item_id": "47886359", "quantity": "1", "price": "899.99", "item_category": "boxspringbetten" }, { "item_id": "31118801", "quantity": "2", "price": "299.99", "item_category": "heimtextilien" }] |
|
currency |
currency |
yes |
string |
EUR |
DIN norm |
orderid |
your identifier for the order |
no |
string |
1172210481407 |
-
Add the "purchase" event as trigger and save the tag

-
Publish your container version