A plain-English guide to the updated welcome pack flow: popup shown once, offer also delivered via email, one campaign in Optimove.
When a new player registers, they see a popup showing three welcome offers โ once and only once. If they close it without choosing, the same three links are in their welcome email. Whichever they click, the site records the selection and Optimove awards the right bonus.
uncompleted_sequence_1
if the business wants it.
Four things happen in sequence from signup to bonus award.
Player registers. Optimove sends a welcome email on sign_up and shows the popup on
sign_up โ page_visit within 15 min. The popup requires page_visit because
Optimove can only display popups on a live FE page event โ the email fires immediately on registration
without waiting for a page load.
Clicking any offer โ in the popup or in the email โ opens the site at
moonspin.us?selected-offer-award-id=<id>&selected-offer-promo-id=<promoId>. Both the award and the promo identifier are embedded in the URL.
The site reads the URL param. Once the player is authenticated it calls the BE, which sends a
promo_award_selected event to Optimove with award_id and promo_id.
The single real-time trigger fires (filter: award_id IN (โฆ) AND promo_id = 'โฆ', once per
lifetime). BE receives the callback, reads award_id from the event, and awards the selected
pack.
?selected-offer-award-id= + ?selected-offer-promo-id= from any entry point โ popup redirect, email click, or
direct URL share.
Each offer has a Package ID โ the SwPackage UUID used in the URL param, the Optimove trigger filter, and the BE award logic. You'll need all three IDs when configuring the trigger and the email links.
Three things to configure: the popup campaign, the welcome email, and one real-time trigger + campaign pair. Much simpler than the previous three-trigger setup.
Create a campaign that shows the welcome pack popup on the player's first page visit after signing up. In the campaign's popup step, paste the popup HTML from the bottom of this page.
The trigger requires page_visit
because Optimove can only display a popup on a live FE page event โ there is no way to show a popup
without an active browser session. The email (step 2) fires on sign_up alone and does not
need a page event.
sign_up โ page_visit within 15 minutes, once per
lifetimeCreate a welcome email in Optimove triggered by sign_up alone โ no
page_visit needed. The email fires immediately on registration, before the player even
returns to the site. Each button links to the site with the award ID and promo ID pre-set in the URL.
If the player dismissed the popup without choosing, the email is their second chance.
https://moonspin.us/en?selected-offer-award-id=a8822f1a-67d4-4110-be4a-17be0d3480db&selected-offer-promo-id=welcome_pack_v1https://moonspin.us/en?selected-offer-award-id=0a1ed776-e408-4edf-9f2b-e17d9e6c21a5&selected-offer-promo-id=welcome_pack_v1https://moonspin.us/en?selected-offer-award-id=d1fde953-1ed4-4018-a3ba-1f0a9a7defa9&selected-offer-promo-id=welcome_pack_v1
promo_award_selectedIn Optimove, go to Triggers โ Real-Time. Create a single trigger that listens for the
promo_award_selected event. Add two filters: award_id scopes it to the three
welcome pack award UUIDs; promo_id scopes it to this specific promo campaign so other future
uses of promo_award_selected don't accidentally fire this trigger. Set it to fire
once per lifetime per player.
promo_award_selected event firesaward_id IN (a8822f1a-..., 0a1ed776-..., d1fde953-...)AND promo_id = 'welcome_pack_v1'The event is sent server-to-server by
the backend when the player lands on the site with valid ?selected-offer-award-id= and
?selected-offer-promo-id= params and is authenticated. The trigger reads award_id
from the event payload so the backend knows which award to give on callback.
Create one campaign triggered by the real-time trigger above. The backend reads
package_id from the event's personalization data in the Optimove callback and awards exactly
that SwPackage. No branching needed โ a single campaign handles all three offers.
award_id
from the event's personalization data in the Optimove callback and awards exactly that pack.
If the business wants every player to receive a welcome bonus regardless of whether they pick one, add a
fallback campaign. Set up a sequential event
(uncompleted_sequence_1) that fires when sign_up occurs but
promo_award_selected does not occur within a defined window (e.g. 24 h).
Campaign 3 then auto-awards the fallback bonus.
Use Optimove's test mode with a test player. Click each offer in the popup and verify the URL redirects
correctly with the right ?selected-offer-award-id= and ?selected-offer-promo-id=
values. Check the Optimove event stream to confirm promo_award_selected arrives with the
correct award_id and promo_id. Verify
Campaign 2 fires and the right SwPackage is awarded.
If a developer is modifying the popup HTML, these are the limitations to watch for. The popup code below is already written to work around all of them.
// single-line comments inside scriptsOptimove's editor strips newlines from script blocks. A // comment would comment out all
remaining code on that line. Use /* */ block comments instead, or no comments at all.
?. optional chainingThe editor inserts a space turning ?. into ? ., which breaks JavaScript syntax and
silently kills the entire script. Use && guards instead.
onclick attributesThe site's security policy blocks inline event handlers. Use addEventListener inside the
<script> block โ it works because the script tag itself receives the security nonce.
Attach click handlers to document and use e.target.closest(). Optimove may
re-render the popup DOM after images load, which destroys listeners attached directly to elements.
The block below is an example popup for the welcome pack. You can build your own โ the design and layout can be anything. The only requirement is that the script fires the right event when a player makes a selection.
The popup no longer calls
optimoveSDK.API.reportEvent()
directly. Instead, clicking an offer navigates the player to the site with
?selected-offer-award-id=
and
selected-offer-promo-id=
URL params. The site reads both params, calls the backend, and the backend sends the
promo_award_selected
event to Optimove server-to-server. This approach is ad-blocker safe.
selected-offer-award-id in the URL.window.location.href
and sets selected-offer-award-id
+ selected-offer-promo-id,
then navigates there after a brief confirmation flash.window.optimoveSDK
at all. Works even if the SDK is blocked by an ad-blocker.<!DOCTYPE html>
<html>
<head>
<title>Optimove Welcome Pack</title>
</head>
<body>
<div class="bee-popup-container">
<style>
.bee-popup-container h1,
.bee-popup-container h2,
.bee-popup-container p {
margin: 0;
padding: 0;
}
.bee-popup-container {
color: #ffffff;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
background-color: #000000;
}
.bee-popup-container * {
box-sizing: border-box;
}
.bee-popup-row {
position: relative;
}
.bee-popup-row-content {
max-width: 700px;
position: relative;
margin: 0 auto;
display: flex;
}
.bee-popup-col-w4 {
flex: 4;
}
.bee-popup-col-w12 {
flex: 12;
}
.bee-popup-row-1 .bee-popup-row-content {
flex-direction: column;
align-items: center;
padding: 32px 24px 16px;
gap: 8px;
}
.bee-popup-eyebrow {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: #13e3ba;
opacity: 0.8;
}
.bee-popup-heading h1 {
color: #ffffff;
font-size: 30px;
font-weight: 800;
letter-spacing: -0.01em;
text-align: center;
line-height: 1.15;
}
.bee-popup-heading h1 span {
color: #13e3ba;
}
.bee-popup-subheading {
font-size: 13px;
color: rgba(255, 255, 255, 0.45);
text-align: center;
margin-top: 4px;
}
.bee-popup-row-2 .bee-popup-row-content {
padding: 16px 24px 36px;
gap: 12px;
align-items: stretch;
}
.bee-popup-offer {
position: relative;
background: linear-gradient(160deg, #161616 0%, #0e0e0e 100%);
border: 1.5px solid #2a2a2a;
border-radius: 16px;
text-align: center;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
transition: border-color 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
overflow: hidden;
}
.bee-popup-offer::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(ellipse at 50% 0%, rgba(19, 227, 186, 0.08) 0%, transparent 70%);
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
z-index: 1;
}
.bee-popup-offer:hover {
border-color: #13e3ba;
transform: translateY(-3px);
box-shadow: 0 8px 28px rgba(19, 227, 186, 0.15);
}
.bee-popup-offer:hover::before {
opacity: 1;
}
.bee-popup-offer:active {
transform: translateY(-1px);
}
.offer-banner {
width: 100%;
display: block;
border-radius: 14px 14px 0 0;
object-fit: cover;
aspect-ratio: 7 / 3;
background-color: #1a1a1a;
}
.offer-body {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 14px 14px 18px;
width: 100%;
}
.offer-highlight {
font-size: 28px;
font-weight: 800;
color: #13e3ba;
letter-spacing: -0.02em;
line-height: 1;
}
.offer-label {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.55);
margin-top: 2px;
}
.offer-divider {
width: 28px;
height: 1px;
background: rgba(19, 227, 186, 0.25);
margin: 4px auto;
}
.offer-description {
font-size: 11px;
color: rgba(255, 255, 255, 0.35);
line-height: 1.4;
}
.offer-badge {
position: absolute;
top: -1px;
right: -1px;
background: #13e3ba;
color: #000000;
font-size: 9px;
font-weight: 800;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 3px 8px;
border-radius: 0 14px 0 8px;
z-index: 2;
}
.bee-popup-row-divider {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06) 30%, rgba(255, 255, 255, 0.06) 70%, transparent);
max-width: 700px;
margin: 0 auto;
}
</style>
<div id="opt-before-select" class="bee-popup-rows-container">
<div class="bee-popup-row bee-popup-row-1">
<div class="bee-popup-row-content">
<div class="bee-popup-eyebrow">New Player Offer</div>
<div class="bee-popup-col bee-popup-col-w12">
<div class="bee-popup-heading">
<h1>CHOOSE YOUR <span>WELCOME PACK</span></h1>
</div>
</div>
<div class="bee-popup-subheading">Pick one offer to claim on your first deposit</div>
</div>
</div>
<div class="bee-popup-row-divider"></div>
<div class="bee-popup-row bee-popup-row-2">
<div class="bee-popup-row-content">
<div class="bee-popup-col bee-popup-col-w4">
<div class="bee-popup-offer" data-award-id="a8822f1a-67d4-4110-be4a-17be0d3480db" data-promo-id="welcome_pack_v1">
<picture>
<source media="(min-width: 480px)" srcset="https://swivel-production-public.s3.eu-west-1.amazonaws.com/sw-packages/7a801a29-c686-4a5a-b533-2d3075e164ca.largeImage">
<img class="offer-banner" src="https://swivel-production-public.s3.eu-west-1.amazonaws.com/sw-packages/1f1f56c2-20f5-4301-aa89-38c3b00922ba.smallImage" alt="Starter Pack">
</picture>
<div class="offer-body">
<div class="offer-highlight">100%</div>
<div class="offer-label">Deposit Bonus</div>
<div class="offer-divider"></div>
<div class="offer-description">Up to $200
<br>matched bonus</div>
</div>
</div>
</div>
<div class="bee-popup-col bee-popup-col-w4">
<div class="bee-popup-offer" data-award-id="0a1ed776-e408-4edf-9f2b-e17d9e6c21a5" data-promo-id="welcome_pack_v1">
<div class="offer-badge">Popular</div>
<picture>
<source media="(min-width: 480px)" srcset="https://swivel-production-public.s3.eu-west-1.amazonaws.com/sw-packages/bef4de2e-337e-41ca-83f3-3adb02e24c29.smallImage">
<img class="offer-banner" src="https://swivel-production-public.s3.eu-west-1.amazonaws.com/sw-packages/bef4de2e-337e-41ca-83f3-3adb02e24c29.smallImage" alt="Free Spins Pack">
</picture>
<div class="offer-body">
<div class="offer-highlight">50</div>
<div class="offer-label">Free Spins</div>
<div class="offer-divider"></div>
<div class="offer-description">No wagering
<br>on winnings</div>
</div>
</div>
</div>
<div class="bee-popup-col bee-popup-col-w4">
<div class="bee-popup-offer" data-award-id="d1fde953-1ed4-4018-a3ba-1f0a9a7defa9" data-promo-id="welcome_pack_v1">
<picture>
<source media="(min-width: 480px)" srcset="https://swivel-production-public.s3.eu-west-1.amazonaws.com/sw-packages/99ba733e-6f85-4861-9984-6c7beb27a5bc.largeImage">
<img class="offer-banner" src="https://d3aaoo30se75mz.cloudfront.net/1712/2026_05_26_9_00_33/smallImg.png" alt="VIP Pack">
</picture>
<div class="offer-body">
<div class="offer-highlight">200%</div>
<div class="offer-label">VIP Bonus</div>
<div class="offer-divider"></div>
<div class="offer-description">Up to $1000
<br>+ priority support</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="opt-after-select" style="display:none; padding:48px 24px; text-align:center; background:#000;">
<div style="font-size:36px; margin-bottom:12px;">โ</div>
<div style="font-size:18px; font-weight:800; color:#13e3ba;">Offer selected</div>
<div style="font-size:13px; color:rgba(255,255,255,0.45); margin-top:8px;">Head to the coin store and buy it.</div>
</div>
</div>
<script>
console.debug("Popup Open");
document.addEventListener("click", function(e)
{
var offer = e.target.closest(".bee-popup-offer[data-award-id]");
if (!offer)
{
return;
}
var awardId = offer.getAttribute("data-award-id");
var promoId = offer.getAttribute("data-promo-id");
var url = new URL(window.location.href);
url.searchParams.set("selected-offer-award-id", awardId);
url.searchParams.set("selected-offer-promo-id", promoId);
document.getElementById("opt-before-select").style.display = "none";
document.getElementById("opt-after-select").style.display = "block";
setTimeout(function()
{
window.location.href = url.toString();
}, 1500);
});
</script>
</body>
</html>