are one click away when you need a human.
## What’s next
[Section titled “What’s next”](#whats-next)
We’re tracking which questions don’t yet have a great answer, so we can grow our docs where you actually need them. If you ask the Assistant something and the response feels thin, that’s a signal we’re listening for — keep asking.
# New: Set a Maximum Number of Uses Per Order on BOGO Offers
You can now **set a maximum number of uses per order** on Quikly BOGO offers. The discount will apply to at most that many qualifying sets of items in a single order — so a “buy one, get one” stays a true one-for-one, even when a shopper loads up their cart.
This is live today.
## How to set up a BOGO on Quikly
[Section titled “How to set up a BOGO on Quikly”](#how-to-set-up-a-bogo-on-quikly)
Setting up a buy-one-get-one offer takes a few steps in the offer builder:
1. **Set up your discount** — choose a percentage or dollar amount off.
2. **Choose what it applies to** — point the discount at specific products or collections. This is the “get one” part of BOGO.
3. **Click “Require specific product(s) in cart”** — this unlocks the conditions that trigger the offer.
4. **Define the trigger conditions** — set the minimum quantity (or purchase amount) and which products or collections qualify. This is the “buy one” part. In the example below, the BOGO is set up for items in a full-priced collection.
5. **Optionally, check “Set a maximum number of uses per order”** — this is the new feature. Enter how many qualifying sets the discount can apply to in one order.

## Why the cap matters
[Section titled “Why the cap matters”](#why-the-cap-matters)
Without a cap, a BOGO applies to every qualifying set in the cart. If a shopper adds eight qualifying items, they’d get four discounted — which isn’t always what you intended.
With **Maximum uses per order** set to `1`, the discount applies to just one set: buy one, get one, full stop. Set it to `2` and a shopper can earn the deal twice, and so on. It’s a simple way to keep promotional margin predictable while still running an enticing offer.
***
This is available now in the offer builder. If you have questions about structuring BOGO offers, reach out to your account team.
# Campaign Visibility: Show the Right Offer to the Right Shopper
One of the most common questions we hear from Shopify merchants is: “How do I make sure my campaign only shows up where it makes sense?”
Maybe you’re running a promotion for a specific product line. Or you want to show different offers on your homepage versus your product pages. Or perhaps you only want visitors from a specific landing page to see your campaign at all.
With Quikly’s visibility controls, you can do all of this—and more.
## Introducing Campaign Visibility Controls
[Section titled “Introducing Campaign Visibility Controls”](#introducing-campaign-visibility-controls)
We’ve just published a comprehensive guide to [Campaign Visibility Controls](/shopify/tutorials/visibility-controls) that walks you through every option available for controlling where and how your campaigns appear.
The guide covers two levels of control:
### Campaign-Level Controls
[Section titled “Campaign-Level Controls”](#campaign-level-controls)
These affect your entire campaign across all placements:
* **URL Rules (Visitor Session)** — Qualify visitors based on how they arrived at your site. Perfect for email-exclusive promotions or affiliate landing pages.
* **Page URL Rules** — Control which pages display the campaign site-wide.
* **Visibility Percentage** — Run A/B tests by showing your campaign to a percentage of visitors.
* **Visitor Limit** — Cap the total number of participants.
### Component-Level Controls
[Section titled “Component-Level Controls”](#component-level-controls)
These let you customize individual placements within a campaign:
* **URL Filtering** — Show or hide specific components based on URL patterns.
* **Template Filtering** — Target Shopify template types like product pages, collection pages, or the cart.
* **Product Handle Filtering** — Show or hide components on specific products.
The combination of campaign-level and component-level controls gives you precise targeting. For example, you could run a sitewide campaign but show a banner on all pages while restricting a popup to only appear on product pages.
[Read the full visibility controls guide](/shopify/tutorials/visibility-controls)
## More Tutorials to Help You Get the Most from Quikly
[Section titled “More Tutorials to Help You Get the Most from Quikly”](#more-tutorials-to-help-you-get-the-most-from-quikly)
While you’re exploring, check out these other tutorials in our Shopify section:
**[Configuring Offers](/shopify/tutorials/offer-configuration)** — Learn how to control which products your discounts apply to, including how to create smart collections in Shopify to exclude specific items from promotions.
**[Show or Hide Components on Specific Products](/shopify/tutorials/product-page-targeting)** — A deep dive into product handle filtering with step-by-step instructions and wildcard pattern examples.
**[How to Duplicate a Campaign](/shopify/tutorials/duplicate-campaign)** — Save time by duplicating existing campaigns. Includes a video walkthrough showing how to copy a campaign and customize it for your next promotion.
***
Have questions about setting up your campaigns? Reach out to your account team—we’re here to help you get the most out of Quikly.
# What's New: One-Click Klaviyo & In-App Help
Two things to share today: a much-improved Klaviyo connection flow, and a new place inside brand admin to get help.
## Klaviyo: One-Click OAuth Connection
[Section titled “Klaviyo: One-Click OAuth Connection”](#klaviyo-one-click-oauth-connection)
The Klaviyo integration now uses **OAuth**, which means you no longer need to generate a private API key and paste it into Quikly to connect.
Click **Connect Klaviyo**, approve the connection in Klaviyo’s UI, and you’re done. Existing lists and profiles are detected automatically, and you can revoke the connection from either Quikly or Klaviyo at any time.
A few notes:
* The new flow works for both fresh Shopify installs and existing brand admin users — if you’re already connected via API key, your integration keeps working, and you can switch to OAuth any time from your integration settings.
* Under the hood, we’re running on the latest Klaviyo API revision, which means faster syncs and more accurate profile data.
* Permissions are scoped — Quikly only requests the access it actually needs to sync opt-ins and event data.
If you’ve been holding off on connecting Klaviyo because the API-key flow felt fiddly, now is a good time to try it.
## In-App Help & Support
[Section titled “In-App Help & Support”](#in-app-help--support)
Brand admin now has a dedicated **Help & Support** area — a single place for documentation, support contact, and self-serve answers.
The most useful piece is a **live search across our help docs** at dev.quikly.com. Type a question — “How do I add a tier?”, “What’s the difference between a panel and a drawer?”, “How do I target visibility by URL?” — and you’ll get an instant answer pulled from the full help library, without having to leave the app and search separately.
The same docs index powers our AI assistant, so the answers stay in sync with the docs as we update them. If you find a topic that isn’t covered well, let us know — improving the docs improves both the search and the assistant at the same time.
***
Both features are live now in your brand admin. As always, reach out to your account team with feedback or questions.
# Quikly + Klaviyo: Capture More Emails and Drive More Sales Without a Deeper Discount
Most Shopify merchants we talk to are stuck on the same trade-off:
> “I want a bigger email list and more immediate sales — but I don’t want to keep increasing my discount to get them.”
It’s a fair worry. The usual playbook is a static “10% off your first order” popup feeding a Klaviyo welcome flow. It works, sort of, but the only lever you have left to pull is a *bigger* number — 15% off, then 20%, then free shipping on top. Margin goes down, and the offer stops feeling special.
Quikly changes which lever you pull. Instead of competing on discount *size*, you compete on **urgency**: scarcity, real time limits, social proof, and FOMO. The same offer captures more emails and drives more immediate purchases — because shoppers are responding to *“only the next 50 get this”* rather than *“this is 5% better than the last popup I saw.”*
This post is the playbook for pairing Quikly with Klaviyo to do exactly that. We’ll cover:
1. How Quikly and Klaviyo fit together
2. The popup play: a daily redemption cap that lifts your email capture rate
3. Segmenting Quikly signups inside Klaviyo
4. The flows that matter most — and where urgency belongs in each
5. The cart abandonment play: an offer that *decays* the longer someone waits
6. Measuring what actually moved (revenue, not opens)
***
## 1. How Quikly and Klaviyo fit together
[Section titled “1. How Quikly and Klaviyo fit together”](#1-how-quikly-and-klaviyo-fit-together)
Quikly captures emails through urgency-driven campaigns on your storefront — popups, banners, teasers, and checkout. Klaviyo is where those subscribers land and where your automated flows nurture them into buyers. The integration connects the two so every email a Quikly campaign collects flows straight into the Klaviyo list you choose, in real time.
Connecting takes about a minute via OAuth — no API keys, no scopes. We won’t re-document it here; the step-by-step lives in the [Klaviyo integration guide](/integrations/klaviyo/). Once you’re connected, this post picks up where that guide leaves off: turning those subscribers into revenue.
One thing worth doing before you launch: set the connected Klaviyo list to **single opt-in** so subscribers are added immediately and your welcome flow can fire without waiting on a confirmation click. (Details are in the [integration guide](/integrations/klaviyo/#recommended-enable-single-opt-in).)
***
## 2. The popup play: capture more emails without a deeper discount
[Section titled “2. The popup play: capture more emails without a deeper discount”](#2-the-popup-play-capture-more-emails-without-a-deeper-discount)

Here’s the mechanic that makes Quikly different from a standard email-capture popup.
In a Quikly campaign you can set a **redemption cap** — a limit on how many shoppers can claim the offer. Pair that with a live countdown (“only the next 50 get this”) and a timer, and the popup stops being a passive “enter your email for 10% off.” It becomes *“claim this before it’s gone.”*
That reframing is the whole point. A shopper who would have ignored a flat 10% popup will hand over their email to lock in a capped offer — because the scarcity, not the discount size, is doing the persuading. You capture more emails at the *same* discount you were already willing to give.
A few ways merchants run this:
* **Sitewide welcome unit** — your standard list-growth popup, but with a capped, time-boxed reward instead of a static code.
* **Exit intent** — trigger the capped offer as a shopper moves to leave, turning abandonment into a last-second opt-in.
* **Daily caps for repeat urgency** — set the cap so it can reset on a cadence (“first 50 each day”), which keeps the FOMO fresh for returning visitors and gives you a recurring reason to drive traffic to it.
The shoppers you collect this way tend to convert better downstream, too — they opted in during a moment of urgency, so they’re primed to respond when your Klaviyo flow follows up. For more campaign structures, see [Campaign Ideas](/shopify/campaign-ideas/).
***
## 3. Segmenting Quikly signups inside Klaviyo
[Section titled “3. Segmenting Quikly signups inside Klaviyo”](#3-segmenting-quikly-signups-inside-klaviyo)
Once subscribers are flowing in, you’ll want to treat Quikly-sourced contacts differently from the rest of your list — they came in hot, and your flows should reflect that.
Quikly writes a few properties to each profile it syncs, which makes segmentation easy:
* `quikly_source` — always set to `quikly`; the simplest way to isolate every Quikly-sourced contact.
* `quikly_campaign_id` — the specific campaign that captured the signup.
* `quikly_campaign_name` — the human-readable campaign name, handy when you’re building segments by hand.
The subscription itself is also tagged with a **source** of `Quikly` in Klaviyo.
To build a segment of Quikly subscribers:
1. In Klaviyo, go to **Lists & Segments** and click **Create New Segment**.
2. Add a condition on **Properties about someone** → `quikly_source` equals `quikly` (or filter by `quikly_campaign_name` for a specific campaign).
3. Save, and you have a living segment you can target with campaigns or use to trigger Quikly-specific flows.
This lets you, for example, send a different welcome series to people who came in through a high-urgency capped offer than to your general newsletter signups.
***
## 4. The flows that matter most — and where urgency belongs
[Section titled “4. The flows that matter most — and where urgency belongs”](#4-the-flows-that-matter-most--and-where-urgency-belongs)
These are the four Klaviyo flows worth optimizing for Quikly subscribers. The Klaviyo-side mechanics are standard; what’s different is *where you let urgency do the work.*
### Welcome flow
[Section titled “Welcome flow”](#welcome-flow)
Your first touch after someone joins. Engagement is highest here, so it usually drives the most revenue of any flow. If your Quikly campaign delivered a code with an expiration window, your first welcome email should fire **immediately** and carry that code so the shopper can redeem before it lapses. Quikly generates and manages standard Shopify discount codes for you, so there’s nothing to set up manually in the Shopify admin — see [how codes are generated and delivered](/shopify/faq/#how-are-discount-codes-generated-and-delivered).
If the urgency window has already closed by the time you’re following up, switch the message: acknowledge they missed the flash offer and point them at a fresh Quikly campaign rather than quietly handing out the same discount with no time pressure. The scarcity is the reason to act now.
### Browse abandonment flow
[Section titled “Browse abandonment flow”](#browse-abandonment-flow)
Triggers when someone views products but doesn’t add to cart. Instead of bolting on a bigger discount to win them back, link them to a capped Quikly offer. The message becomes “the deal you were eyeing is going fast” — urgency, not a markdown, closes the gap.
### Cart abandonment flow
[Section titled “Cart abandonment flow”](#cart-abandonment-flow)
The highest-leverage flow for urgency, and the one we dig into next. These shoppers were one step from buying; a time-sensitive, decaying offer is far more persuasive than a flat “10% off, come back anytime.”
### Post-purchase flow
[Section titled “Post-purchase flow”](#post-purchase-flow)
Nurture the relationship after the sale — reviews, recommendations, and the next reason to come back. A capped Quikly offer for repeat buyers (“first 100 returning customers”) gives loyal shoppers a reason to act now instead of waiting for your next sitewide sale.
***
## 5. The cart abandonment play: an offer that decays as they wait
[Section titled “5. The cart abandonment play: an offer that decays as they wait”](#5-the-cart-abandonment-play-an-offer-that-decays-as-they-wait)
This is the second mechanic that lets you drive sales *without* a deeper discount — by making waiting cost the shopper something.
A typical Klaviyo cart abandonment flow sends a reminder, then a bigger discount a day later if they still haven’t bought. That trains shoppers to *wait*: they learn that hesitating earns them a better deal. You’re paying more to close the same sale.
Quikly inverts that. You point the cart-reminder email at a Quikly activation whose offer **tiers down over time** — the best discount goes to the fastest buyers, then it steps to a lower tier, and a live countdown shows where things stand:
* First to act: the strongest offer (e.g. $20 off).
* Wait a while: it steps down to the next tier (e.g. $15 off).
* Keep waiting: the lower tier, or it closes out entirely.
Now hesitation is expensive instead of rewarded. The shopper has a concrete reason to come back and check out *now*, and you never had to widen your discount to create that pressure — the structure does it. Because the campaign is gated to an activation link, you can keep this offer exclusive to the abandoning-cart segment and measure it cleanly. (More on link-gated audiences in the [FAQ](/shopify/faq/#can-i-target-only-low-intent-users).)
A note on how this fits together: Klaviyo owns the *trigger and the send* (the cart abandonment flow, timing, and email content), and Quikly owns the *offer behind the link* (the tiers, the cap, the countdown, and the code). You build the flow once in Klaviyo and let the Quikly campaign supply the urgency.
***
## 6. Measure what actually moved
[Section titled “6. Measure what actually moved”](#6-measure-what-actually-moved)
The reason to run urgency over a deeper discount is incremental revenue — so measure that, not opens and clicks.
Quikly’s A/B testing is built in and reports on **revenue lift**, which lets you put a capped or decaying Quikly offer head-to-head against your current flat-discount baseline and see which one actually produces more revenue per send. Run a tiered Quikly offer against your existing “10% off” cart reminder for a couple of weeks and let the revenue numbers settle the question. See the [Overview](/shopify/overview/) for how lift reporting works.
***
## In short
[Section titled “In short”](#in-short)
You don’t have to choose between a bigger list and protecting your margin. Quikly’s urgency mechanics — caps, tiers, countdowns, and link-gated offers — give you a second lever beyond discount size:
* A **capped popup** captures more emails at the discount you already offer.
* A **decaying cart-reminder offer** drives faster checkouts without training shoppers to wait for a better deal.
* Klaviyo handles the segments, flows, and sends; Quikly supplies the urgency behind the link.
Ready to wire it up? Start with the [Klaviyo integration guide](/integrations/klaviyo/), then browse [Campaign Ideas](/shopify/campaign-ideas/) for offer structures that match your goal this week.
# What's New: Redesigned Brand Admin & Smarter Marketing Insights
This release focuses on the surfaces you spend the most time in — your brand admin and your Insights views — and adds a long-requested ability to pause an active campaign without unpublishing it.
## Brand Admin Redesign
[Section titled “Brand Admin Redesign”](#brand-admin-redesign)
The brand admin has been rebuilt on top of a new design system, with tighter spacing, clearer typography, and a more consistent visual language across screens.
* **Campaign Detail**, **Results**, and **Discount Analytics** have all been reorganized so the most important information is front and center, with secondary controls tucked into expandable cards.
* The sidebar nav has been cleaned up and the old **Dashboard** is now simply called **Home**.
* A new **Traffic Sources** table has been added to your A/B test results, so you can see which channels drove each variant.
* The Top Discounts table now includes **HoverCard previews** — hover any discount to see its full details without leaving the page.
* A consistent empty-state component now appears across all Results cards, so it’s always clear when there’s no data versus when something is loading.
## Marketing Insights Upgrades
[Section titled “Marketing Insights Upgrades”](#marketing-insights-upgrades)
Marketing Insights has gained a few features that make it much easier to compare what’s happening across your channels and across time.
* **Calendar view on the Email tab** — see your sends laid out by date, with psychology counts reframed as a percentage of total messages rather than raw counts.
* **SMS Insights tab** — a brand new tab with the same layout as Email, so cross-channel comparisons are one click away.
* **Window-scoped stats** — the stat cards at the top of Insights now respect the date range you’ve selected, so filtered views and whole-account totals no longer get mixed together.
* Behind the scenes, merchant matching across ESPs, marketing platforms, and shared domains is more accurate, which means your insights are more reliably attributed to the right brand.
## Pause Active Campaigns
[Section titled “Pause Active Campaigns”](#pause-active-campaigns)
You can now **pause** any active campaign directly from brand admin. Pausing stops the campaign from running immediately — no claims, no opt-ins, no banner — but preserves its configuration, schedule, and audience exactly as you set them. When you’re ready, hit resume and the campaign picks up right where it left off.
This is especially useful if you spot something mid-flight that you want to fix, or if you need to temporarily back off a campaign during a sale or other event without going through the unpublish-and-republish cycle.
***
All of these are available now in your brand admin. As always, reach out to your account team with questions or feedback.
# What's New: Faster Reports, Smoother Campaigns & Smarter Recommendations
Here’s a look at the latest features and improvements we’ve shipped to help you launch better campaigns, understand your results faster, and deliver smoother experiences to your shoppers.
## Instant Campaign Reports
[Section titled “Instant Campaign Reports”](#instant-campaign-reports)
Campaign reports are now significantly faster. We’ve overhauled our reporting infrastructure so that results load in seconds instead of minutes — including AI-generated performance summaries that highlight what’s working and where there’s room to improve.
If you manage multiple Shopify stores under one brand, you’ll also notice a new **consolidated report view** that rolls up results across all your stores into a single dashboard. No more switching between merchants to piece together the full picture.
## Smarter Campaign Recommendations
[Section titled “Smarter Campaign Recommendations”](#smarter-campaign-recommendations)
Our AI-powered campaign recommendations are now more accurate. We’ve improved how we calculate discount levels relative to your store’s average order value, refined how offer budgets scale across campaign weeks, and added better handling for stores with limited order history.
The result: the campaigns we suggest are closer to what you’d actually want to run, so you spend less time tweaking and more time selling.
## Teaser Animations & New Display Formats
[Section titled “Teaser Animations & New Display Formats”](#teaser-animations--new-display-formats)
Campaign teasers now slide in and out smoothly instead of appearing abruptly, creating a more polished experience for your shoppers. We’ve also added new display format options — you can now choose between a **Card** or **Circle** teaser style for hotspot campaigns, giving you more flexibility to match your site’s look and feel.
You can also now add a **dismiss button** to teasers, letting shoppers close the teaser if they’re not interested. This is fully configurable — you decide whether to show it or not.
## Better Mobile Experience
[Section titled “Better Mobile Experience”](#better-mobile-experience)
We’ve fixed several layout issues that could cause campaigns to display incorrectly on mobile devices, including sizing and layering problems with sticky elements. Your campaigns should now look great on every screen size.
## Brand Dashboard Improvements
[Section titled “Brand Dashboard Improvements”](#brand-dashboard-improvements)
Finding your campaigns just got easier. We’ve added a **search bar** to the brand dashboard header so you can quickly jump to any campaign by name. We’ve also refreshed the navigation with branded avatars and improved the overall look and feel of the admin experience.
## Postscript Compatibility
[Section titled “Postscript Compatibility”](#postscript-compatibility)
If you use Postscript for SMS marketing, Quikly now automatically hides the Postscript popup widget when your campaign is active — just like we already do for Attentive, Klaviyo, and Alia. This prevents overlapping popups and keeps your storefront clean for shoppers.
***
These features are available now in your Quikly dashboard. Have questions or want help setting up your next campaign? Reach out to your account team — we’d love to hear from you.
# What's New: Campaign Calendar, Smart Recommendations & More
We’ve been hard at work making Quikly more powerful and easier to use. Here’s a roundup of the latest features and improvements now available in your dashboard.
## Campaign Calendar View
[Section titled “Campaign Calendar View”](#campaign-calendar-view)
Planning your promotional calendar just got a lot easier. You can now toggle between a **list view** and a **calendar view** of your campaigns, giving you a bird’s-eye view of what’s running, what’s coming up, and where you have gaps in your promotional schedule.
Campaigns are color-coded by status so you can quickly see what’s live, scheduled, or completed at a glance.
## Smart Campaign Recommendations
[Section titled “Smart Campaign Recommendations”](#smart-campaign-recommendations)
Not sure what campaign to run next? Quikly now automatically generates **personalized campaign recommendations** based on your store’s data and goals. When you install the app, you’ll see suggested campaigns ready to customize and launch—no more starting from scratch.
These recommendations update over time as we learn more about what works for your business.
## Brand-Wide Style Settings
[Section titled “Brand-Wide Style Settings”](#brand-wide-style-settings)
Tired of setting fonts and colors for every campaign? You can now configure **brand-level style settings** that automatically apply to all your campaigns. Set your brand fonts once and they’ll be available across all your offers, banners, and claim pages.
This is especially helpful for teams managing multiple campaigns—your brand stays consistent without the extra work.
## Brand Asset Library
[Section titled “Brand Asset Library”](#brand-asset-library)
We’ve added a centralized **asset library** where you can upload and manage images used across your campaigns. No more re-uploading the same logo or product image for each campaign. Upload once, use everywhere.
## Product Page Targeting
[Section titled “Product Page Targeting”](#product-page-targeting)
You now have more control over where your offers appear with **visibility rules based on product page handles**. Want to show a specific offer only on certain product pages? Or hide an offer from a particular collection? Now you can.
This gives you the flexibility to run targeted promotions without showing irrelevant offers to shoppers.
## Improved Campaign Reporting
[Section titled “Improved Campaign Reporting”](#improved-campaign-reporting)
We’ve made several improvements to help you understand campaign performance:
* **Lift calculations** now show the incremental impact of your campaigns on net sales and total sales
* **Aggregated reports** make it easier to see results across multiple campaigns
* Reports are now **auto-generated** when campaigns complete, so you don’t have to remember to pull them
***
These features are available now in your Quikly dashboard. Have questions or feedback? Reach out to your account team—we’d love to hear from you.
# What's New: A/B Testing, Visual Previews & Smarter Discount Controls
Here’s a look at the latest features and improvements we’ve shipped to help you make more confident campaign decisions, move faster from idea to launch, and give your shoppers more precisely targeted offers.
## A/B Campaign Testing
[Section titled “A/B Campaign Testing”](#ab-campaign-testing)
You can now run head-to-head tests between two Quikly campaigns and see exactly which one drives better results. Link any two campaigns as a split test, and Quikly will automatically track which visitors saw each campaign and what they purchased — then surface a clear summary of lift, conversion, and revenue for each variant.
This makes it easy to answer questions like “does a tiered offer outperform a flat discount for my store?” with real data instead of guesswork. Split test results are available directly in your brand dashboard alongside your other campaign reports.
## Visual Previews for Campaign Recommendations
[Section titled “Visual Previews for Campaign Recommendations”](#visual-previews-for-campaign-recommendations)
When Quikly recommends a campaign for your store, you’ll now see a **live visual preview** of what that campaign will look like on your site — before you commit to building it. Previews reflect your brand colors, fonts, and offer details so you can quickly evaluate whether a recommendation fits your goals.
You can also expand any recommendation card to see the full breakdown: tier structure, target audience, offer sizing, and the reasoning behind the suggestion. And if a recommendation isn’t quite right, you can now give it a thumbs up or down — that feedback goes directly back into the system to improve future suggestions for your store.
## Subscription-Only Discounts
[Section titled “Subscription-Only Discounts”](#subscription-only-discounts)
Quikly campaigns can now generate discount codes that apply **only to subscription orders**, leaving one-time purchases unaffected. This is especially useful if you want to reward or accelerate subscriber growth without discounting your full catalog.
When configuring your campaign offers, you’ll find a new option in the Incentives section to restrict each discount to subscriptions. You can mix and match — some tiers subscription-only, others available to everyone — giving you precise control over how your offers are structured.
## Shopify Marketing Contacts Integration
[Section titled “Shopify Marketing Contacts Integration”](#shopify-marketing-contacts-integration)
Quikly now syncs opt-ins directly with **Shopify’s built-in marketing contacts**, so customers who engage with your campaigns are automatically added to your Shopify marketing list. This keeps your Shopify subscriber data accurate and up to date without any manual exports or third-party sync steps.
## Auto-Detect Your Brand Fonts
[Section titled “Auto-Detect Your Brand Fonts”](#auto-detect-your-brand-fonts)
Setting up your brand in Quikly just got faster. You can now click **Detect Fonts** and Quikly will scan your live Shopify storefront, identify the fonts you’re using, and import them directly into your brand library — including Google Fonts and system fonts. No more hunting for font names or manually entering them one by one.
## Redesigned Campaign Overview
[Section titled “Redesigned Campaign Overview”](#redesigned-campaign-overview)
The campaign detail page has a new layout that puts the most important controls front and center. Timing, visibility settings, and offer tiers are each organized into their own cards with inline editing, so you can make quick adjustments without leaving the page. Publishing and unpublishing now include a confirmation step to prevent accidental changes.
## Campaign List Search & Sorting
[Section titled “Campaign List Search & Sorting”](#campaign-list-search--sorting)
Finding a specific campaign in a long list is now much easier. The campaign list supports **live search by name**, sortable columns for start date, end date, and status, and proper pagination for larger accounts. If you manage a catalog of 50 or more campaigns, you’ll notice the difference immediately.
## Repeat Visitor Controls
[Section titled “Repeat Visitor Controls”](#repeat-visitor-controls)
You can now configure a campaign to **hide itself from shoppers who have already seen another Quikly campaign** on your store. This is useful for keeping your experience fresh — new visitors get the full campaign, while repeat visitors who’ve already engaged aren’t shown the same treatment again.
***
All of these features are available now in your Quikly dashboard. Have questions or want help making the most of any of these updates? Reach out to your account team — we’d love to hear from you.
# What's New: A Smoother Campaign Builder, New Dynamic Tags & Per-Campaign Checkout Controls
A big batch of campaign builder improvements landed over the last few weeks, along with three new dynamic tags and a new toggle for the Shopify checkout banner.
## The Campaign Builder Is Faster and More Reliable
[Section titled “The Campaign Builder Is Faster and More Reliable”](#the-campaign-builder-is-faster-and-more-reliable)
If you spend most of your time inside the builder, you’ll feel these improvements right away.
* **Drag-and-drop fixes** — reorders no longer get lost when you make an edit mid-drag, and moving blocks between sections no longer duplicates them.
* **Deeper nesting** — content blocks can now be nested up to four levels deep, opening up more sophisticated layouts.
* **A more faithful live preview** — section-scoped style settings, teaser width, container hide-on-mobile, and your brand logo now all render correctly inside the preview iframe, so what you see is what your shoppers will see.
* **CodeMirror JSON/YAML editors** — campaign template editing in admin now uses CodeMirror, with proper syntax highlighting, line numbers, and indentation.
* **Split button color** — set the background color and text color of buttons independently, so you can hit your brand and contrast targets without compromise.
* **Responsive layout controls** on Row and Container blocks — configure per-breakpoint sizing and alignment.
* **Per-view vertical alignment** on JSON template sections, so the same template can present differently across teaser, popup, and panel views.
* **Max Width + Alignment** controls on the inline component, for finer control over how it sits inside its container.
* **Animated Icons on Loading screens** — the old “Icon” block is now called “Animated Icon” for clarity, and you can use it on Loading screens too.
## New Dynamic Tags
[Section titled “New Dynamic Tags”](#new-dynamic-tags)
Drop any of these into a rich text field to make text and buttons interactive:
* **`{{CloseLink}}`** — turn any link or button into a popup-dismiss action. Useful for “No thanks” copy and custom close buttons.
* **`{{OverviewLink}}`** — send shoppers back to your offer list from inside a deal view, without leaving the campaign.
* **`{{OpenFeatureLink}}`** — open any feature in your campaign (panel, drawer, etc.) from a rich text link, giving you full control over how shoppers navigate between sections.
## Disable the Shopify Checkout Banner Per Campaign
[Section titled “Disable the Shopify Checkout Banner Per Campaign”](#disable-the-shopify-checkout-banner-per-campaign)
Quikly campaigns can now opt out of the post-purchase Shopify checkout banner on a per-campaign basis. There’s a new toggle in campaign settings — flip it off for any single campaign without affecting others.
This is useful when a campaign already includes its own claim or confirmation experience and you don’t want a second touchpoint at checkout. Most merchants will want the banner on for most campaigns, but the new toggle gives you the precision to make exceptions where they make sense.
***
All of these features are available now. If you have questions about any of them — especially the new dynamic tags, which open up some genuinely new patterns — reach out to your account team.
# CRM Integration
> Custom integrations to submit lead information in real time
Quikly can build custom integrations to submit lead information in real time to your CRM system. This enables seamless data flow between Quikly campaigns and your customer relationship management platform, ensuring that valuable lead data is captured and available for immediate follow-up.
## Key Features
[Section titled “Key Features”](#key-features)
* **Real-time Data Sync**: Lead information is transmitted to your CRM instantly as users engage with Quikly campaigns
* **Custom Field Mapping**: Map Quikly data fields to your specific CRM schema and custom fields
* **Bi-directional Communication**: Support for both sending data to and retrieving information from your CRM
* **Event-based Triggers**: Automatically send data based on specific user actions or campaign milestones
## Supported CRMs
[Section titled “Supported CRMs”](#supported-crms)
Quikly has experience building integrations with major CRM platforms including:
* Salesforce
* HubSpot
* Marketo
* Custom/Proprietary CRM systems
## Integration Methods
[Section titled “Integration Methods”](#integration-methods)
### API Integration
[Section titled “API Integration”](#api-integration)
Direct API-to-API integration provides the most flexibility and real-time capabilities. We work with your CRM’s REST or SOAP APIs to establish secure, authenticated connections.
### Webhook Integration
[Section titled “Webhook Integration”](#webhook-integration)
For CRMs that support webhooks, we can push lead data to your specified endpoints whenever relevant events occur within Quikly campaigns.
### Batch Processing
[Section titled “Batch Processing”](#batch-processing)
For high-volume scenarios or systems with rate limits, we can implement batch processing to efficiently transfer lead data at scheduled intervals.
## Data Types
[Section titled “Data Types”](#data-types)
Common data points that can be integrated include:
* Contact information (email, phone, name)
* Timestamp data for all interactions
## Getting Started
[Section titled “Getting Started”](#getting-started)
To begin setting up a CRM integration:
1. Contact your Quikly account manager to discuss your integration requirements
2. Provide CRM API documentation and authentication credentials
3. Define field mapping requirements
4. Test the integration in a staging environment
5. Deploy to production with monitoring
## Security & Compliance
[Section titled “Security & Compliance”](#security--compliance)
All CRM integrations are built with security best practices:
* Encrypted data transmission (TLS 1.2+)
* Secure credential storage
* GDPR and CCPA compliance support
* Audit logging for all data transfers
* Rate limiting and retry logic for reliability
Contact your Quikly representative to learn more about building a custom CRM integration for your organization.
# Quikly Drop
Quikly Drop campaigns feature a finite amount of rewards and/or offers awarded by rank, elapsed time, or randomly.
Drop campaigns are made up of a main screens with three different stats, and one additional component that displays offer details after claiming.
## Overview
[Section titled “Overview”](#overview)
The basic concept for a Drop campaign is to send a viewer’s email address to attempt to claim an offer. If successful, the API returns a unique `receiptToken` that can be used to fetch offer details, as well as an `authToken` for the user to be used in subsequent API calls to provide authenticated access on that user’s behalf.
Additional fields in the API provide CMS-like data that is helpful when rendering a campaign, including headings, images, fine print, and offer details.
## Offer Screen
[Section titled “Offer Screen”](#offer-screen)
Use the following query to fetch the content for the initial screen of the campaign. In this way the API acts as a CMS so that a campaign manager can update the promotion titles, fine print, and images from the Quikly admin interface.

```graphql
query promoLandingQuery($dealHashid: String!) {
quikly(hashid: $dealHashid) {
id
name
description
header: customContentFor(key: "embedded_instant_sign_in_header")
finePrint
buttonText: customContentFor(key: "embedded_instant_sign_in_button_text")
image {
url(variant: "original")
}
}
```
The `quikly` type refers to the campaign instance.
## Claiming an Offer
[Section titled “Claiming an Offer”](#claiming-an-offer)
To claim an offer, send the user’s email address to the `createEmbeddedClaim` mutation. This returns a unique offer identifer (`qid`) and `receiptToken` used to securely fetch offer details, as well as the user’s `authToken` which can then be used to identify this user on subsequent interactions with the API via the [authorization header](/graphql-api).
```graphql
mutation createEmbeddedClaim(
$dealHashid: String!
$email: String!
) {
createEmbeddedClaim(
input: { dealId: $dealHashid, email: $email }
) {
errors {
key
message
}
user {
id
authToken
}
order {
id
qid
receiptToken
}
}
}
```
You should store the order’s `qid`, `receiptToken` and the user’s `authToken` locally.
## Fetching Offer Detail
[Section titled “Fetching Offer Detail”](#fetching-offer-detail)
Use the `getOrder` query to fetch offer details for a user once they have successfully claimed.

```graphql
query getOrder($orderId: Int!, $receiptToken: String!, $dealTierId: Int) {
order(
orderId: $orderId
receiptToken: $receiptToken
dealTierId: $dealTierId
) {
id
qid
expirationDate
itemFullDescription
itemInstructions
finePrint
codes: codesWithLabelsAndBarcodes {
instore
pin
value
label
}
links {
id
name
url
position
}
quikly {
id
hashid
termsUrl
congratsMessage: customContentFor(key: "claim_congrats")
congratsHeader: customContentFor(key: "claim_congrats_embedded_header")
congratsSubheader: customContentFor(
key: "claim_congrats_embedded_subheader"
)
redeemCopy: customContentFor(key: "embed_redeem_body")
}
}
}
```
This example shows how you can fetch content from the Quikly campaign (such as a congrats message or campaign heading) as well as data tied to the individual’s offer: one or more offer codes, an expiration date, associated fine print or redemption urls, and more. At this point, referencing the `OrderType` in the [GraphiQL Explorer](https://graphql-explorer.quiklydemo.com) will be very useful!
## Checking Claim Status
[Section titled “Checking Claim Status”](#checking-claim-status)
A quick way to check if a currently authenticated user has already claimed an offer is to use the `alreadyClaimed` field on the `wantIn` type:
```graphql
query checkAlreadyClaimed($dealHashid: String!) {
quikly(hashid: $dealHashid) {
id
wantIn {
id
alreadyClaimed
}
}
}
```
The `wantIn` type represents the viewer’s opt-in to a campaign (stemming from the phrase “I Want In”). It ties a `user` to a `quikly`.
# Quikly Swap
Quikly Swap campaigns incentivize a specific action in exchange for an offer. You can easily layer in urgency or scarcity by limiting rewards, or configuring offers that decrease in value over time. For example, the marketing message could be:
\*\* Sign up in the next 24 hours to receive $10 off your order, or in the next 48 hours for $5 off your next order. \*\*
Or, alternatively:
\*\* The first 500 people to sign up will receive a code good for 20% off. \*\*
## Overview
[Section titled “Overview”](#overview)
Swap campaigns are generally comprised of three visual components:
* **instructions banner:** conveys real-time offer detail (current offer eligilbilty, quantity or time left, etc)
* **offer presentation:** offer codes, fine print, terms
* **redemption banner:** display any previously claimed offers, useful during checkout
In terms of the API, the basic concept for a Swap campaign is to 1) query for offer details, 2) trigger an event that indicates the targeted action was completed, and 3) query for claimed offer details.
Campaigns can be configured to be visible to all users, or only those that have obtained a unique access key via an activation URL.
## Instructions Banner
[Section titled “Instructions Banner”](#instructions-banner)
Use the following query to fetch dynamic content useful to explain what action is needed to receive the offer, as well as what offers are available.

```graphql
query InstructionsQuery($hashid: String!) {
quikly(hashid: $hashid) {
id
instructionsStatic: customContentFor(key: "embed_instructions_static")
instructionsUpcoming: customContentFor(key: "embed_instructions_upcoming_offer")
instructionsExpiring: customContentFor(
key: "embed_instructions_expiring_offer"
)
instructionsLast: customContentFor(key: "embed_instructions_last_offer")
dealClosedTitle: customContentFor(key: "deal_closed")
dealClosedSubtitle: customContentFor(key: "deal_closed_subtitle")
ended
claimBySpeed
incentiveTiers {
id
rank
description
upperResponseTime
quantity
claimCount
allClaimed
}
}
}
```
The fields include:
* `quikly`refers to the campaign instance
* `customContentFor` loads text content from the CMS
* `ended` is a boolean value indicating if the campaign has expired
* `claimBySpeed` is a boolean value indicating if the offer value is determined by the speed of response rather than rank
* `incentiveTiers` provides info about the offer types available
## Claiming an Offer
[Section titled “Claiming an Offer”](#claiming-an-offer)
To claim an offer, send the user’s email address to the `createEmbeddedClaim` mutation. This returns a unique offer identifier (`qid`) and `receiptToken` used to securely fetch offer details, as well as the user’s `authToken` which can then be used to identify this user on subsequent interactions with the API via the [authorization header](/graphql-api).
```graphql
mutation createEmbeddedClaim(
$dealHashid: String!
$email: String!
) {
createEmbeddedClaim(
input: { dealId: $dealHashid, email: $email }
) {
errors {
key
message
}
user {
id
authToken
}
order {
id
qid
receiptToken
}
}
}
```
You should store the `qid`, `receiptToken` from the `order` object locally to retrieve the offer detail.
## Fetching Offer Detail
[Section titled “Fetching Offer Detail”](#fetching-offer-detail)
Use the `getOrder` query to fetch offer details. At a minimum you’ll probably want top display the `itemFullDescription` the `codes`, but the API also provides access to other useful content as demonstrated in the Offer Detail screen below.

```graphql
query getOrder($orderId: Int!, $receiptToken: String!) {
order(
orderId: $orderId
receiptToken: $receiptToken
) {
id
qid
expirationDate
itemFullDescription
itemInstructions
finePrint
codes: codesWithLabelsAndBarcodes {
instore
pin
value
label
}
links {
id
name
url
position
}
quikly {
id
hashid
termsUrl
congratsMessage: customContentFor(key: "claim_congrats")
congratsHeader: customContentFor(key: "claim_congrats_embedded_header")
congratsSubheader: customContentFor(
key: "claim_congrats_embedded_subheader"
)
redeemCopy: customContentFor(key: "embed_redeem_body")
}
}
}
```
If you wanted to simply display a banner like the image below, you may end up building a query like this:

```graphql
query getOrder($orderId: Int!, $receiptToken: String!) {
order(
orderId: $orderId
receiptToken: $receiptToken
) {
id
qid
redeemCopy
codes: codesWithLabelsAndBarcodes {
pin
value
}
}
}
```
At this point, referencing the `OrderType` in the [GraphiQL Explorer](https://graphql-explorer.quiklydemo.com) will be very useful!
# Explorer
# GraphiQL Explorer
[Section titled “GraphiQL Explorer”](#graphiql-explorer)
Visit the Quikly GraphiQL API explorer for a graphical interactive in-browser GraphQL IDE. It features syntax highlighting, intelligent type ahead of fields, arguments, types and more.
[Quikly GraphQL API Explorer](https://graphql-explorer.quiklydemo.com)
# GraphQL API
The Quikly GraphQL API lets you build apps and other integrations using Quikly’s urgency marketing platform. With the API, you can create unique experiences for your audiences natively within your app, or more tightly integrated with your website than the Javascript SDK may allow. This is the same API that powers Quikly’s campaign microsites and embedded experiences.
## Authentication
[Section titled “Authentication”](#authentication)
The GraphQL API requires a Quikly access token for making authenticated requests. Include the access token as a `X-Quikly-Access-Token` header in your requests. Independent of this header, some API calls may require a `Bearer` authorization token that uniquely identifies a user accessing the API (whereas the access token identifies your app).
## Endpoint and Queries
[Section titled “Endpoint and Queries”](#endpoint-and-queries)
All queries are run by sending a POST HTTP request to a single GraphQL endpoint:
```plaintext
POST https://api.quikly.com/graphql
```
In GraphQL, queries are the the equivalent of REST’s GET actions, but they allow you to fetch exactly the data you need. Any requests made to the GraphQL endpoint will be a POST request regardless if you are retrieving or modifying data.
## Example Query
[Section titled “Example Query”](#example-query)
The following example shows a query for fetching basic details about a Quikly campaign, include the name, description, and customized labels used at various spots in the UI.
```graphql
query promoLandingQuery() {
quikly(hashid: "XqyhklE") {
id
name
description
header: customContentFor(key: "embedded_instant_sign_in_header")
finePrint
buttonText: customContentFor(key: "embedded_instant_sign_in_button_text")
image {
url(variant: "original")
}
}
}
```
The response will be a json structure with a root-level “data” key and then data that mirrors what was requested in the query:
```json
{
"data": {
"quikly": {
"id": "RGVhbC04Mzc3",
"name": "Flash Promo Demo",
"description": "",
"header": "Sign into your account to get this offer.",
"cookieNotRequired": true,
"finePrint": "",
"buttonText": "Sign in",
"signInPixel": null,
"image": {
"url": "/assets/missing.png"
}
}
}
}
```
Visit the official [GraphQL site](https://graphql.org/learn/queries/) to learn more about queries.
## Exploring the API
[Section titled “Exploring the API”](#exploring-the-api)
While specific GraphQL client libraries exist, you can access the GraphQL API from any programming language or framework that supports HTTP requests and JSON. While you are learning the API, a good way to test out queries and explore the schema is to the Quikly GraphQL API, but you can also use curl, a tool like [Postman](https://postman.org), or the language of your choice.
* [Use the GraphiQL App](#usethegraphiqlapp)
* [Use curl](#usecurl)
### Use the GraphiQL App
[Section titled “Use the GraphiQL App”](#use-the-graphiql-app)
Visit the [GraphiQL Explorer](https://graphql-explorer.quiklydemo.com) to explore the schema and test out your queries. The GraphiQL Explorer allows you to authenticate, paste in your queries, supports autocompletion and syntax highlighting, and provides a detailed schema reference.
### Use curl
[Section titled “Use curl”](#use-curl)
Here is an example of loading data you might want to display on the first screen of a Quikly Drop. You could extend this example to work from any language that is capable of making HTTP requests. Note the POST body must be a valid JSON with a “query” key that contains the actual GraphQL query.
```bash
curl -X POST https://api.quikly.com/graphql \
-H 'Content-Type: application/json' \
-H 'Accept: application/json; charset=utf-8' \
-H 'X-Quikly-Access-Token: {account-token}' \
-d @- << 'EOF'
{
"query": "{
quikly(hashid: \"WLYhqa\") {
id
name
description
header: customContentFor(key: \"embedded_instant_sign_in_header\")
cookieNotRequired
finePrint
buttonText: customContentFor(key: \"embedded_instant_sign_in_button_text\")
signInPixel: getPixel(placement: \"embedded-sign-in-js\") {
content
repeatable
}
image {
url(variant: \"original\")
}
}
}"
}
EOF
```
If you were making this request via Javascript, you could build your request body with something like this:
```javascript
const accountToken = '';
const options = {
method: 'POST',
headers: {Accept: 'application/json; charset=utf-8', 'Content-Type': 'application/json', 'X-Quikly-Access-Token': accountToken},
body: JSON.stringify({
query: `
query promoLandingQuery {
quikly(hashid: "XqyhklE") {
id
name
}
}
`
})
};
fetch('https://api.quikly.com/graphql', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
```
# Mutations
[Section titled “Mutations”](#mutations)
In GraphQL, mutations are like any of the data-modifying REST verbs (PUT, POST, DELETE) that modify data on the server, but you can also specify the fields contained in (or the “shape of”) the response. This example shows a mutation that attempts to claim a Quikly offer. This example makes use of [variables](https://graphql.org/learn/queries/#variables), but you could also include the values directly in the mutation. If you do use variables, you pass them in a separate (usually JSON) variables dictionary.
```graphql
mutation createEmbeddedClaim(
$dealHashid: String!
$email: String!
) {
createEmbeddedClaim(
input: { dealId: $dealHashid, email: $email }
) {
errors {
key
message
}
user {
id
authToken
}
order {
id
qid
receiptToken
}
}
}
```
With curl:
```bash
curl -X POST \
http://api.quikly.com/graphql \
-H 'Content-Type: application/json' \
-H 'Accept: application/json; charset=utf-8' \
-H 'X-Quikly-Access-Token: {account-token}' \
-d @- << 'EOF'
{
"query": "mutation createEmbeddedClaim(
$dealHashid: String!,
$email: String!
) {
createEmbeddedClaim(
input: { dealId: \$dealHashid, email: \$email }
) {
errors {
key
message
}
user {
id
authToken
}
order {
id
qid
receiptToken
}
}
}",
"variables": "{
\"dealHashid\": \"WLYhqa\",
\"email\": \"scott@quikly.com\"
}"
}
EOF
```
Visit the official [graphql.org](https://graphql.org/learn/queries/#mutations) site for more background on mutations.
We have built a collection of queries and mutations common to specific Quikly campaign types. Check out the [GraphQL Examples](/graphql-api/examples).
# Klaviyo
> Connect your Quikly account with Klaviyo using OAuth
Quikly integrates with Klaviyo to sync email subscribers captured through your Quikly campaigns directly into the Klaviyo list of your choice. Connecting via OAuth is the preferred method — it requires no API keys, no scope configuration, and stays in sync automatically.
## Connecting Klaviyo
[Section titled “Connecting Klaviyo”](#connecting-klaviyo)
1. From your brand dashboard, click **Settings**.
2. Open the **Integrations** tab.
3. Under **Available Integrations**, find **Klaviyo** and click **Connect**.

4. In the modal that appears, select **Connect with Klaviyo**.

5. You’ll be redirected to Klaviyo. Confirm you’re signed in to the correct Klaviyo account, then click **Allow** to grant Quikly access.

6. After granting access, you’ll be returned to Quikly and prompted to choose the list that new email contacts should be added to. Select your list and click **Save**.

That’s it — new signups captured by your Quikly campaigns will flow into the selected Klaviyo list.
## Recommended: Enable Single Opt-In
[Section titled “Recommended: Enable Single Opt-In”](#recommended-enable-single-opt-in)
We recommend configuring your Klaviyo list for single opt-in so subscribers are added immediately without needing to confirm via a follow-up email.
1. In Klaviyo, go to **Lists & Segments** and open the list connected to Quikly.
2. Click **Settings**, then **Opt-in Process**.
3. Set the list to **Single opt-in**.
For more details, see [Klaviyo’s guide to the opt-in process](https://help.klaviyo.com/hc/en-us/articles/115005251108).
## Synced Properties
[Section titled “Synced Properties”](#synced-properties)
When a subscriber is captured by a Quikly campaign and synced to Klaviyo, the following custom properties are written to their Klaviyo profile:
* `quikly_source` — always set to `quikly`, useful for identifying Quikly-sourced contacts
* `quikly_campaign_id` — the ID of the Quikly campaign that captured the signup
* `quikly_campaign_name` — the name of the Quikly campaign that captured the signup
The subscription itself is tagged with a **source** of `Quikly` in Klaviyo, which you can use to build segments or trigger welcome flows specific to Quikly signups.
## Switching Lists or Disconnecting
[Section titled “Switching Lists or Disconnecting”](#switching-lists-or-disconnecting)
To change which list contacts are synced to, return to **Settings → Integrations** and reconfigure the Klaviyo integration. You can disconnect at any time from the same screen, or by revoking Quikly’s access from within your Klaviyo account settings.
## Looking for the legacy API key setup?
[Section titled “Looking for the legacy API key setup?”](#looking-for-the-legacy-api-key-setup)
If you previously connected Klaviyo using a private API key, see [Klaviyo (Legacy API Key)](/integrations/klaviyo-legacy/). We recommend migrating to the OAuth flow above.
# Klaviyo (Legacy API Key)
> Connect your Quikly account with Klaviyo using a private API key
Note
This page documents the legacy API key flow. We recommend using the [OAuth integration](/integrations/klaviyo/) instead.
Quikly integrates with Klaviyo to sync your campaign data. Follow these steps to connect your accounts.
## Connecting Klaviyo
[Section titled “Connecting Klaviyo”](#connecting-klaviyo)
1. From your Quikly dashboard, click **Settings**
2. Under **Available Integrations**, find **Klaviyo** and click the blue **Connect** button

3. In the popup window, enter your **API Key** and **List Name**
4. Click **Save Integration**
## Getting Your API Key
[Section titled “Getting Your API Key”](#getting-your-api-key)
You’ll need a private API key from Klaviyo. Only Owners and Admins can create API keys.
1. Go to [Klaviyo API Keys Settings](https://www.klaviyo.com/settings/account/api-keys)
2. Click **Create Private API Key**
3. Give the key a descriptive name (e.g., “Quikly Integration”)
4. Select **Custom** access level and enable the required scopes (see below)
5. Click **Create**
### Required API Scopes
[Section titled “Required API Scopes”](#required-api-scopes)
When creating your API key, select **Custom** and enable the following scopes:
* `profiles:read` — read profile data and list relationships
* `profiles:write` — create and update profiles
* `subscriptions:write` — subscribe profiles to lists
* `lists:read` — check list membership
* `lists:write` — add profiles to a list
[Your browser does not support the video tag.](/integrations/klaviyo/create_klaviyo_private_key.mp4)
**Important:** Copy and save your API key immediately after creation. You won’t be able to view it again. Store it securely in a password manager.
For detailed instructions, see [Klaviyo’s guide on creating private API keys](https://help.klaviyo.com/hc/en-us/articles/7423954176283).
## Finding Your List ID
[Section titled “Finding Your List ID”](#finding-your-list-id)
The List Name field in Quikly requires your Klaviyo list ID, which is the alphanumeric code found in the list URL.
To find it:
1. In Klaviyo, navigate to **Lists & Segments**
2. Select the list you want to connect
3. Open the **Settings** tab
4. The list ID appears near the top of the page under **List Details**

Alternatively, look at the URL in your browser when viewing the list. The list ID is the short alphanumeric string in the URL (e.g., `https://www.klaviyo.com/list/ABC123/...` — the list ID is `ABC123`).
For more details, see [Klaviyo’s guide on finding a list ID](https://help.klaviyo.com/hc/en-us/articles/115005078647).
# Quikly Drop
> Quikly Drop allows you to drop rewards to instantly surprise and delight your audience.
[Quikly Drop](/activations/drop) allows you to “drop” rewards to instantly surprise and delight your audience.
This activation type can show realtime content including number of offers claimed, the number remaining, the current offer available, or time remaining before the event is over.

## Code
[Section titled “Code”](#code)
```html
```
## Live Demo
[Section titled “Live Demo”](#live-demo)
Click to load the demo
# Quikly Hype
> Quikly Hype allows you to build hype, anticipation and engagement over days, as consumers opt in for a chance to claim exciting rewards.
[Quikly Hype](/activations/hype) allows you to build hype, anticipation and engagement over days, as consumers opt in for a chance to claim exciting rewards.

## Code
[Section titled “Code”](#code)
```javascript
```
# Live Demo
[Section titled “Live Demo”](#live-demo)
Click to reveal the Quikly campaign
# Quikly Offer Display
> Quikly Offer Display allows you to track dynamic offer codes provided in an activation link for display in custom UI widgets throughout your site.
You can use the Quikly tag along with special activation links to assign dynamic offer codes to individuals using a unique URL that can then be displayed throughout your site.
## Step 1: Activation Links
[Section titled “Step 1: Activation Links”](#step-1-activation-links)
Use an activation link provided by Quikly to enable the offer display for that visitor. You can append the offer code to the link with a query parameter `q_code`. You will receive one activation link per offer and landing page.
## Step 2: Ensure Quikly JS tag is present on landing page
[Section titled “Step 2: Ensure Quikly JS tag is present on landing page”](#step-2-ensure-quikly-js-tag-is-present-on-landing-page)
Ensure the Quikly javascript tag is present on any landing page used with an activation link, as the tag will capture offer code from the URL and enable the offer for that visitor. This will not display anything on the page.
```html
```
## Step 3: Displaying Offer Code
[Section titled “Step 3: Displaying Offer Code”](#step-3-displaying-offer-code)
Include the code below anywhere you’d like to display the offer code to those who have successfully activated the offer. The code will not activate unless the viewer previously clicked an activation link.

```html
```
Note: If you already include the base Quikly tag globally on your site, you only have to include the last line (`qData('ui')...`) on any page where you’d like to display the offer code.
# Quikly Components
> Quikly Components allow you to render multiple Quikly components throughout on a single page.
You can use the Quikly tag to display custom components in multiple spots on your site.
# Step 1: Ensure Quikly JS tag is present on landing page
[Section titled “Step 1: Ensure Quikly JS tag is present on landing page”](#step-1-ensure-quikly-js-tag-is-present-on-landing-page)
Ensure the Quikly javascript tag is present on any landing page used with an activation link, as the tag will capture offer code from the URL and enable the offer for that visitor. This will not display anything on the page.
```html
```
# Step 2: Displaying the Component
[Section titled “Step 2: Displaying the Component”](#step-2-displaying-the-component)
Include the code to render your component. The `placements` key should contain an array of component definitions.
```html
```
Reminder: If you already include the base Quikly tag globally on your site, you only have to include the last line (`qData('ui')...`) on any page where you’d like to display the component.
# Example: Bounceback Offer with Live Counter
[Section titled “Example: Bounceback Offer with Live Counter”](#example-bounceback-offer-with-live-counter)
This example demonstrates how to implement a bounceback offer that shows a banner with available offers and allows users to claim rewards post-purchase.
## Implementation
[Section titled “Implementation”](#implementation)
### 1. Initial Setup with Offer Banner
[Section titled “1. Initial Setup with Offer Banner”](#1-initial-setup-with-offer-banner)
Display a banner showing available offers using the `offerTeaser` component:
```html
```
### 2. Trigger Reward Claim on Purchase
[Section titled “2. Trigger Reward Claim on Purchase”](#2-trigger-reward-claim-on-purchase)
When a purchase is completed or reward event occurs, display the reward feature:
```html
```
### Key Features
[Section titled “Key Features”](#key-features)
* **Live Counter**: The `offerTeaser` component automatically displays and updates the number of available offers
* **Reward Claiming**: The `rewardFeature` component handles the reward claim process
* **Email Association**: Pass the user’s email when triggering the reward to associate it with their account
* **Automatic Updates**: The banner counter decreases automatically as rewards are claimed
# Quikly Swap
> Quikly Swap allows you to swap the completion of marketing activities for instant rewards.
# Overview
[Section titled “Overview”](#overview)
[Quikly Swap](/activations/swap) allows you to swap the completion of marketing activities for instant rewards.
## Step 1: Displaying Instructions
[Section titled “Step 1: Displaying Instructions”](#step-1-displaying-instructions)
Quikly can display messaging around an active promotion to visitors who clicked an activation link. Additional parameters can be passed to the “ui” command to control where the message appears.
This code assumes you have already [loaded the SDK](/javascript-sdk/overview) on your site.
```html
```

## Step 2: Tracking and Rewarding the Action
[Section titled “Step 2: Tracking and Rewarding the Action”](#step-2-tracking-and-rewarding-the-action)
Upon completing the action and triggering the event, Quikly can display a modal window with more detail about the awarded offer. In this case, we set up the “reward” page, and as soon as the “loyalty\_signup” event is triggered, the reward UI is revealed.
```html
```

## Step 3: Displaying Redemption Details on Checkout
[Section titled “Step 3: Displaying Redemption Details on Checkout”](#step-3-displaying-redemption-details-on-checkout)
Quikly can surface additional redemption messaging on any page of your site to those who have successfully activated the offer. The code will not activate unless an offer was previously awarded.
```html
```

## Complete Code Sample
[Section titled “Complete Code Sample”](#complete-code-sample)
The following snippet shows the complete code necessary to render the instructions banner on your page.
```html
```
## Live Demo
[Section titled “Live Demo”](#live-demo)
Click to load the instructions banner
# Javascript SDK
> Javascript SDK Overview
Quikly provides a small javascript code snippet that you can place on your website to feature an activation. You can further customize the experience using the `qData` command to render dynamic content, such as limited time promotions, individualized redemption instructions, reward details, and more.
# Activation Links
[Section titled “Activation Links”](#activation-links)
If you’d like to limit participation in a campaign to a specific audience, Quikly can provide activation links that can be used in your marketing efforts to activate the campaign only for those visitors arriving via that link, and to aid in tracking the performance of each channel.
# Setup
[Section titled “Setup”](#setup)
Place the Quikly script tag on any page that:
1. is the target of an activation URL
2. renders a Quikly activation
3. tracks an incentivized action
### Staging Code Sample
[Section titled “Staging Code Sample”](#staging-code-sample)
The commands passed to `qData` determine whether to track an action or to display a UI component. Check out the [Javascript SDK Reference](/javascript-sdk/reference) for a full list of commands.
# Domain Safelist + QA/Staging Environment Access
[Section titled “Domain Safelist + QA/Staging Environment Access”](#domain-safelist--qastaging-environment-access)
All domains that may embed Quikly content must be safelisted by Quikly before they will function. Please share your staging and production domains with us during setup.
Also, it is helpful for the Quikly team to have access to your staging environment to assist with the integration and final quality checks before launch. Please check the [IP List](/documentation/ip-safelist) in case your QA environment is behind an IP firewall.
# Reference
> Quikly provides a small javascript code snippet that you can place on your website to feature an activation.
# Loading the SDK
[Section titled “Loading the SDK”](#loading-the-sdk)
This script tag must be placed on any page you want to include Quikly functionality. You must include your unique brand key in the “config” command. Once this is in place, you can make additional `qData` commands to render specific content or track specific activity.
## Staging Script Tag
[Section titled “Staging Script Tag”](#staging-script-tag)
```html
```
## Production Script Tag
[Section titled “Production Script Tag”](#production-script-tag)
```html
```
# Command Summary
[Section titled “Command Summary”](#command-summary)
After placing the script tag on your site, you control what is displayed or when activity is completed by passing commands to the `qData` function. These calls take the format:
```javascript
qData(command, value);
```
where the command is one of `'config'`, `'ui'`, or a custom event name configured for your campaign.
The `ui` commands take an object as the second argument that defines the activation IDs and what components to display. Here we use the `placements` option to define each component separately:
```javascript
qData('ui', {
placements: [
{ component: 'feature', root: '#quikly-embed', position: 'inline' }
]
});
```
Legacy integrations only accepted one component at at a time:
```javascript
qData('ui', { page: 'drop', ids: ['example'], root: 'my-container' });
```
# Commands
[Section titled “Commands”](#commands)
These commands are passed in as the first parameter to the `qData` function.
| Command | Description |
| ------------- | ------------------------------------------- |
| `config` | Specify your brand key. |
| `ui` | Display an interface element. |
| `[event key]` | A custom event name used to track activity. |
# UI Commands
[Section titled “UI Commands”](#ui-commands)
These keys can be specified in the `value` object as the second argument to the `qData` function.
| Value Keys | Description |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `placements` | An array of component definitions. When using placements, do not use the page or root keys. |
| `ids` | An array of strings specifying the campaigns you’d like to render. |
| `email` | The email address of the current user. Not required. |
| `page` | If rendering a legacy component, this specifies what component to display. Not compatible with `placements`. |
| `root` | If rendering a legacy component, the DOM ID of the element that will contain the rendered component. Do not use with `placements`. |
### Placement Configuration
[Section titled “Placement Configuration”](#placement-configuration)
| Value Keys | Description |
| ----------- | ------------------------------------------------------------------------------------------------------ |
| `component` | The name of the component, i.e. ‘feature’, ‘teaser’. |
| `root` | A CSS selector for the element that will contain this component (e.g. `'#quikly-embed'`). |
| `position` | How the component should be wrapped, i.e. ‘banner’, ‘drawer’, ‘fixedCenter’, ‘inline’, or ‘skyscraper’ |
| `open` | Set default open state, used with drawer or fixedCenter positions. |
| `template` | Override the components template. |
| `append` | `Boolean` Append the component to the DOM root element rather than replacer its contents. |
| `prepend` | `Boolean` Prepend the component to the DOM root element rather than replacer its contents. |
# Common Configurations
[Section titled “Common Configurations”](#common-configurations)
### Placements
[Section titled “Placements”](#placements)
Display any combination of elements on the page at the same time. Each element in the `placements` array should define the component, position (i.e. drawer, modal, banner), and it’s target element. For example, this will render the “feature” component in a drawer into the “quikly-embed” DOM element.
```javascript
qData('ui', {
ids: ['ABC']
placements: [{ component: 'feature', root: '#quikly-embed', position: 'drawer' }]
});
```
### Default
[Section titled “Default”](#default)
Render a Quikly anchored to the bottom of the page.
```javascript
qData('ui', { page: 'default', root: 'quikly-embed', ids: ['example'] });
```
### Hype
[Section titled “Hype”](#hype)
Display a full screen Hype activation.
```javascript
qData('ui', { page: 'hype', root: 'quikly-embed', ids: ['example'] });
```
### Drop
[Section titled “Drop”](#drop)
Display a small tile featuring a specific Drop.
```javascript
qData('ui', { page: 'drop', root: 'quikly-embed', ids: ['example'] });
```
### Instructions
[Section titled “Instructions”](#instructions)
Display a banner with instructions on what actions are necessary to receive an incentive.
```javascript
qData('ui', { page: 'instructions', root: 'quikly-embed' } )
```
### Redemption
[Section titled “Redemption”](#redemption)
Display a banner with redemption details, typically at the top of your shopping cart page.
```javascript
qData('ui', { page: 'redemption', root: 'quikly-embed' });
```
### Reward
[Section titled “Reward”](#reward)
Display an overlay with reward details. If a participant has already received an incentive, then the reward will display right away. Otherwise, this sets up the page to display the reward as soon as one is granted (i.e. via a Swap event, outlined below).
```javascript
qData('ui', { page: 'reward' } )
```
# Event Commands
[Section titled “Event Commands”](#event-commands)
Track arbitrary events, typically used to grant an incentive/reward/offer with Swap, or a heads-up within Hype. Quikly will provide you with the appropriate event name to use in the first parameter. The second parameter is optional but can be used as a payload to track alongside the event. This can be useful for reporting or audit purposes.
```javascript
qData("signup", { email: "name@example.com" });
```
If you want to track the event on a link click or form submit, set the `requestType` to `"beaconAPI"` to allow the request to finish even if the page is unloaded upon navigation.
```javascript
qData("signup", { email: "name@example.com", requestType: "beaconAPI" });
```
You can also listen for a custom event:
```javascript
window.addEventListener('qDataComplete', function(event) {
console.log('Event: ' + event.detail); // { code: "signup", args: { email: "name@example.com" } }
});
qData("signup", { email: "name@example.com", requestType: "beaconAPI" });
```
# IP Safelist
> These are the IPs Quikly uses when whitelisting is required.
These are the IPs Quikly uses when whitelisting is required for access to your systems.
```plaintext
52.5.235.149
23.22.76.198
```
# Shopify App Performance
> How Quikly's Shopify app is engineered to stay out of your storefront's critical path, plus our Lighthouse performance analysis, testing methodology, and App Store / Built for Shopify thresholds.
App bloat is a real concern on Shopify, and “what does this do to my site speed?” is one of the first questions we hear during evaluation. Here’s exactly how Quikly stays out of your storefront’s way — followed by the Lighthouse measurements that back it up.
## The short version
[Section titled “The short version”](#the-short-version)
* **Our bootstrap script can’t block your page.** The Quikly app block adds one small script that loads asynchronously — the same approach used by Google Analytics and the Meta Pixel — so it never sits in your page’s critical rendering path.
* **No active campaign? Effectively nothing loads.** When you don’t have a campaign running, our server returns an empty response and no campaign code, UI, or stylesheet is downloaded or run.
* **A live campaign loads off the critical path and stays isolated.** When a campaign is live, the widget loads asynchronously *after* your storefront is already interactive and renders into its own container — it never re-renders or reflows your theme.
## When no campaign is running
[Section titled “When no campaign is running”](#when-no-campaign-is-running)
This is the reassuring baseline, and it’s enforced on our servers — not just in the browser.
The app block adds a single small **bootstrap** script. Its only job is to ask Quikly whether anything should run on this page. When no campaign is live for your store, the answer is “no,” and our server returns an **empty JavaScript body**. There’s no popup code, no campaign UI bundle, and no campaign stylesheet — nothing beyond that tiny bootstrap is ever downloaded or executed.
In other words, the campaign UI is loaded lazily, and only when a campaign is actually live and targeting a given shopper. Most of the time, that’s the state your storefront is in.
## When a campaign is live
[Section titled “When a campaign is live”](#when-a-campaign-is-live)
Even with a campaign running, Quikly is engineered to load after your page — never before it.
**The bootstrap script is non-blocking.** It’s injected at runtime and explicitly marked `async`, so the browser fetches and runs it independently of parsing your HTML:
```js
s = d.createElement("script");
s.id = "quikly-embed-js";
s.src = "https://pixel.quikly.com/embed/js";
s.async = true; // ← non-blocking, like GA or the Meta Pixel
f.parentNode.insertBefore(s, f);
```
It’s also de-duplicated, so it’s requested at most once per page even if the block renders more than once.
**The widget runs after your page paints.** When a campaign is live, the UI bundle is requested via another dynamically-injected (and therefore async) script, so it executes *after* your storefront markup has been parsed and painted. It loads alongside and after your page — never in front of it. That means no effect on your store’s first paint, `DOMContentLoaded`, or time-to-interactive.
**It renders into its own isolated container.** Quikly creates a brand-new ``, appends it to the page, and mounts its interface into *that div only*. The only change to your theme’s DOM is one appended sibling node — Quikly never re-renders, reflows, or invalidates your existing markup.
**Its styles can’t leak into your theme.** Quikly’s CSS reset is namespaced to its own `q_`-prefixed classes, so it can’t restyle your elements. The campaign stylesheet is injected via JavaScript after your page has already rendered, so it isn’t render-blocking for your storefront — it only gates the paint of Quikly’s own widget.
## What this means for your Core Web Vitals
[Section titled “What this means for your Core Web Vitals”](#what-this-means-for-your-core-web-vitals)
Because nothing Quikly loads is render-blocking, it stays out of the metrics that matter most for your storefront:
* **Largest Contentful Paint (LCP):** Quikly’s scripts and styles load off the critical path, so they don’t delay your store’s main content from painting.
* **Interaction to Next Paint / responsiveness:** the widget’s work happens after your page is interactive, so it doesn’t push back time-to-interactive.
* **First paint and `DOMContentLoaded`:** unaffected, since every Quikly script is async or dynamically injected.
The Quikly widget appears as a follow-on paint into its own layer, after your store is already usable.
### A note on layout shift (CLS)
[Section titled “A note on layout shift (CLS)”](#a-note-on-layout-shift-cls)
The widget can render in several different placements, and its layout impact depends on which you choose:
* **Overlay placements** — a popover, or a fixed bar pinned to the top of the viewport — layer *over* your page without moving your existing content, so they don’t shift your storefront’s layout.
* **Inline placements** — injecting the widget into the flow of the page, or having it take over an existing theme banner — occupy space in your layout by design. Because the widget paints after your page loads, an inline placement can produce a small layout shift as it appears.
Either way, the placement is something you configure intentionally, so any space the widget takes is a deliberate part of your page rather than an unexpected jump. If layout stability is a priority for a given page, an overlay placement avoids movement entirely.
## The one honest caveat
[Section titled “The one honest caveat”](#the-one-honest-caveat)
We won’t claim a live campaign is completely free. When a campaign *is* running, there’s a small, **deferred** cost: an extra UI bundle download, the widget’s stylesheet, a client init, and one data round-trip before the widget appears. Like any third-party widget, that competes for bandwidth and CPU.
The important part is *when* it happens: all of it occurs **after** your storefront is interactive, and none of it sits in front of your page render or delays first paint, `DOMContentLoaded`, or interactivity. In short — there’s a lightweight cost only when there’s actually something to show a shopper, and even then it’s engineered to stay off the critical path.
## How to verify it yourself
[Section titled “How to verify it yourself”](#how-to-verify-it-yourself)
Open your browser’s DevTools, go to the **Network** tab, and load a storefront page:
* **With no live campaign:** you’ll see the small `embed/js` bootstrap request resolve to an empty response — no campaign UI bundle or stylesheet follows.
* **With a live campaign:** you’ll see the additional UI bundle and stylesheet load *after* the page’s own requests, as async, post-load fetches rather than render-blocking resources.
***
## Our Lighthouse measurements
[Section titled “Our Lighthouse measurements”](#our-lighthouse-measurements)
The rest of this page documents Quikly’s analysis of the performance impact of the Quikly Shopify app on a Shopify store.
### Requirements
[Section titled “Requirements”](#requirements)
* [To be published in the Shopify App Store, your app must not reduce storefront Lighthouse performance scores by more than 10 points.](https://shopify.dev/docs/apps/best-practices/performance#testing-for-performance:~:text=To%20be%20published%20in%20the%20Shopify%20App%20Store%2C%20your%20app%20must%20not%20reduce%20storefront%20Lighthouse%20performance%20scores%20by%20more%20than%2010%20points)
* To qualify for Built for Shopify status, your app must not reduce Lighthouse performance scores by more than ten points, and must meet [other applicable criteria](https://shopify.dev/docs/apps/store/built-for-shopify/criteria)
### Testing methodology
[Section titled “Testing methodology”](#testing-methodology)
Each page is analyzed as many times as needed for a performance number to repeat three times. Only when a performance is achieved three times is it taken into account, after which the counter for that number restarts. The first five numbers (including if duplicated) are taken into account, as a way to eliminate outliers in the final calculation. The extremes will be mentioned but not taken into account mathematically since they tend to be anomalies. Usually this results in around 50 lighthouse reports for each state.
### Storefront App Extension Performance
[Section titled “Storefront App Extension Performance”](#storefront-app-extension-performance)
These are the lighthouse reports for the storefront page on mobile (as per the [testing guide](https://shopify.dev/docs/apps/best-practices/performance/storefront)). Our app loads a banner and also an animated drawer here. Due to the nature of the drawer animation and the need to fetch graphics, it is within expectations that performance would be slightly reduced compared to the clean install of the shopify storefront.
#### Performance on a Clean Storefront (Starting Score)
[Section titled “Performance on a Clean Storefront (Starting Score)”](#performance-on-a-clean-storefront-starting-score)
The average performance of a clean storefront hovered in the low eighties, peaked once at 91 and bottomed out at 59. Interesting, it would also very consistsently yield a score of 62 or 63 every third or fourth page load. Maybe a nuance with Shopify caching?

Starting Score = (85 + 62 + 83 + 86 + 88) / 5 = (404 / 5) = 80.8
#### Performance with our App (Ending Score)
[Section titled “Performance with our App (Ending Score)”](#performance-with-our-app-ending-score)
The average performance of the storefront with our app against our test servers hovered in the high seventies. Peaking once at 89 and bottoming out at 52. Noticeably, our app seems to underperform on first load after a fresh install, and performs much better on subsequent pageloads. Our app also performs significantly better on desktop rather than mobile, likely due to our app taking up relatively more visual space on mobile, tending to hover in the high eighties and low nineties versus the clean desktop performance hovering the mid-nineties. The addition of our app also seemed to eliminate the frequency with which the storefront lighthouse performance would cyclically hit the low sixties, as it would do clean.

Ending Score = (78 + 79 + 63 + 74 + 76) / 5 = (370 / 5) = 74
#### Storefront App Extension Performance Ratio
[Section titled “Storefront App Extension Performance Ratio”](#storefront-app-extension-performance-ratio)
Performance ratio = (Starting Score / Ending Score) = 74 / 80.8 = 0.915
Furthermore, on average, our app does stay within a 10 point reduction of the lighthouse performance score. That being said, there are definitely improvments to be made here. To begin with, testing against our test API is slower than our live API, so there should be performance improvements available there. In addition to that, once client needs become more apparent after launch, there are a lot of variations with how we can store information within shopify to create more elegant loading states for banners that appear immediately, which should drastically improve performance.
### Checkout Extension Performance
[Section titled “Checkout Extension Performance”](#checkout-extension-performance)
The lighthouse on mobile for a clean checkout throws an error: The page did not load any content. Please ensure you keep the browser window in the foreground during the load and try again. So we will be using the desktop for this. All in all, using the eye test and with the knowledge of how lighthouse performance check works, the checkout extension should fare much better –– the loading should be virtually instantaneous since the presence of the intial state is determined not by a fetch but by cart attributes.
#### Performance on a Clean Checkout (Starting Score)
[Section titled “Performance on a Clean Checkout (Starting Score)”](#performance-on-a-clean-checkout-starting-score)
The performance of a clean checkout stayed between low eighties and high seventies with no real outliers.

Starting Score = (82 + 78 + 74 + 83 + 80) / 5 = (397 / 5) = 79.4
#### Performance with the Checkout Extension (Ending Score)
[Section titled “Performance with the Checkout Extension (Ending Score)”](#performance-with-the-checkout-extension-ending-score)
As per expectations, our checkout extension performs very well in lighthouse performance analysis. It is not 100% clear as to why the score is elevated to above that of a clean page, but regardless of that, the score is well within acceptable ranges and effectively does not reduce lighthouse performance at all. There were no real outliers during numerous reloads –– the checkout extension is very consistent.

Ending Score = (82 + 83 + 83 + 79 + 81) / 5 = (408 / 5) = 80.6
#### Storefront Checkout Extension Performance Ratio
[Section titled “Storefront Checkout Extension Performance Ratio”](#storefront-checkout-extension-performance-ratio)
Performance ratio = (Starting Score / Ending Score) = 80.6 / 79.4 = 1.015
# Campaign Ideas
> Common ways brands use Quikly — pick the play that matches your goal this week.
Not sure what to run next? These are the plays we see brands return to most often, paired with examples from real customer campaigns. Each one maps to a specific goal.
## Grow your email list
[Section titled “Grow your email list”](#grow-your-email-list)

**Goal:** Turn anonymous traffic into subscribers.
Run a campaign that reveals an offer in exchange for an email. Use it as your sitewide welcome unit, or trigger it on exit intent. Subscribers get a real reward (not a static “10% off”), and you walk away with audiences that opted in to a moment of urgency — they tend to convert better downstream.
*Pictured: Chunk Nibbles uses a tiered reward to motivate a fast email opt-in.*
Related: [Klaviyo integration](/integrations/klaviyo/)
## Re-engage your existing email or SMS audience
[Section titled “Re-engage your existing email or SMS audience”](#re-engage-your-existing-email-or-sms-audience)

**Goal:** Drive a measurable lift from a list send.
Send your subscribers to a Quikly campaign built just for them. Because the reward is time-boxed and quantity-limited, the same list responds harder than it does to a flat promo code. Pair the campaign with a Klaviyo or Vibes flow so the reward delivers automatically.
*Pictured: Jupiter offers the first 75 subscribers a free Clarity Comb with any $65+ order — one clean reward, capped quantity, no tiered math to parse.*
## Promote a specific product or collection
[Section titled “Promote a specific product or collection”](#promote-a-specific-product-or-collection)

**Goal:** Concentrate demand on a single SKU, launch, or category.
Target the campaign to product or collection pages so the offer only appears in the right context. This works well for new launches, slow-moving inventory you want to clear, or a hero collection you’re trying to build heat around.
*Pictured: Beek puts a one-day, capped offer behind a single hero shoe — concentrated demand without sitewide markdowns.*
Related: [Product page targeting](/shopify/tutorials/product-page-targeting/), [Visibility controls](/shopify/tutorials/visibility-controls/)
## Share links with affiliates and influencers
[Section titled “Share links with affiliates and influencers”](#share-links-with-affiliates-and-influencers)

**Goal:** Give partners something more compelling than a static discount code.
Generate campaign links that unlock a Quikly offer when clicked. Each partner can share their own link, and the urgency mechanic gives their audience a reason to click *now* instead of bookmarking it. You get cleaner attribution and a more interesting story for the creator to tell.
*Pictured: Borboleta gives each influencer a tiered VIP unlock — the first 25 of their followers get the deepest discount, then it tapers.*
## A/B test offers on the same day
[Section titled “A/B test offers on the same day”](#ab-test-offers-on-the-same-day)
 
**Goal:** Find out which offer strategy actually moves the needle.
Run two campaigns side by side and split traffic between them — for example, a tiered dollar-off ladder against a flat percentage-off code, or a discount against a free-gift-with-purchase. Because Quikly campaigns are quick to spin up, you can resolve the test in hours instead of weeks and roll the winner out the same day.
*Pictured: a tiered “first 10 get the most off” structure versus a flat 25% off promo code — same audience, very different psychology.*
Related: [Duplicate a campaign](/shopify/tutorials/duplicate-campaign/), [Offer configuration](/shopify/tutorials/offer-configuration/)
## Add urgency to a scheduled storewide sale
[Section titled “Add urgency to a scheduled storewide sale”](#add-urgency-to-a-scheduled-storewide-sale)
 
**Goal:** Lift conversion on a sale you’re already running.
Instead of “20% off all day,” cap the offer with a redemption limit or a countdown. Shoppers who would have browsed and bounced have a reason to check out now. Layer this on top of your existing sale calendar — it doesn’t require a new campaign concept, just a tighter constraint on the one you’ve already planned.
*Pictured: Neuro tiers the discount so the fastest shoppers get the most off, and Jetson caps a sitewide offer at 25 orders to convert browsers into buyers before the count runs out.*
Related: [Offer configuration](/shopify/tutorials/offer-configuration/)
## Amplify an existing sale
[Section titled “Amplify an existing sale”](#amplify-an-existing-sale)

**Goal:** Pull conversion forward in the day on a sale you’re already running.
Stack a small bonus on top of your live sitewide promotion — an extra $5 off, or an extra 5% — and keep the quantity intentionally tight. Even something like *an extra $5 off the first 20 orders* is enough to pull conversion forward in the day. The reward can be nominal because the urgency is doing the work, not the discount. Especially effective for brands with steady traffic but late-day purchasing patterns.
*Pictured: Lucky Energy stacks just an extra 5% off — capped at the first 25 orders — on top of their existing sale. Small reward, tight cap, real-time countdown doing the heavy lifting.*
Related: [Offer configuration](/shopify/tutorials/offer-configuration/)
***
Still not sure where to start? Most brands run their first Quikly campaign as either a list-growth play or a re-engagement send to an existing audience — both have the shortest path to a measurable result.
# Legacy Checkout Extension
# Legacy Checkout Extension
[Section titled “Legacy Checkout Extension”](#legacy-checkout-extension)
If you use Shopify Plus and have not yet updated to the new “extensible checkout” system, you can use the “Edit Code” feature in your Shopify theme to add the Quikly checkout snippet.
1. Create a new snippet called ‘quikly.liquid’. Make sure you replace ##REPLACE\_WITH\_BRAND\_ID## with your config key provided by Quikly. You can find the full source of this file below.


2. Edit the checkout.liquid file, and add `{% render 'quikly' %}` to include the new snippet. Typically you will want to place this directly above the `{{ content_for_layout }}` tag

```html
{% render 'quikly' %}
{{ content_for_layout }}
```
Here is the contents of snippets/quikly.liquid.
```html
```
# FAQ
> Answers to common questions from Shopify merchants evaluating Quikly.
Common questions we hear from merchants evaluating Quikly. If yours isn’t here, reach out — we’ll answer it and add it.
## Can Quikly read live Shopify inventory and show “X units left” in a popup?
[Section titled “Can Quikly read live Shopify inventory and show “X units left” in a popup?”](#can-quikly-read-live-shopify-inventory-and-show-x-units-left-in-a-popup)
Quikly works on offer limits rather than live SKU stock, which plays nicely with scarcity messaging. You set a redemption cap (a bit below your available units) and the campaign enforces it in real time.
So if you have 55–60 units pre-promo, you’d run something like “only the next 50 orders get X% off” with a live countdown as purchases come in. The cap becomes the marketing hook — and because it’s enforced by Quikly, you don’t have to worry about overselling if Shopify’s inventory count drifts.
Related: [Campaign Ideas](/shopify/campaign-ideas/)
## Can I target only low-intent users?
[Section titled “Can I target only low-intent users?”](#can-i-target-only-low-intent-users)
Yes. The strongest lever is **list segmentation via an activation link**. Quikly campaigns can be gated to a specific link, so the offer is only visible to traffic you send to it — e.g. email subscribers who haven’t engaged in the last 6 months. That gives you a clean, defined audience, predictable volume, and clear attribution.
You can also layer in on-site signals like exit intent, and suppress the campaign for logged-in repeat customers. We generally recommend leading with the segmented activation link though — it’s a more precise definition of “low intent” than behavioral guesses, and it measures better.
Related: [Klaviyo integration](/integrations/klaviyo/), [Visibility controls](/shopify/tutorials/visibility-controls/)
## How long does it take to install and launch a first campaign?
[Section titled “How long does it take to install and launch a first campaign?”](#how-long-does-it-take-to-install-and-launch-a-first-campaign)
Quikly installs from the Shopify App Store and the storefront extension drops into your theme without code changes. Most merchants are live with a first campaign the same day — configure the offer, set the cap and timing, point it at the audience or pages you want, and publish.
Related: [Getting Started](/shopify/getting-started/), [Offer configuration](/shopify/tutorials/offer-configuration/)
## Will Quikly slow down my storefront?
[Section titled “Will Quikly slow down my storefront?”](#will-quikly-slow-down-my-storefront)
No. The app extension is built against Shopify’s performance requirements (no more than a 10-point Lighthouse drop, which is the Built for Shopify bar). We continuously test against a clean storefront baseline.
Related: [Shopify App Performance](/reference/performance/)
## Does it work with a headless Shopify store?
[Section titled “Does it work with a headless Shopify store?”](#does-it-work-with-a-headless-shopify-store)
Yes — Quikly has a headless integration path for Hydrogen and custom storefronts using our JavaScript SDK.
Related: [Headless](/shopify/headless/), [JavaScript SDK](/javascript-sdk/overview/)
## How are discount codes generated and delivered?
[Section titled “How are discount codes generated and delivered?”](#how-are-discount-codes-generated-and-delivered)
Quikly generates and manages standard Shopify discount codes on your behalf — no manual setup in the Shopify admin.
## Can I reuse the same discount code across campaigns?
[Section titled “Can I reuse the same discount code across campaigns?”](#can-i-reuse-the-same-discount-code-across-campaigns)
No. Because Quikly controls the total number of uses for each offer code, every campaign needs its own unique code. If you’re reusing a base code across many campaigns and running out of variations, two easy options:
* **Append the date** to the code — for example, `TENOFF0530` for a campaign starting May 30th.
* **Leave the discount code blank** when configuring the offer, and Quikly will auto-generate a unique code for you.
Related: [Configuring Offers](/shopify/tutorials/offer-configuration/)
## Can I run Quikly inside Shopify Checkout?
[Section titled “Can I run Quikly inside Shopify Checkout?”](#can-i-run-quikly-inside-shopify-checkout)
Yes. Our checkout extension lets you surface urgency messaging and offers inside the checkout itself, which is otherwise locked down on Shopify Plus.
Related: [Checkout Extension](/shopify/checkout-extension/)
## Can I A/B test Quikly against my current popup or promo?
[Section titled “Can I A/B test Quikly against my current popup or promo?”](#can-i-ab-test-quikly-against-my-current-popup-or-promo)
Yes — A/B testing is built in, and reports on revenue lift rather than just click or open rates. That’s the cleanest way to prove incremental revenue versus a flat-discount baseline.
Related: [Overview](/shopify/overview/)
## How do I move the Teaser or Popup up/down on the page, or layer it behind my theme?
[Section titled “How do I move the Teaser or Popup up/down on the page, or layer it behind my theme?”](#how-do-i-move-the-teaser-or-popup-updown-on-the-page-or-layer-it-behind-my-theme)
Both the Teaser and the Popup expose the same two controls under **Build > Components > \[component] > Edit > Styles > Display**:
* **Spacing > Offset** sets the distance from the edge of the screen, with a separate **Vertical (Mobile)** field for tuning mobile independently.
* **Layout > Z-Index** sets stacking order. The default is `1001`; lower the value to sit behind a theme element like the cart drawer.
Related: [Position and Layer the Teaser and Popup](/shopify/tutorials/positioning-teaser-and-popup/)
## In the popup, how do I insert copy that dynamically pulls in the offer description and quantity?
[Section titled “In the popup, how do I insert copy that dynamically pulls in the offer description and quantity?”](#in-the-popup-how-do-i-insert-copy-that-dynamically-pulls-in-the-offer-description-and-quantity)
Use **Dynamic Text**. In the Design Editor, go to **Build > Components > Popup**, click the **Edit** (pencil) icon, and click into the Formatted Text block. Place your cursor where the value should appear, click **Dynamic Text**, and choose **Offer info**. From there you can insert the offer’s full title, short title, subtitle, or quantity, and choose whether it reflects the current tier or a specific tier (e.g. tier 1 for “the first 50 to buy”).
To show how many offers have been *claimed* or are *still available* rather than the tier’s total quantity, use the **Offer count** dynamic text type instead.
Related: [Insert Dynamic Offer Text in the Popup](/shopify/tutorials/dynamic-offer-tokens/)
## How do I automatically adjust the reward description from singular to plural?
[Section titled “How do I automatically adjust the reward description from singular to plural?”](#how-do-i-automatically-adjust-the-reward-description-from-singular-to-plural)
Use the **Offer count** dynamic text tag instead of typing the word yourself. If you hardcode “offers” after the count, you’ll get “1 offers left” when the count reaches 1. Instead, insert a **Dynamic Text > Offer count** tag, set the **Count type** (e.g. Remaining in tier), and fill in its **Singular word** and **Plural word** fields — then delete the static word from the text. The tag picks the right form automatically, so it reads “1 order left” or “12 orders left.”
Related: [Insert Dynamic Offer Text in the Popup](/shopify/tutorials/dynamic-offer-tokens/)
## How do I test offer codes before the campaign is live?
[Section titled “How do I test offer codes before the campaign is live?”](#how-do-i-test-offer-codes-before-the-campaign-is-live)
On the campaign overview screen, open the **Timing** card, click **Edit**, and under **Timing > Starts** check **Activate discount codes before campaign launches**. The codes become active in your store so you can confirm their configuration and how they combine with your other offers before the start date. Note that the codes are genuinely valid while this is checked — anyone who knows a code could use it — so you may want to uncheck it once testing is done.
Related: [Test Offer Codes Before Launch](/shopify/tutorials/test-before-launch/)
## How do I add an image to the popup?
[Section titled “How do I add an image to the popup?”](#how-do-i-add-an-image-to-the-popup)
In the Design Editor, click **Components**, select the **Popup**, and click the **pencil** to edit. Click **Add Content > Image** (or click an existing image), then **Select image** to open your library — choose an existing image or click **Upload new**. After selecting, you can expand the image to the popup edges, set its size and crop (aspect ratio), align it, round its corners, and add borders or a background.
Uploads can be any image format (PNG, JPG, GIF, and others such as SVG or WebP), up to a maximum of 10 MB. There’s no fixed dimension requirement.
Related: [Add an Image to the Popup](/shopify/tutorials/popup-image/)
## On mobile, the popup looks too large — can I optimize it?
[Section titled “On mobile, the popup looks too large — can I optimize it?”](#on-mobile-the-popup-looks-too-large--can-i-optimize-it)
Yes. The most effective option is to wrap the content you don’t need on phones — like a next-up offer display — in a **Container** block and hide that container on mobile: in the block’s **Styles > Visibility**, choose **Mobile (≤480px)** or **Tablet and below (≤768px)**. Only the wrapped content drops on small screens, so the popup renders more compactly. You can also resize the **Teaser** under **Build > Components > Teaser > Edit > Styles**, where you’ll find width and height controls.
Related: [Position and Layer the Teaser and Popup](/shopify/tutorials/positioning-teaser-and-popup/)
## Once my campaign is published, how do I take it off my site?
[Section titled “Once my campaign is published, how do I take it off my site?”](#once-my-campaign-is-published-how-do-i-take-it-off-my-site)
Open the campaign’s detail page and click **Pause campaign** in the header. Pausing hides it from visitors and returns it to draft mode — you don’t need to delete it. Re-publish from the same page whenever you want it live again.
Related: [Managing a Live Campaign](/shopify/tutorials/managing-live-campaign/)
## Can I change the quantities in a tier while the campaign is live?
[Section titled “Can I change the quantities in a tier while the campaign is live?”](#can-i-change-the-quantities-in-a-tier-while-the-campaign-is-live)
Yes, though we don’t recommend it — fixed quantities protect the authenticity of the promotion. If you need to, go to the campaign overview page, click **View All** next to Offer configuration, expand the offer level, update the quantity, and click **Save Offer**. Because customers may have already claimed, you’ll get a confirmation prompt before it applies: increasing the quantity adds new offers to the claim queue immediately, while decreasing only removes *unclaimed* offers (anyone who already claimed keeps theirs).
Related: [Managing a Live Campaign](/shopify/tutorials/managing-live-campaign/)
## Can I make the discount code valid only on certain products?
[Section titled “Can I make the discount code valid only on certain products?”](#can-i-make-the-discount-code-valid-only-on-certain-products)
Yes. Each offer level has a **“Discount applies to”** dropdown under **Build > Offers** where you can scope the discount to **All Products**, **Specific products**, or **Specific collections**. To *exclude* certain items instead, create a Shopify collection of everything that should be eligible and point the offer at that collection — Shopify discount codes only support “applies to” rules, not “exclude” rules.
Related: [Configuring Offers](/shopify/tutorials/offer-configuration/)
## What happens when the offer cap is hit?
[Section titled “What happens when the offer cap is hit?”](#what-happens-when-the-offer-cap-is-hit)
The campaign stops awarding the headline offer in real time and can fall back to a lower tier (e.g. “next 100 get 10% off”) or close out entirely. Customers see exactly where they stand while it’s live, which is what creates the urgency.
Related: [Campaign Ideas](/shopify/campaign-ideas/)
# Getting Started
The steps to add Quikly to your shopify store can vary depending on if you are on Shopify Plus or use a headless commerce set up. The basic steps are:
1. Create your Quikly account. Your client success rep will provide you with your login information.
2. Install the Quikly App into your shopify store.
3. Turn on the plugin in the theme editor, or for a headless setup, install the script tag.
4. If you use the legacy checkout.liquid checkout process, install the checkout extension snippet in your theme.
# App Installation
[Section titled “App Installation”](#app-installation)
1. Install the [Quikly: Urgency Marketing for Shopify App](https://apps.shopify.com/quikly) on your Shopify store.

2. Follow the onboarding steps to enable the theme extension.
## Theme Extensions
[Section titled “Theme Extensions”](#theme-extensions)
### App Embed Block
[Section titled “App Embed Block”](#app-embed-block)
If you use a shopify theme, you can use the Shopify theme editor to enable the Quikly components in your theme.
First let’s use the theme editor to add the Quikly App Embed block to your theme. Click to Sales channels > Online Store > Themes, then on “Customize”. Click on the App embeds icon, then turn on the “Quikly Embed Block”. This will allow any current Quikly promotion to appear seamlessly on your site.

### Checkout Extension (Shopify Plus)
[Section titled “Checkout Extension (Shopify Plus)”](#checkout-extension-shopify-plus)
The Quikly Checkout Extension displays Quikly promotions during checkout (for example, a banner confirming a claimed discount is applied to the cart). It is separate from the App Embed Block above — the embed block powers Quikly across your storefront, while the checkout extension specifically adds Quikly to the checkout page. Shopify Plus is required to add checkout extensions.
To install the Checkout Extension:
1. **Access checkout settings:** From your Shopify Admin, go to **Settings > Checkout**.
2. **Open the editor:** Scroll to the **Configuration** section and click **Customize** (or **Customize checkout**).
3. **Add the app block:** In the checkout editor, click **Add app** (or **Add block**) in the left-hand sidebar.
4. **Select the extension:** Find **Quikly** in the list and enable the **Quikly Checkout Extension**.
5. **Position and save:** Drag the block to place it where you want it in the checkout layout, then click **Save**.
[Your browser does not support the video tag.](/shopify/QuiklyCheckoutExtension.mp4)
You’re done! Quikly will work with you to configure your campaigns and provide activation links to trigger these promotions on demand.
# Headless Commerce
[Section titled “Headless Commerce”](#headless-commerce)
If you have a custom storefront that does not use a shopify theme, you can grab the [script tag here](./headless).
# Example
[Section titled “Example”](#example)
Here is an example campaign that is configured with a limited quantity discount, providing a unique discount to the first 25 customers that enter their email address.

During checkout, those fast enough to claim a discount code will see an information banner to indicate if the promotion is currently applied to their cart.

# Script Tag / Headless Setup
# Headless Commerce
[Section titled “Headless Commerce”](#headless-commerce)
If you have a custom storefront that does not use a shopify theme, you can use a version of our [script tag](/javascript-sdk/reference) along with Google Tag Manager or pasted directly into your frontend code.
```html
```
If you use Shopify’s checkout process, follow the [checkout extension installation instructions](/shopify/checkout-extension) to complete the set up.
# Identifying Quikly Orders in Shopify
> Two ways an order relates to a Quikly campaign — the offer code a shopper used, and the _q_scope attribute that records who saw the campaign — and how to find each in the Shopify admin and order exports.
A common question is: *which of my Shopify orders came from Quikly?* There are two distinct ways an order relates to a Quikly campaign, and they answer different questions. Knowing which one to look at — and where to look — saves a lot of confusion.
In short:
* **Offer codes** tell you who **used** an offer.
* The **`_q_scope` attribute** tells you who **saw** the campaign.
## Offer codes — who *used* an offer
[Section titled “Offer codes — who used an offer”](#offer-codes--who-used-an-offer)
Any order that redeemed a Quikly offer will have one of the campaign’s promo/discount codes applied. This is the clearest “this order came from Quikly” signal, and it’s the easiest to work with in the Shopify admin.
To find these orders, filter or search your orders by the discount code(s) associated with the campaign. The code appears on the order like any other discount, so it shows up in the admin, on the order detail, and in exports without any special handling.
The limitation: offer codes only capture **redemptions**. They tell you nothing about shoppers who were shown the campaign but didn’t use an offer — which you need in order to measure the campaign’s overall impact.
## The `_q_scope` attribute — who *saw* the campaign
[Section titled “The \_q\_scope attribute — who saw the campaign”](#the-_q_scope-attribute--who-saw-the-campaign)
Separately, any order that came from a session that was *shown* the Quikly campaign — whether or not an offer code was ultimately used — receives an order note attribute called `_q_scope`.
This is the signal that captures the full population exposed to the campaign, not just the people who redeemed.
There are a few important things to know about how this attribute behaves in Shopify:
* **It’s an order attribute, not a tag.** It will **not** appear in the Tags column or in tag-based filters.
* **It surfaces under Additional Details.** On an individual order, you’ll find it in the **Additional Details** section of the order page.
* **Admin search does not index note attributes.** Searching for `_q_scope` in the Shopify admin returns nothing. This is the single most common point of confusion — the value is on the order, but the admin’s search simply doesn’t look at note attributes.
* **It is included in a CSV order export.** Exporting your orders to CSV is the easiest way to pull and filter on `_q_scope`. In the export, note attributes appear in the `Note Attributes` column as `Name:Value` pairs (for example, `_q_scope:...`).
So if you want to work with `_q_scope` across many orders, export to CSV and filter there rather than trying to search the admin.
## Why the distinction matters
[Section titled “Why the distinction matters”](#why-the-distinction-matters)
When a campaign is configured to display to only a percentage of traffic (an A/B test), `_q_scope` is what makes measurement possible. It lets us compare the **served** group against the **control** group and measure the campaign’s overall lift on metrics like conversion rate, sales per session, and average order value.
Offer codes alone can’t do this — they only tell you redemptions, not overall lift across everyone who was exposed.
When Quikly reports on A/B test results, we report on the number of sessions served and the orders from those sessions, broken out between the served and control groups.
## Summary
[Section titled “Summary”](#summary)
| Signal | Answers | Where to find it |
| ------------------------ | ------------------------ | -------------------------------------------------------------------------------------- |
| **Offer code** | Who **used** an offer | Shopify admin — filter/search by discount code |
| **`_q_scope` attribute** | Who **saw** the campaign | Order page → Additional Details; or the `Note Attributes` column of a CSV order export |
***
Need help reconciling your order data with a Quikly A/B test? Reach out to your account team and we can walk through the numbers with you.
# Quikly: Urgency Marketing for Shopify
> Drive immediate purchases with real scarcity. Limited offers boost conversions and AOV.
Drive immediate purchases with real scarcity. Limited offers boost conversions and AOV.
Boost conversions & AOV with urgency campaigns proven by 70M+ consumers. Launch quantity-limited flash sales, tiered fast-buyer discounts, or time-sensitive offers where the discount decreases hourly. Our algorithm analyzes your store’s sales & discount history to suggest urgency strategies—whether you want to move inventory, bump AOV, or shorten decision time.

## Key Features
[Section titled “Key Features”](#key-features)
### **Proven Tactics and Designs**
[Section titled “Proven Tactics and Designs”](#proven-tactics-and-designs)
“Next 50 get $20 off” beats “20% off this weekend” every time. Our urgency campaigns are backed by data from 70M+ consumer interactions.
### **Urgency Across All Channels**
[Section titled “Urgency Across All Channels”](#urgency-across-all-channels)
Deploy campaigns via popups, banners, checkout, email & SMS to drive immediate purchases wherever your customers are.
### **Smart Urgency Recommendations**
[Section titled “Smart Urgency Recommendations”](#smart-urgency-recommendations)
Learn from your own sales & discount patterns. Our algorithm suggests the most effective urgency strategies for your specific store.
### **Shift Stale Stock Fast**
[Section titled “Shift Stale Stock Fast”](#shift-stale-stock-fast)
Time-limited deals like “15% off for next 5 hours” help move inventory quickly while protecting margins.
### **A/B Testing Built-In**
[Section titled “A/B Testing Built-In”](#ab-testing-built-in)
Measure revenue lift, not just clicks or opens. Prove incremental revenue with our built-in testing framework.
## How It Works
[Section titled “How It Works”](#how-it-works)
### Example Campaign
[Section titled “Example Campaign”](#example-campaign)
Configure a promotion to offer one-time-use discount codes to the first 100 customers that click through an email. After the first 100 claim, the next 500 will earn a discount of a lower value. This promotion is only visible to customers who click from your email.
### Real-Time Updates
[Section titled “Real-Time Updates”](#real-time-updates)
Customers see exactly where they stand in the queue and how long they have to act before the promotion ends. This creates genuine urgency to respond and get the best offer.
## Pricing
[Section titled “Pricing”](#pricing)
We offer flexible pricing plans to fit businesses of all sizes, from free plans for smaller stores to enterprise solutions for high-volume merchants.
**View our complete pricing details at [hello.quikly.com/pricing](https://hello.quikly.com/pricing)**
All plans include 14-day free trials and are designed to scale with your business growth.
## Integrations
[Section titled “Integrations”](#integrations)
Works seamlessly with:
* **Checkout Extensions** - Native Shopify checkout integration
* **Email Marketing** - Klaviyo, Iterable, Braze
* **CRM** - Salesforce integration
* **SMS** - Direct SMS campaign deployment
* **All Shopify Themes** - Works with the latest themes
## Quick Start for Developers
[Section titled “Quick Start for Developers”](#quick-start-for-developers)
1. **Install the App** - Available in the [Shopify App Store](https://apps.shopify.com/quikly)
2. **Configure Campaigns** - Use our intuitive dashboard to set up urgency campaigns
3. **Deploy** - Campaigns go live in minutes across all your channels
4. **Measure** - Track performance with built-in analytics and A/B testing
## Technical Requirements
[Section titled “Technical Requirements”](#technical-requirements)
* Shopify store with admin access
* Compatible with all modern Shopify themes
* No coding required for basic setup
* API access available for custom integrations
## Support & Resources
[Section titled “Support & Resources”](#support--resources)
* **Documentation** - Comprehensive guides and API reference
* **Developer Support** - Technical assistance for custom implementations
* **Community** - Connect with other developers using Quikly
* **Status Page** - Real-time system status and updates
# How to Use the Advanced Editor
> Learn how to use the advanced editor features in the Quikly platform.
Coming Soon
This tutorial will cover how to use the advanced editor features in the Quikly platform to create more sophisticated campaigns.
## What You’ll Learn
[Section titled “What You’ll Learn”](#what-youll-learn)
* Advanced campaign configuration options
* Custom styling and branding features
* Advanced targeting and segmentation
* A/B testing capabilities
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [How to Duplicate a Campaign](/shopify/tutorials/duplicate-campaign)
* [How to Position the Banner Component](/shopify/tutorials/position-banner) - Coming Soon
# Coordinating Your Site with the Quikly Test Group
> How Quikly handles A/B test split groups, and how to detect whether a visitor is in the Quikly half if you want to coordinate your own site elements.
When you run Quikly as an A/B test — for example, showing the campaign to 50% of visitors via [Visibility Percentage](/shopify/tutorials/visibility-controls/) — a common question is: *can I tell which visitors are in the Quikly half, so I can keep other site elements consistent?*
Here’s how we think about it, and what’s available if you need to drive your own logic off the split.
## The control group is left untouched
[Section titled “The control group is left untouched”](#the-control-group-is-left-untouched)
For the **control (BAU) group** — the visitors who don’t get Quikly — we intentionally don’t touch anything. Those shoppers get your normal site experience with no Quikly footprint, which keeps the test clean: any lift you measure is attributable to Quikly, not to a side effect of the test setup.
## Let Quikly account for coordinating changes
[Section titled “Let Quikly account for coordinating changes”](#let-quikly-account-for-coordinating-changes)
For the **Quikly group**, the ideal is that Quikly handles any coordination on your behalf, rather than you detecting the split and reacting to it yourself.
The most common example is other on-site popups overlapping the campaign. Quikly has a built-in **“hide other popups”** setting that suppresses competing popups whenever the Quikly campaign is visible — and only for the Quikly group, since the control group never triggers it. If that’s the coordination you’re after, your account team can enable it on the campaign and you don’t need to write any code.
If there’s a coordinating change you’d like Quikly to handle, ask your account team first — there’s a good chance we can do it on our side.
## Detecting the Quikly group yourself
[Section titled “Detecting the Quikly group yourself”](#detecting-the-quikly-group-yourself)
If you want to conditionally show or hide *your own* site elements based on the split, you can read Quikly’s first-party `q_visibility` cookie on the client. When a visitor is placed in the Quikly half, the cookie holds an entry set to `true`.
This helper returns `true` when a Quikly campaign is showing for the visitor:
```js
function isQuiklyVisible() {
var match = document.cookie.match(/q_visibility=([^;]+)/);
if (!match) return false;
var visibility = JSON.parse(decodeURIComponent(match[1]));
return Object.keys(visibility).some(function (key) {
return !key.startsWith("v_") && visibility[key] === true;
});
}
```
Run it on a short delay so Quikly’s script has time to load and make the assignment, then coordinate your own elements:
```js
setTimeout(function () {
if (isQuiklyVisible()) {
// visitor is in the Quikly group —
// e.g. hide your own competing popup
}
}, 1000); // 1s is usually plenty
```
### Two things to keep in mind
[Section titled “Two things to keep in mind”](#two-things-to-keep-in-mind)
* **Give it a moment.** The group assignment is made client-side just after Quikly’s script loads, so the cookie isn’t guaranteed to be there on the very first render. The `setTimeout` above handles this — bump it to `1500`–`2000`ms if your page loads slowly. This approach fits elements that aren’t first-paint-critical, like a delayed popup, better than something that must be correct the instant the page paints.
* **Check for “visible,” not “not visible.”** Only an explicit `true` means a campaign is showing. A missing or `false` entry can mean the visitor is in the control half *or* that a campaign was hidden for another reason (sold out, repeat visitor, a URL rule, and so on) — so treat the helper as “is Quikly showing?” rather than “is this visitor in the control group?”
***
Not sure which approach fits your test? Reach out to your account team — if you tell us the specific element you’re trying to coordinate, we can usually handle it on our side.
# How to Duplicate a Campaign
> Learn how to duplicate an existing campaign in the Quikly platform.
Sometimes you want to create a new campaign based on an existing one. This tutorial shows you how to duplicate a campaign in the Quikly platform.
## What You’ll Learn
[Section titled “What You’ll Learn”](#what-youll-learn)
* How to access campaigns from your Shopify admin
* How to duplicate an existing campaign with all original settings
* How to update campaign timing and scheduling
* How to modify incentive structures and discount codes
* How to preview campaigns in your store before publishing
* How to publish and schedule your duplicated campaign
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [How to Use the Advanced Editor](/shopify/tutorials/advanced-editor) - Coming Soon
* [How to Position the Banner Component](/shopify/tutorials/position-banner) - Coming Soon
# Insert Dynamic Offer Text in the Popup
> Use Dynamic Text to pull an offer's title, subtitle, and quantity into your popup copy so it stays accurate as the campaign runs.
Instead of typing your offer details into the popup by hand, you can insert **Dynamic Text** that pulls them straight from the offer — the title, subtitle, and quantity. Because the text reads from the campaign’s offer data, the copy stays correct even if you change the offer later, and it can reference a specific tier (for example, “the first 50 to buy”).
[Your browser does not support the video tag.](https://d1kt5al5rlsv0i.cloudfront.net/quikly.github.io/videos/dynamic-tags.mp4)
## Insert an Offer Detail
[Section titled “Insert an Offer Detail”](#insert-an-offer-detail)
1. In the Design Editor, go to **Build > Components > Popup**, then click the **Edit** icon (the pencil).

2. Click the **Formatted Text** block you want to edit to open the rich text editor.
3. Place your cursor where you want the dynamic value to appear, then click **Dynamic Text** at the bottom of the panel.

4. Choose **Offer info** from the Dynamic Text menu.

5. Pick the **Offer field** you want to display:
* **Full (long) title**
* **Short title**
* **Subtitle**
* **Quantity** — the total number of offers in the selected tier
6. Set the **Offer level** to control which tier the value comes from:
* **Current tier** — shows the value for whichever tier is active at the moment a shopper sees the popup
* **A specific tier** (e.g. `1`) — always shows that tier’s value
7. Click **Add Text** to insert the token, then **Save**.

## Example: “The first 50 to buy”
[Section titled “Example: “The first 50 to buy””](#example-the-first-50-to-buy)
To show how many total offers are in the first tier:
* **Offer field:** Quantity
* **Offer level:** 1
Surround the token with your own copy in the editor — for example, type `The first`, insert the Quantity token, then type `to buy` — and the popup renders **“The first 50 to buy”** automatically.
## Showing Claimed or Remaining Counts Instead
[Section titled “Showing Claimed or Remaining Counts Instead”](#showing-claimed-or-remaining-counts-instead)
The **Offer info > Quantity** field shows the *total* number of offers in a tier. If you want a live count of how many have been **claimed** or how many are **still available (left)**, use the **Offer count** dynamic text type instead of Offer info. Offer count updates in real time as shoppers claim the offer, which is what drives the scarcity messaging.
## Automatically Pluralize the Reward Word
[Section titled “Automatically Pluralize the Reward Word”](#automatically-pluralize-the-reward-word)
When a count drops to 1, hardcoded copy reads awkwardly — “1 **offers** left” instead of “1 offer left.” Let the **Offer count** tag handle the singular/plural switch for you instead of typing the word yourself.
The common mistake is to insert the count tag and then type the word after it. Here, “orders” is typed as static text, so it always reads “orders” even when the count is 1:

Instead, define the word inside the tag:
1. Insert a **Dynamic Text > Offer count** tag and set the **Count type** (for example, **Remaining in tier**).
2. Fill in the **Singular word** (shown when the count is 1, e.g. `order`) and the **Plural word** (shown when the count is more than 1, e.g. `orders`).
3. Click **Save**, then **delete the static word** you typed in the text field — the tag now supplies the correctly pluralized word.

The copy now reads “1 order left” or “12 orders left” automatically as the count changes. This works for any reward word — offer, reward, discount, order, and so on.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Configuring Offers](/shopify/tutorials/offer-configuration) — Set the products, tiers, and quantities the dynamic text reads from
* [Position and Layer the Teaser and Popup](/shopify/tutorials/positioning-teaser-and-popup)
* [Campaign Visibility Controls](/shopify/tutorials/visibility-controls)
# Country, Market, Language & Currency Targeting
> Show or hide campaigns based on a visitor's country, your store's selected market, their language, or their currency.
Quikly can show or hide a campaign based on where a visitor is located and how they’re browsing your storefront. For example, to make a campaign appear only to people in the United States, enable **Based on visitor’s location** and add the United States to **Show only in these countries**.
## Configuring the filters
[Section titled “Configuring the filters”](#configuring-the-filters)
1. From the **Campaign Overview** page, click **Edit** next to **Audience Targeting**.

2. In the modal that appears, find the **Additional Filters** section and check the box for the filter you want to use:
* **Based on visitor’s location** — Show or hide the campaign for visitors from specific countries, based on their IP address.
* **Based on store’s selected market** — Show or hide the campaign based on your Shopify store’s selected market.
* **Based on language** — Show or hide the campaign based on visitors’ language preferences.
* **Based on currency** — Show or hide the campaign based on visitors’ currency preferences.
3. Once a filter is enabled, two fields appear. Add values to either or both:
* **Show only in/for these…** — The campaign appears *only* when the visitor matches one of these values (an allow list).
* **Hide in/for these…** — The campaign is hidden when the visitor matches any of these values (a block list).

4. Click **Save**.
When you save, the enabled filters appear back on the Campaign Overview under **Additional Filters** (e.g. “Geo countries — Include: United States”).
## The four filters
[Section titled “The four filters”](#the-four-filters)
There are four independent filters. Each is optional, and you can combine them — for example, show a campaign only to visitors physically located in Canada *and* browsing in French.
| Filter | What it’s based on | Source |
| ------------------------------------ | ---------------------------------------------------- | --------------------------------------- |
| **Based on visitor’s location** | The visitor’s country, derived from their IP address | Quikly geolocation (server-side) |
| **Based on store’s selected market** | The Shopify market the visitor is browsing in | Shopify Markets |
| **Based on language** | The visitor’s selected storefront language | Shopify Markets / translated storefront |
| **Based on currency** | The store’s currency for the visitor | Shopify |
### How Quikly gets these values
[Section titled “How Quikly gets these values”](#how-quikly-gets-these-values)
Quikly reads three of these values directly from your Shopify storefront through the Quikly theme app embed:
```liquid
localization: {
country_code: {{ localization.country.iso_code | json }}, // store's selected market
language_code: {{ localization.language.iso_code | json }}, // language
currency_code: {{ shop.currency | json }} // currency
}
```
* **Store’s selected market**, **language**, and **currency** come from Shopify’s `localization` object — i.e. whatever market, language, and currency the visitor has selected (or been assigned) in your storefront. These reflect the visitor’s *storefront selection*, which Shopify drives from their location, browser settings, and any selectors you’ve added.
* **Visitor’s location** is different: it is resolved by Quikly from the visitor’s **IP address**, independent of Shopify. Use this when you care about where the visitor physically is, regardless of which market or currency they happen to be browsing in.
Note
“Based on visitor’s location” (IP geolocation) and “Based on store’s selected market” can return different countries for the same visitor — for instance, a visitor in Germany who has manually switched your storefront to the United States market. Pick the one that matches your intent.
## How include and exclude interact
[Section titled “How include and exclude interact”](#how-include-and-exclude-interact)
For each filter:
* **Exclude takes precedence.** If a visitor matches a “Hide in/for these…” value, the campaign is hidden — even if they also match an included value.
* If you set **only** an include list, the campaign shows only to matching visitors and is hidden for everyone else.
* If you set **only** an exclude list, the campaign shows to everyone *except* the excluded visitors.
* Leaving both fields empty disables that filter entirely.
When multiple filters are enabled, a visitor must pass **all** of them for the campaign to appear.
## Requirements & notes
[Section titled “Requirements & notes”](#requirements--notes)
* **Market, language, and currency filters depend on Shopify.** The values are only meaningful if your store uses [Shopify Markets](https://www.shopify.com/markets) (for markets and currency) and/or offers multiple storefront languages. If your store sells in a single market, language, and currency, those three filters won’t meaningfully segment your traffic — use **Based on visitor’s location** instead.
* **Visitor’s location works on any store** because it relies on IP geolocation rather than Shopify configuration.
* Countries are selected by name; the value stored is the ISO country code (e.g. `US`). Languages and currencies use their ISO codes as well (e.g. `EN`, `USD`).
* In the **campaign editor/preview**, these filters are bypassed so you can always see your campaign while building it.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Campaign Visibility Controls](/shopify/tutorials/visibility-controls) — URL rules, visitor limits, visibility percentage, and component-level filters
* [Show or Hide Components on Specific Products](/shopify/tutorials/product-page-targeting)
# Managing a Live Campaign
> Pause a published campaign to take it off your storefront, then re-publish it whenever you're ready.
Once a campaign is published, you can still manage it without taking it down — pause it from your storefront, or adjust tier quantities while it runs. This tutorial covers both.
## Pause a Published Campaign
[Section titled “Pause a Published Campaign”](#pause-a-published-campaign)
If you no longer want a campaign active on your site, you don’t need to delete it — you can pause it. Pausing hides the campaign from visitors and returns it to draft mode, and you can re-publish it at any time to make it live again.
1. Open the campaign you want to take down to its **campaign detail page**.
2. Click **Pause campaign** in the header. The status badge changes from **Active**, the campaign stops showing to visitors, and it returns to draft.

When you’re ready to bring it back, return to the same page and re-publish it — the campaign goes live again with its existing settings.
## Change Tier Quantities While Live
[Section titled “Change Tier Quantities While Live”](#change-tier-quantities-while-live)
You *can* change the quantity available for an offer that hasn’t been fully claimed, but we don’t recommend it — keeping quantities fixed protects the authenticity of the promotion. If you do need to adjust a quantity on a running campaign:
1. From the **campaign overview page**, next to **Offer configuration**, click **View All**.
2. Next to the offer level you want to modify, click it to expand the details.
3. Update the **quantity**, then click **Save Offer**.
### What happens when you change a live quantity
[Section titled “What happens when you change a live quantity”](#what-happens-when-you-change-a-live-quantity)
Because customers may have already claimed, Quikly shows a **confirmation prompt** describing the impact, and the change isn’t applied until you acknowledge it:
* **Increasing the quantity** creates new offers and adds them to the claim queue, so they become available right away.
* **Decreasing the quantity** removes only **unclaimed** offers. Customers who have already claimed keep their offer — you can’t reduce a tier below the number that has already been claimed.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [How to Duplicate a Campaign](/shopify/tutorials/duplicate-campaign)
* [Campaign Visibility Controls](/shopify/tutorials/visibility-controls) — Hide a live campaign on specific pages without pausing it entirely
* [Configuring Offers](/shopify/tutorials/offer-configuration)
# Configuring Offers
> How to configure which products your Quikly discount applies to, including how to exclude specific items
Quikly uses Shopify’s native discount codes, so discounts follow Shopify’s standard discount rules. Within each offer level in your Quikly campaign, you can configure which products the discount applies to.
## Configuring Discount Scope
[Section titled “Configuring Discount Scope”](#configuring-discount-scope)
1. Navigate to **Build > Offers** in your Quikly campaign.
2. For each offer level, locate the **“Discount applies to”** dropdown.

3. Select one of the following options:
* **All Products** - The discount applies to every item in the cart
* **Specific products** - The discount applies only to selected products
* **Specific collections** - The discount applies only to items within selected collections
4. If you selected “Specific products” or “Specific collections”, click **Browse** to open the selection modal.

5. Check the boxes next to the products or collections you want included, then save your changes.
## Excluding Items from a Discount
[Section titled “Excluding Items from a Discount”](#excluding-items-from-a-discount)
Shopify discount codes don’t support “exclude” rules directly (for example, you cannot simply exclude fleece items). Instead, discounts can only be configured to **apply to** specific products or collections.
To effectively exclude certain items from a discount, the recommended approach is to create a collection in Shopify that includes all products eligible for the discount, while ensuring the excluded items are not part of that collection.
### Creating a Smart Collection in Shopify
[Section titled “Creating a Smart Collection in Shopify”](#creating-a-smart-collection-in-shopify)
A smart collection automatically includes products based on conditions you define. This is the recommended way to create an exclusion collection.
1. In your Shopify Admin, go to **Products > Collections**.
2. Click **Create collection**.
3. Under **Collection type**, select **Smart**.

4. Under **Conditions**, set up rules that include all products except the ones you want to exclude. For example:
* **Product tag** is not equal to `new-launch`
* **Product tag** is not equal to `sale-exclusion`
* **Product type** is not equal to `Gift Cards`
You can combine multiple conditions using “Products must match: **all conditions**” to create precise exclusion rules.

5. Give your collection a descriptive name (e.g., “Discount Eligible Products”).
6. Save the collection.
Note
This collection does not need to be published to any sales channels. It only needs to exist in Shopify for the discount to reference it. You can leave all sales channels unchecked under **Publishing**.
7. Return to your Quikly campaign, and under **“Discount applies to”**, select **Specific collections** and choose your newly created smart collection.
This approach ensures that only eligible products receive the discount while the excluded items remain at full price.
# How to Upgrade Your Quikly Subscription
> Step-by-step guide to upgrading your Quikly plan through the Shopify Admin
[Your browser does not support the video tag.](https://d1kt5al5rlsv0i.cloudfront.net/quikly.github.io/videos/QuiklyPlanSelection.mp4)
## Steps
[Section titled “Steps”](#steps)
1. From the Shopify Admin area, click on **Apps**, then select **“App and sales channel settings”**

2. Click on **Quikly** from the app list.

3. Click **“Manage”** under Billing and usage charges

4. Select the appropriate plan based on your store size, and choose monthly or yearly billing.

5. Click **“Approve”** on the following page.

# Add an Image to the Popup
> Add an image to your popup from the Design Editor, choose it from your library or upload a new one, and adjust how it sizes, crops, and aligns.
You can add an image to your popup — or any component — from the Design Editor, then control how it sizes, crops, and aligns.
## Add the Image Content
[Section titled “Add the Image Content”](#add-the-image-content)
1. In the Design Editor, click **Components**, then click the component you want to modify. For this example, we’ll use the **Popup**.
2. Click the **pencil** icon to edit.
3. Add an Image content block one of two ways:
* Click **Add Content** and choose **Image** from the content types, or
* Click an existing image in the content stack to edit it.

4. In the Image content block, click **Select image**.

5. The **Choose from Library** window opens. Pick an existing image from your library, or click **Upload new** to add one to your account.

## Adjust How the Image Displays
[Section titled “Adjust How the Image Displays”](#adjust-how-the-image-displays)
Once an image is selected, the Image block exposes controls for fit and styling:
* **Expand to container edges** — Makes the image span the full popup width by ignoring the side padding. (Leave it off to keep the image inset within the popup’s padding.)
* **Size** — Set the image’s width and height. You can also set an **aspect ratio** to crop the image to a fixed shape; when an aspect ratio is set, the image fills the frame (cover) and is centered by default, with controls to change how it’s fitted and positioned within the crop.
* **Layout** — Align the image left, center, or right within the popup.
* **Borders** — Round the corners (border radius) and add a border color.
* **Background** and **Spacing** — Set a background color behind the image, and adjust margins and padding around it.
Click **Save** when you’re done, and preview the popup to confirm the image looks right on both desktop and mobile.
## Image Specifications
[Section titled “Image Specifications”](#image-specifications)
When uploading a new image to your library:
* **File type:** Any image format. The uploader shows PNG, JPG, and GIF, and also accepts other image formats such as SVG and WebP.
* **Maximum file size:** 10 MB.
There’s no fixed dimension requirement, but for crisp results upload an image at least as large as the space it will fill in the popup, and use the **Size** and aspect-ratio controls above to fit it. Smaller, web-optimized files also keep the popup loading fast.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Insert Dynamic Offer Text in the Popup](/shopify/tutorials/dynamic-offer-tokens)
* [Position and Layer the Teaser and Popup](/shopify/tutorials/positioning-teaser-and-popup)
# How to Position the Banner Component
> Learn how to position and customize the banner component in your Shopify store.
Coming Soon
This tutorial will show you how to position and customize the Quikly banner component in your Shopify store for optimal visibility and conversion.
## What You’ll Learn
[Section titled “What You’ll Learn”](#what-youll-learn)
* How to position the banner on different pages
* Customizing banner appearance and styling
* Mobile vs desktop positioning considerations
* Best practices for banner placement
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Position and Layer the Teaser and Popup](/shopify/tutorials/positioning-teaser-and-popup) - Adjust offset and Z-Index for the Teaser and Popup
* [How to Duplicate a Campaign](/shopify/tutorials/duplicate-campaign)
* [How to Use the Advanced Editor](/shopify/tutorials/advanced-editor) - Coming Soon
# Position and Layer the Teaser and Popup
> Adjust the vertical offset, Z-Index, and mobile sizing of the Teaser and Popup components so they sit in the right place — and at the right size — on your storefront.
The **Teaser** and **Popup** components share two styling controls that determine where they appear on the page:
* **Offset** — how far the component sits from the edge of the screen (Spacing).
* **Z-Index** — whether the component stacks in front of or behind other on-page elements (Layout).
Both controls live in the same place for each component: **Build > Components > \[Teaser or Popup] > Edit > Styles > Display**. They’re [component-level](/shopify/tutorials/visibility-controls#component-level-controls) styling options, so adjusting one placement doesn’t affect the rest of the campaign.
## Adjusting the Vertical Offset
[Section titled “Adjusting the Vertical Offset”](#adjusting-the-vertical-offset)
The vertical offset controls the distance between the component and the edge of the screen it’s anchored to. A separate **Vertical (Mobile)** field lets you tune mobile independently, since storefronts often have different headers, footers, and safe areas on smaller screens.
1. Go to **Build > Components**.
2. Click the **Teaser** (or **Popup**) row, then click **Edit**.
3. Open the **Styles** tab.
4. Expand **Display**, then expand **Spacing**.
5. Under **Offset**, update **Vertical** for desktop and **Vertical (Mobile)** for mobile. You can also tune **Horizontal** here if needed.

6. Click **Save** and preview the storefront on both desktop and mobile to confirm placement.
Values accept any standard CSS length unit — `px` and `rem` are the most common.
## Adjusting the Z-Index
[Section titled “Adjusting the Z-Index”](#adjusting-the-z-index)
Z-Index controls stacking order: a higher number sits in front, a lower number sits behind. Quikly components are designed to sit on top of your storefront so they stay visible, but occasionally one overlaps a piece of your theme’s UI that should win instead — most commonly the **cart drawer on mobile**, but also sticky headers, chat widgets, and cookie banners.
Every Quikly component defaults to a Z-Index of **1001**, which keeps it above typical storefront content. To push a component *behind* a theme element, set its Z-Index **below** that element’s z-index.
### Example: Teaser Behind the Mobile Cart Drawer
[Section titled “Example: Teaser Behind the Mobile Cart Drawer”](#example-teaser-behind-the-mobile-cart-drawer)
When a shopper opens the cart drawer on mobile, you usually want the drawer in front and the teaser tucked behind it.
1. Go to **Build > Components**.
2. Click the **Teaser** row, then click **Edit**.
3. Open the **Styles** tab.
4. Expand **Display**, then expand **Layout**.
5. Set **Z-Index** to a value below your cart drawer’s z-index. In the example below it’s set to `999`, just under the `1001` default.

6. Click **Save**, then open your storefront on mobile and open the cart drawer to confirm the teaser now sits behind it.
### Finding the Right Value
[Section titled “Finding the Right Value”](#finding-the-right-value)
The number that works depends on the z-index your theme assigns to the element you’re layering against:
* **To go behind an element**, set Quikly’s Z-Index lower than that element’s.
* **To stay in front**, leave the default (1001) or raise it.
If you’re not sure what z-index your theme uses for the cart drawer or header, inspect that element in your browser’s dev tools and read its `z-index` value, then set the Quikly component just below it. Many Shopify themes use values in the low thousands for drawers and overlays, so a value like `999` is a safe starting point for sending a component behind them.
## Advanced: Override the Offset on Specific Templates
[Section titled “Advanced: Override the Offset on Specific Templates”](#advanced-override-the-offset-on-specific-templates)
The Offset fields apply globally to the component. If you need a different offset on just one template — for example, more breathing room on the product page so the teaser clears a sticky “Add to Cart” bar — you can override the CSS variable for that template only.
1. Go to **Build > Styles > Theme CSS**.
2. Add a rule that targets the template’s body class and the component’s position class. For example, to set the mobile vertical offset to `115px` on the product page when the Teaser is positioned in the bottom-left:
```css
html body:not(.q_layout_root).product-page .q_Panel_position_bottomLeft {
--q-component-mobileOffsetVertical: 115px !important;
}
```
3. Click **Save** and preview the product page on mobile to confirm.
This assumes the product page renders with a `product-page` class on the `` tag. Most Shopify themes do, but if yours doesn’t, inspect the page in your browser’s dev tools and substitute whatever class the template actually uses. Adjust `.q_Panel_position_bottomLeft` if your component is anchored to a different corner.
## Making the Popup More Compact on Mobile
[Section titled “Making the Popup More Compact on Mobile”](#making-the-popup-more-compact-on-mobile)
If the popup feels too large on phones, there are two ways to slim it down.
### Hide Bulky Content on Mobile with a Container
[Section titled “Hide Bulky Content on Mobile with a Container”](#hide-bulky-content-on-mobile-with-a-container)
Wrap the content you want to drop on small screens — for example, a next-up offer display — in a **Container** block, then hide that container on mobile. The rest of the popup stays intact; only the wrapped content is removed on phones, so the popup renders more compactly.
1. In the Design Editor, open the popup for editing and select the **Container** block that holds the content you want to hide.
2. Open the **Styles** tab and expand **Visibility**.
3. Set the visibility option to the breakpoint where the container should disappear:
* **Mobile (≤480px)** — hides the container on phones.
* **Tablet and below (≤768px)** — hides it on tablets and phones.
* **Always show** — the default; the container shows on every screen size.

4. Click **Save** and preview on a phone to confirm the popup is more compact.
### Resize the Teaser
[Section titled “Resize the Teaser”](#resize-the-teaser)
You can also shrink the **Teaser** directly:
1. Go to **Build > Components > Teaser** and click the **Edit** (pencil) icon.
2. Open the **Styles** tab and look under the display options for the teaser’s **width** and **height**.
3. Adjust the size, **Save**, and preview on mobile.
## Teaser vs. Popup
[Section titled “Teaser vs. Popup”](#teaser-vs-popup)
The Teaser and Popup expose the same Offset and Z-Index controls in the same locations, so the steps above apply identically to both. Adjust them independently — each component has its own Styles, so changing the Teaser’s offset or Z-Index doesn’t affect the Popup.
## When to Use Z-Index vs. Hiding the Component
[Section titled “When to Use Z-Index vs. Hiding the Component”](#when-to-use-z-index-vs-hiding-the-component)
If you want the component gone entirely on certain pages or states rather than just layered behind something, use [visibility controls](/shopify/tutorials/visibility-controls) instead — for example, hiding a placement on the cart template. Z-Index is the right tool when the component should still be present, just not on top.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Campaign Visibility Controls](/shopify/tutorials/visibility-controls) — Show or hide placements by page, template, or product
* [How to Position the Banner Component](/shopify/tutorials/position-banner)
* [Show or Hide Components on Specific Products](/shopify/tutorials/product-page-targeting)
# Show or Hide Components on Specific Products
> Learn how to control which product pages display your Quikly components
You can configure Quikly components to appear on specific product pages or be hidden from certain products. This is useful when you want to target promotions to particular products or exclude components from pages where they don’t apply.
This is one of several [component-level visibility controls](/shopify/tutorials/visibility-controls#component-level-controls) available for fine-tuning where your placements appear.
## Accessing Component Settings
[Section titled “Accessing Component Settings”](#accessing-component-settings)
1. Navigate to **Build > Components** in your Quikly campaign.
2. Find the component you want to configure (i.e. popup or banner).
3. Click the **pencil icon** to enter Edit mode.
4. Click the **Settings** tab.

## Show Only on Specific Products
[Section titled “Show Only on Specific Products”](#show-only-on-specific-products)
Use this setting to display a component only on certain product pages.
In the **“Show only on Specific Products”** field, enter a comma-separated list of product handles or patterns.
> Only show this component on products matching these handles. Supports wildcards (e.g., ‘swoop-\*’). Comma separated list of product handles or patterns.
**Examples:**
* `blue-widget` - Show only on the “Blue Widget” product page
* `swoop-*` - Show on all products with handles starting with “swoop-”
* `blue-widget, red-widget, green-widget` - Show on these three specific products
## Hide on Specific Products
[Section titled “Hide on Specific Products”](#hide-on-specific-products)

Use this setting to hide a component from certain product pages while showing it everywhere else.
In the **“Hide on Specific Products”** field, enter a comma-separated list of product handles or patterns.
> Do not show this component on products matching these handles. Supports wildcards (e.g., ‘swoop-\*’). Comma separated list of product handles or patterns.
**Examples:**
* `gift-card` - Hide on the gift card product page
* `bundle-*` - Hide on all bundle products
* `clearance-item-1, clearance-item-2` - Hide on specific clearance items
## Finding Product Handles
[Section titled “Finding Product Handles”](#finding-product-handles)
A product handle is the URL-friendly version of your product title. You can find it in:
1. **Shopify Admin**: Go to **Products**, click on a product, scroll down to **Search engine listing**, and look at the URL handle.
2. **Product URL**: The handle is the last part of your product URL. For example, in `yourstore.com/products/blue-widget`, the handle is `blue-widget`.
## Using Wildcards
[Section titled “Using Wildcards”](#using-wildcards)
Wildcards (`*`) match any characters, making it easy to target groups of products:
| Pattern | Matches |
| ---------- | ------------------------------------------------- |
| `summer-*` | `summer-dress`, `summer-hat`, `summer-sale-item` |
| `*-bundle` | `starter-bundle`, `deluxe-bundle`, `gift-bundle` |
| `*sale*` | `summer-sale`, `sale-item`, `clearance-sale-2024` |
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Campaign Visibility Controls](/shopify/tutorials/visibility-controls) - All visibility options including URL rules, template filtering, and more
* [Configuring Offers](/shopify/tutorials/offer-configuration) - Control which products discounts apply to
* [How to Position the Banner Component](/shopify/tutorials/position-banner)
# Test Offer Codes Before Launch
> Activate your discount codes ahead of a scheduled campaign so you can verify they work in your store before going live.
If you want to confirm your discount codes work — and see how they combine with your other offers — before a campaign goes live, you can activate the codes ahead of the launch date.
## Activate Codes Before Launch
[Section titled “Activate Codes Before Launch”](#activate-codes-before-launch)
1. On the campaign overview screen, find the **Timing** card and click **Edit**.
2. Under **Timing > Starts**, check the box next to **Activate discount codes before campaign launches**.

3. Save the change. Your discount codes are now active in the store, so you can test them before the campaign’s start date.
This lets you verify that a code’s configuration is correct and check how it combines with your other existing offers.
Caution
While this box is checked, the codes are genuinely active and valid. If anyone knows a code before the campaign’s start date, they can use it. Once your testing is complete, you may want to uncheck **Activate discount codes before campaign launches** until launch.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Configuring Offers](/shopify/tutorials/offer-configuration) — Set which products each discount applies to
* [Managing a Live Campaign](/shopify/tutorials/managing-live-campaign)
* [How to Duplicate a Campaign](/shopify/tutorials/duplicate-campaign)
# Campaign Visibility Controls
> Control where and how your campaigns appear using campaign-level and component-level visibility settings
Quikly offers visibility controls at two levels: **campaign-level** controls that affect the entire campaign, and **component-level** controls that let you customize individual placements.
## Campaign-Level Controls
[Section titled “Campaign-Level Controls”](#campaign-level-controls)
These controls affect the entire campaign — all placements, all components.
| Control | Persistence | Use Case |
| ------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------- |
| **URL Rules (Visitor Session)** | Cookie-based (session) | Qualify visitors based on entry page (e.g., “only show to visitors who landed on /promo”) |
| **Page URL Rules** | Per page view | Control which pages the campaign appears on site-wide |
| **Visibility Percentage** | Cookie-based (session) | A/B testing — show campaign to X% of visitors |
| **Visitor Limit** | Server-side | Cap total participants |
### Accessing Campaign-Level Settings
[Section titled “Accessing Campaign-Level Settings”](#accessing-campaign-level-settings)
1. Open your campaign in the Quikly dashboard.
2. Navigate to the **Editor > Launch** page.
3. Look for the **Visibility** section.
### URL Rules (Visitor Session)
[Section titled “URL Rules (Visitor Session)”](#url-rules-visitor-session)
Use this to qualify visitors based on how they arrived at your site. Once a visitor qualifies, they’ll see the campaign on every page during their session.
**Example:** You’re running a sitewide promotion but only want visitors who entered through a specific landing page to see it. Set a visitor session rule for `/promo` — once qualified, they’ll see the campaign everywhere.
### Page URL Rules
[Section titled “Page URL Rules”](#page-url-rules)
Use this to control which pages display the campaign. Unlike visitor session rules, these are evaluated on every page view.
* **Show only on pages matching** — Campaign only appears on matching pages
* **Hide on pages matching** — Campaign is hidden on matching pages
## Component-Level Controls
[Section titled “Component-Level Controls”](#component-level-controls)
These controls affect individual placements — you can show different placements on different pages within the same campaign.
| Control | Use Case |
| ------------------------------ | --------------------------------------------------------- |
| **Show only on Specific URLs** | Show this placement only on matching pages |
| **Hide on Specific URLs** | Hide this placement on matching pages |
| **Template Filtering** | Show/hide on Shopify template types (cart, product, etc.) |
| **Product Handle Filtering** | Show/hide on specific product pages |
### Accessing Component-Level Settings
[Section titled “Accessing Component-Level Settings”](#accessing-component-level-settings)
1. Navigate to **Build > Placements** in your campaign.
2. Click the **pencil icon** on the placement you want to configure.
3. Look for the filtering fields in the settings panel.
### URL Filtering
[Section titled “URL Filtering”](#url-filtering)
Control where a specific placement appears based on URL patterns:
* **Show only on Specific URLs** — Only display this placement on pages where the URL contains the specified patterns
* **Hide on Specific URLs** — Hide this placement on pages where the URL contains the specified patterns
**Example:** You have a campaign with a banner and a popup. You want the banner on all pages, but the popup only on product pages. Use URL filtering on the popup to show only on `/products/`.
### Template Filtering
[Section titled “Template Filtering”](#template-filtering)
Filter by Shopify template type instead of URL pattern. Useful for targeting:
* Product pages
* Collection pages
* Cart page
* Blog posts
### Product Handle Filtering
[Section titled “Product Handle Filtering”](#product-handle-filtering)
Show or hide placements on specific product pages by handle. See [Show or Hide Components on Specific Products](/shopify/tutorials/product-page-targeting) for details.
## When to Use Which
[Section titled “When to Use Which”](#when-to-use-which)
| Scenario | Recommended Control |
| ------------------------------------------------------- | ---------------------------------------------------------- |
| ”Only show campaign to visitors from email links” | Campaign: Visitor Session URL Rules |
| ”Show campaign on product pages only” | Campaign: Page URL Rules |
| ”Show banner everywhere, popup only on /collections/\*“ | Component: URL Filtering on popup |
| ”A/B test: show to 50% of visitors” | Campaign: Visibility Percentage |
| ”Hide campaign on cart/checkout” | Campaign: Page URL Rules OR Component: Template Filtering |
| ”Different offers on different product categories” | Multiple campaigns, or Component: Product Handle Filtering |
## Key Differences
[Section titled “Key Differences”](#key-differences)
| Aspect | Campaign-Level | Component-Level |
| --------------------- | -------------------------- | ------------------------------- |
| Affects | All placements | Single placement |
| Visitor Session rules | Remembered via cookie | N/A |
| Flexibility | All-or-nothing | Fine-grained per placement |
| Setup complexity | Simpler for sitewide rules | More flexible for mixed layouts |
## URL Pattern Matching
[Section titled “URL Pattern Matching”](#url-pattern-matching)
Both campaign and component URL filters use the same pattern matching:
### Substring Matching
[Section titled “Substring Matching”](#substring-matching)
Patterns match if they appear anywhere in the URL.
| Pattern | Matches | Doesn’t Match |
| --------------------- | ------------------------------------------------- | ---------------------- |
| `snowboard` | `/products/snowboard-bundle`, `/snowboard-pro` | `/products/skateboard` |
| `/products/` | `/products/anything`, `/products/bundle-123` | `/collections/winter` |
| `/collections/winter` | `/collections/winter`, `/collections/winter-sale` | `/collections/summer` |
### Wildcards
[Section titled “Wildcards”](#wildcards)
Use `*` as a wildcard to match any characters:
| Pattern | Matches |
| --------------------- | ------------------------------------------------------ |
| `/products/*-bundle` | `/products/snowboard-bundle`, `/products/ski-bundle` |
| `/collections/*/sale` | `/collections/winter/sale`, `/collections/summer/sale` |
| `*.myshopify.com` | `store.myshopify.com`, `test.myshopify.com` |
### Multiple Patterns
[Section titled “Multiple Patterns”](#multiple-patterns)
Separate multiple patterns with commas:
```plaintext
/products/*, /collections/winter, snowboard
```
This matches any URL containing `/products/`, `/collections/winter`, OR `snowboard`.
### Pattern Notes
[Section titled “Pattern Notes”](#pattern-notes)
* Patterns are **case-insensitive**
* Patterns match against the **full URL** including protocol and domain
* In the **campaign editor/preview**, URL filtering is bypassed so you can always see your placements
## Evaluation Order
[Section titled “Evaluation Order”](#evaluation-order)
A placement only appears if it passes all three levels:
1. **Campaign-level visitor session rules** — Is this visitor qualified? (cookie persisted)
2. **Campaign-level page rules** — Should the campaign show on this page?
3. **Component-level filters** — Should this specific placement show on this page?
At each level, exclude rules are checked first. If any exclude pattern matches, the placement is hidden. Then include rules are checked — if specified, at least one must match.
## Related Tutorials
[Section titled “Related Tutorials”](#related-tutorials)
* [Show or Hide Components on Specific Products](/shopify/tutorials/product-page-targeting) - Filter by product handle
* [Position and Layer the Teaser and Popup](/shopify/tutorials/positioning-teaser-and-popup) - Adjust vertical offset and Z-Index to position components and layer them behind theme UI
* [How to Position the Banner Component](/shopify/tutorials/position-banner)
# Klaviyo
To configure SMS integration between your Quikly activation and [Klaviyo](https://www.klavyio.com), please provide your Client Success manager the following information:
* [An api key](https://help.klaviyo.com/hc/en-us/articles/115005062267-Manage-Your-Account-s-API-Keys#generate-a-private-api-key3)
* [The list id of your SMS subscriber list](https://help.klaviyo.com/hc/en-us/articles/115005078647-How-to-find-a-list-ID)
It is also important that you confirm that double-opt in is turned on for this subscriber list. [More on double-opt-in.](https://help.klaviyo.com/hc/en-us/articles/115005251108-Understanding-the-double-opt-In-process)
# SMS Integration
> Quikly can integrate with your mobile marketing platform.
Quikly activations are a great way to gain SMS subscribers. You can motivate participants to sign up for your SMS program in exchange for a heads-up about an upcoming live release in a Hype campaign, or provide an immediate incentive with Swap.
For SMS acquisition, Quikly will integrate with your mobile marketing platform so that participants can fully opt-in to your SMS marketing list.
At a minimum, Quikly requires the ability to add a phone number as a subscriber via an API.
In addition, we also strongly recommend the ability to do a lookup on an existing phone number (so credit can be given in a campaign in realtime) and the ability to confirm the double-opt-in process for new subscribers so that we can refrain from granting time until the participant fully opts-in.
## Integrated Platforms
[Section titled “Integrated Platforms”](#integrated-platforms)
Quikly has integrated with the following platforms. Our team is able to integrate with additional providers not listed here.
* [Klaviyo](/sms-integration/klaviyo)
* [Mobivity](https://www.mobivity.com)
* [Paytronix](https://www.paytronix.com/platform/crm/)
* [Responsys](https://docs.oracle.com/en/cloud/saas/marketing/responsys-user/SMS_Overview.htm)
* [Salesforce Marketing Cloud](https://www.salesforce.com/products/marketing-cloud/overview/)
* [Vibes](/sms-integration/vibes)
# Vibes
To configure SMS integration between your Quikly activation and [Vibes](https://www.vibes.com), please provide your Client Success manager the following information:
* [Company ID](https://developer.vibes.com/display/APIs/Vibes+Account+Manager)
* [Acquisition ID](https://developer.vibes.com/display/APIs/Acquisition)
* A username and password