Installing the Onsite Messaging script
Prerequisites
- For GTM Implementation: Google Tag Manager correctly installed on your website. (GTM Installation Guide) 
- Access to your GTM container with publish permissions 
- Access to your website’s dataLayer or variables for: - Order totals and revenue 
- Customer email addresses 
- Currency codes 
- Discount/coupon codes 
 
- A test environment to validate implementation 
- (Optional) Voyado Chrome Extension installed for testing 
Implementation paths
Google Tag Manager (SPA)
Single Page Applications (SPAs) require special handling because page navigation doesn’t trigger traditional page loads. This guide shows how to implement all four recommended tags.
Understanding SPA Requirements
Why SPAs are different:
Critical Rule: If your trigger uses custom pageload events, your exceptions must also use custom pageload events. Mixing event types (e.g., history change with pageview exceptions) will cause failures.
- Navigation happens without full page reloads 
- Scripts must trigger on virtual page views 
- Open widgets persist during navigation and must be explicitly closed 
- All triggers must use consistent event types 
GTM Tag Configuration
You’ll create four tags. Suggested naming:
- Voyado Onsite Cornerwidget – Displays messaging widgets broadly on the website 
- Voyado Onsite Hide – Closes any open widgets on checkout/cart pages 
- Voyado Onsite Purchase – Tracks conversions and displays messages on order confirmation page 
- Voyado Onsite Login – Identifies user and enables personalized onsite messaging 
Tag #1: Voyado Onsite Cornerwidget
Purpose: Load and display messaging widgets on landing pages and virtual page views
Trigger: All pageviews EXCEPT checkout, cart, and order confirmation pages. Should also be excluded when login script is called instead.
Script:
<script>
if (typeof redeal !== 'function') {
  (function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
}
redeal('cornerwidget');
</script>Why the if (typeof redeal !== 'function') check? This prevents loading the script multiple times on SPAs. The script loader only runs once, but redeal('cornerwidget') can be called on each virtual page load.
Configuration in GTM:
- Tag Type: Custom HTML 
- Trigger: Custom Event on page load 
- Exceptions: Checkout page, Cart page, Order confirmation page and Login event 
Alternative tag names: Replace ‘cornerwidget’ with any tag name configured in Onsite Manager. Use multiple tags with different conditions to trigger different onsite messages.
Tag #2: Voyado Onsite Hide
Purpose: Close open widgets when users navigate to pages where widgets shouldn’t appear (checkout, cart)
Why is this needed? On SPAs, excluding pages from Tag #1 only prevents NEW widgets from loading. It doesn’t close widgets that are already open. This tag explicitly closes them.
Trigger: Pageview on checkout and cart pages
Script:
<script>
if(typeof redealHide === 'function') {
  redealHide();
}
</script>Configuration in GTM:
- Tag Type: Custom HTML 
- Trigger: Custom Event on page load when Page Path contains “/checkout” or “/cart” 
Tag #3: Voyado Onsite Purchase
Purpose: Track conversions and optionally trigger post‑purchase onsite messages
Trigger: Order confirmation page load (immediately after purchase)
Script:
<script>
if (typeof redeal !== 'function') {
  (function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
}
if(typeof IsRedealOpen !== 'function' || !IsRedealOpen('checkout')) {
  redeal('checkout', {
    total: {{sumtotal}},
    revenue: {{sumrevenue}},
    currency: {{currencycode}},
    email: {{userEmail}},
    coupons: [{{discountcode}}]
  });
}
</script>| Variable Name | Type | Example value | Description | 
|---|---|---|---|
| {{sumtotal}} | Data Layer Variable | "113.75" | Total amount paid by customer (string) | 
| {{sumrevenue}} | Data Layer Variable | "93" | Revenue excluding tax and shipping (string) | 
| {{currencycode}} | Data Layer Variable | "SEK" | ISO currency code | 
| {{userEmail}} | Data Layer Variable | "john@doe.com" | Customer email address | 
| {{discountcode}} | Data Layer Variable | "aJ7q54JU" | Coupon/discount code used (can be array) | 
Expected output format:
redeal('checkout', {
  total: "113.75",
  revenue: "93",
  currency: "SEK",
  email: "john@doe.com",
  coupons: ["aJ7q54JU"]
});Important notes:
- total = full amount paid by customer (including tax, shipping, fees) 
- revenue = amount excluding tax, shipping, and fees 
- coupons should be an array, even with one code 
- Missing or null values won’t break functionality but will limit tracking and reporting 
- Optional fields: name, phone (useful for prefilling onsite messages and automatically identifying visitors) 
Configuration in GTM:
Tag Type: Custom HTML
Trigger: Custom Event associated with reaching the order confirmation page, for example “purchase”, or when Page Path contains “/order‑confirmation” or “/thank‑you”
Tag #4: Voyado Onsite Login
Purpose: Identifying unidentified visitors and triggering personalized onsite messages
When to trigger:
- When a user actively logs in (login button click) 
- On page load if user is already logged in (“soft login”) 
Trigger: Login event OR pageview when user is authenticated
Important: The login script should only be called once per session, not on subsequent page views after the login event occurs. On subsequent page views calling the cornerwidget script is expected.
Script:
<script>
if (typeof redeal !== 'function') {
  (function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
}
if(typeof IsRedealOpen !== 'function' || !IsRedealOpen('login')) {
  redeal('login', {
    email: {{userEmail}}
  });
}
</script>Required GTM Variables:
- {{userEmail}} – User’s email address (unhashed string) 
Optional fields:
- phone: {{userPhone}} – User’s phone number 
- contactId: {{contactID}} – User’s contact ID in Voyado Engage 
Configuration in GTM:
- Tag Type: Custom HTML 
- Trigger: Custom Event user_login OR first page view when User State = “logged_in”, expected to fire only once per session 
Benefits:
- Enables automated consent updates via Reach Ability feature 
- Triggers onsite messages to complete missing profile data 
- Improves contact data quality 
- Improves onsite scripts user identification 
Google Tag Manager (Traditional Sites)
For traditional multi‑page applications (non‑SPAs), the implementation is simpler because each page navigation is a full page load.
Key Differences from SPA Implementation
- No need for the if (typeof redeal !== 'function') check 
- No need for the Hide tag (widgets don’t persist across page loads) 
Tag #1: Cornerwidget (Traditional Sites)
Script:
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('cornerwidget');
</script>Trigger: All Pages EXCEPT checkout and order confirmation.
Tag #2: Purchase (Traditional Sites)
Script:
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('checkout', {
  total: {{sumtotal}},
  revenue: {{sumrevenue}},
  currency: {{currencycode}},
  email: {{userEmail}},
  coupons: [{{discountcode}}]
});
</script>Trigger: Order Confirmation Page
Tag #3: Login (Traditional Sites)
Script:
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('login', {
  email: {{userEmail}}
});
</script>Trigger: Login success event OR first page view when user is authenticated
Direct implementation
For sites without GTM or when you want full control, you can add the script directly to your HTML.
Basic script structure
Place this immediately after the opening <body> tag on all pages where messaging should appear:
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
</script>This loads the script asynchronously without blocking page rendering.
Implementing each tag type
Cornerwidget (landing pages):
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('cornerwidget');
</script>With user prefill (when user data is available):
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('cornerwidget', {
  email: "",
  name: "",
  phone: ""
});
</script>Checkout (order confirmation page):
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('checkout', {
  total: "",
  revenue: "",
  currency: "",
  email: "",
  coupons: []
});
</script>Login (login page or when user is authenticated):
<script>
(function(i,s,o,g,r,a,m){i['RedealObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window, document, 'script', 'https://static.redeal.se/widget/js/redeal.js', 'redeal');
redeal('login', {
  email: "",
  phone: ""
});
</script>Hide function (SPAs only):
// Call this when navigating to pages where widgets should be hidden
if(typeof redealHide === 'function') {
  redealHide();
}Script reference
Standard tag names
| Tag name | When to use | Recommended data | 
|---|---|---|
| cornerwidget | Landing pages, general browsing | None (optional/if available: email, name, phone, contact ID) | 
| checkout | Order confirmation/thank you page | total, revenue, currency, email, coupons | 
| login | After user login or when authenticated | email (optional/alternative: phone, contact ID) | 
| custom | Any custom trigger defined in Onsite Manager | Varies by onsite message | 
Data field specifications
Checkout fields:
{
  total: "113.75",        // String: Total amount paid (including tax, shipping)
  revenue: "93",          // String: Revenue excluding tax and fees
  currency: "SEK",        // String: ISO 4217 currency code
  email: "user@example.com",  // String: Customer email
  coupons: ["CODE123"],   // Array: Discount codes used
  name: "John Doe",       // Optional String: Customer name
  phone: "+46701234567",  // Optional String: Customer phone
  contactId: "b9a02dc6-52b8-4e11-b929-b37500f002f1"  // Optional String: User’s Contact ID in Engage
}Cornerwidget fields (all optional):
{
  email: "user@example.com",
  name: "John Doe",
  phone: "+46701234567",
  contactId: "b9a02dc6-52b8-4e11-b929-b37500f002f1"
}Login fields
{
  email: "user@example.com",     // Optional: User email
  phone: "+46701234567",         // Optional: User phone
  contactId: "b9a02dc6-52b8-4e11-b929-b37500f002f1"  // Optional: User’s Contact ID in Engage
}Note
Which of the field(s) above to pass in the script call is optional, but at least one needs to be passed for script identification upon login to work.
Custom trigger tags
You can define custom tags in Onsite Manager and trigger them:
redeal('custom-tag-name');Use case: Trigger different onsite messages based on user actions:
<button onclick="redeal('special-offer)">View Offer</button>A/B Testing: If multiple onsite messages use the same script tag and fulfil any additional targeting criteria needed to be displayed, one will be randomly selected and displayed to the user.
Hide function
Basic usage (hide all open widgets):
redealHide();
Onsite message URL links
Each onsite message in Onsite Manager generates an onsite message URL link as well as a QR code. Use these links in emails or social media to drive traffic to your website and trigger the appropriate onsite messages automatically.
With personalization variables (for email marketing):
https://yoursite.com/onsite-message-url?email=*|EMAIL|*&name=*|NAME|*
Replace *|EMAIL|* and *|NAME|* with the merge tag format from your email service provider.
Domain matching
Important: Onsite messages only trigger on domains configured in Onsite Manager.
Use:
Production domain: www.example.com
Test subdomain: test.example.com
Do not use:
Local development: localhost or 127.0.0.1
IP addresses: 192.168.1.100
For testing on non-production domains: Add the test domain to your site configuration in Onsite Manager.
Testing and validation
Using the Chrome Extension
- Install the Voyado Chrome Extension 
- Navigate to your website 
- Wait for the page to fully load 
- Click the Voyado extension icon 
- Verify that the script is detected 
Note
The extension only detects if the script is loaded on the current page. It doesn’t validate triggers or data.
Browser Console Checks
Use this URL parameter to activate debug mode:
https://yoursite.com/?voyado-debug
Open your browser’s developer console (F12) and verify:
- Script loaded successfully: typeof redeal === 'function' — Should return: true 
- Check if widget is open: typeof IsRedealOpen === 'function' && IsRedealOpen('cornerwidget') — Should return: true if cornerwidget is open 
- View script errors: Look for red errors in the console. Common issues: - Failed to load resource: https://static.redeal.se/widget/js/redeal.js – Script blocked by firewall/CSP 
- redeal is not a function – Script not loaded or loaded after trigger call 
 
Clear testing session
Use this URL parameter to reset your test session:
https://yoursite.com/?voyado-clear
This clears local and session storage, allowing you to test onsite messages as an unidentified/new visitor.
Testing checklist
- Script loads on all intended pages 
- Only one script call loads per pageview 
- Cornerwidget appears on landing pages 
- Cornerwidget does NOT appear on checkout/cart pages 
- Open widgets close when navigating to checkout (SPAs only) 
- Checkout tag fires on order confirmation with correct data 
- Login tag fires when user logs in 
- Console shows no JavaScript errors 
- All GTM variables return expected values 
- Onsite message URL links work and trigger onsite messages 
- Onsite messages only trigger on configured domains 
- Test with ?voyado-clear to simulate new visitors 
Note
For implementations using Google Tag Manager, using GTM’s Preview mode to ensure the tags are correctly installed and that triggers are firing appropriately with complete and correctly formatted variables is highly recommended.
Network tab verification
In browser DevTools > Network tab:
- Filter by “redeal” 
- Verify redeal.js loads successfully (Status: 200) 
- Check that POST requests to Voyado API return 200 status 
- Verify request payloads contain expected data 
Troubleshooting
Script not loading
Symptoms:
- Chrome extension shows no detection 
- typeof redeal returns undefined 
- No network requests to static.redeal.se 
Possible causes:
1. Content Security Policy (CSP) blocking the script
Solution: Add https://static.redeal.se to your CSP script‑src directive
2. Ad blocker or privacy extension
Solution: Disable extensions for testing or whitelist your domain
3. Firewall blocking external scripts
Solution: Whitelist static.redeal.se in your firewall rules
4. Incorrect GTM trigger configuration
Solution: Check trigger fires on page load using GTM Preview mode
5. Script tag syntax error
Solution: Validate HTML, ensure no extra characters in script tag
Widget not appearing
Symptoms:
- Script loads but no widget visible 
- Console shows no errors 
Possible causes:
1. Domain not configured in Onsite Manager
Solution: Verify your domain is added to the onsite message configuration
2. Onsite message not active
Solution: Check onsite message status in Onsite Manager
3. Onsite message already shown to this user
Solution: Test with ?voyado‑clear URL parameter
4. Wrong tag name
Solution: Verify tag name in code matches Onsite Manager configuration
5. Z‑index conflict
Solution: Check if widget is rendering but hidden behind other elements
Conversion not tracking
Symptoms:
- Checkout tag fires but conversions don’t appear in reports 
Possible causes:
1. Variables returning null or undefined
Solution: Use GTM Preview to verify variable values
2. Incorrect data types
Solution: Ensure total, revenue are strings, coupons is array
3. Currency code format
Solution: Use ISO 4217 codes (SEK, USD, EUR, etc.)
4. Race condition (script triggers before GTM variables populate)
Solution: Add a small delay or ensure dataLayer pushes before tag fires
GTM variables not populating
Symptoms:
- GTM Preview shows undefined or null variable values 
Solutions:
- Verify dataLayer structure matches your variable configuration 
- Check that dataLayer.push() happens before page load events 
- Use “Data Layer Variable” type (not “JavaScript Variable”) in GTM 
- Test dataLayer in console: console.log(dataLayer) 
SPA issues
Widget persists on checkout page:
- Ensure Hide tag is implemented and triggering correctly 
- Verify Hide tag trigger uses same event type as other triggers 
- Check GTM Preview to confirm Hide tag fires 
Widget not appearing after navigation:
- Confirm Cornerwidget tag triggers on virtual pageviews 
- Verify history change events are captured in GTM 
- Check that custom events fire consistently 
Script loaded multiple times:
- Ensure if (typeof redeal !== 'function') wrapper is present 
- Check GTM Preview for duplicate tag fires 
Console error messages
Common errors and solutions:
| Error | Cause | Solution | 
|---|---|---|
| Failed to load resource: https://static.redeal.se/… | Script blocked | Check CSP, firewall, ad blockers | 
| redeal is not a function | Script not loaded before call | Ensure script loader runs first | 
| IsRedealOpen is not defined | Script not fully initialized | Add conditional check: typeof IsRedealOpen === 'function' | 
| Cannot read property 'email' of undefined | Missing data object | Verify GTM variables are defined | 
Performance & security
Content Security Policy (CSP)
If your site uses CSP headers, add these directives:
Content‑Security‐Policy: script‑src 'self' https://static.redeal.se; connect‑src 'self' https://*.redeal.se;
Without these directives, the script will be blocked by the browser.
Script loading strategy
The Voyado script uses async loading to minimise performance impact:
a.async=1; // Script loads asynchronously
Benefits:
- Non‑blocking: Page rendering continues while script loads 
- Fast: Script hosted on CDN with global distribution 
- Cached: Browser caches script for subsequent visits 
Performance Impact:
- Initial load: ~15‑25 KB (minified and gzipped) 
- Subsequent loads: 0 KB (cached) 
- Page load delay: <50 ms on average 
Best practices
Do:
- Load script on all relevant pages (cornerwidget is reusable) 
- Use async loading (included in implementation) 
- Implement proper CSP directives 
- Test on staging before production 
- Monitor console for errors after deployment 
Don’t:
- Block page rendering waiting for script 
- Load script multiple times on same page (use conditional checks for SPAs) 
- Hardcode customer data in JavaScript (security risk) 
- Skip testing on different browsers 
- Forget to update CSP when implementing 
Data privacy
Email handling:
- Emails are transmitted over HTTPS 
- Never expose emails in URLs (use POST data) 
- Comply with GDPR/privacy regulations in your region 
- Inform users about data collection in your privacy policy 
Session data:
- Script uses local storage and session storage 
- Data is stored client‑side only 
- Use ?voyado‑clear to manually clear stored data 
Browser compatibility
The script is compatible with:
- Chrome (latest) 
- Firefox (latest) 
- Safari (latest) 
- Edge (latest) 
- Mobile browsers (iOS Safari, Chrome Mobile) 
Not supported:
- Internet Explorer 11 and earlier 
Additional resources
Onsite Manager: Configure onsite messages, sites/domains and view analytics
Chrome Extension: Download here
GTM Documentation: Google Tag Manager Help
Voyado Support: Contact your Voyado representative for technical assistance
Need help?
If you have questions or encounter issues not covered in this guide, reach out to Voyado support or your Voyado representative.