How to setup Instant Winback Offer on your storefront?

This article explains how to integrate and implement the Instant Winback Offer experience in your custom or headless customer portal using Loop Storefront APIs.

Before you start

  1. Setting up the Instant Winback Offer experience requires using the Loop Storefront APIs listed below.
  2. Ensure that Instant Winback offers are configured and active in the Loop admin portal before implementing the storefront logic.

Pre-requisites

  • Configure Instant Winback offers in Loop admin.
  • Ensure the offer configuration is active for the relevant subscription plans.
  • Instant Winback requires two steps on the storefront: fetching the offer, and accepting it on customer action.
  • Keep the subscriptionId ready — this is required for both API calls.
  • This flow only applies to subscriptions that have already been cancelled.

The Instant Winback flow

  1. Customer cancels a subscription on the storefront.
  2. Immediately after cancellation, call the List Instant Winback Offer API using the cancelled subscriptionId.
  3. If an offer is returned, render the offer banner and details drawer using the response copy (banner text, expiry text, drawer title/body, CTAs).
  4. If no offer is returned (NO_OFFER_AVAILABLE), skip rendering the banner entirely — this is not an error state.
  5. When the customer taps the Reactivate subscription CTA, call the Apply Instant Winback Offer API, passing the discountIncentiveId received in step 2 as instantWinbackOfferId.
  6. On success, the subscription is reactivated with the discount applied
  7. On failure (offer expired, already redeemed, etc.), handle the specific error code returned and prompt the customer accordingly (e.g., re-fetch a fresh offer, or show a generic failure state).

Step 1 — Fetch the instant winback offer

This endpoint returns the winback offer applicable for a cancelled subscription, along with all copy needed to render the banner and drawer.

Endpoint payload (what it returns)

  • discountIncentiveId: Unique identifier for the offer. Pass this value into the Apply Instant Winback Offer API to accept the offer.
  • banner.text: Customer-facing banner headline. Render this directly in the UI.
  • banner.expiryText: Text describing when the offer expires.
  • drawer.title: Title shown in the expanded offer details drawer.
  • drawer.body: Body copy for the offer details drawer.
  • drawer.acceptCta: Label for the accept-offer button.
  • drawer.closeCta: Label for the close/dismiss button.

If the subscription is not eligible for any offer, the API returns data: null with the code NO_OFFER_AVAILABLE — no banner should be rendered in this case.


Step 2 — Apply the instant winback offer

This endpoint reactivates the cancelled subscription with the winback discount applied, once the customer accepts the offer.


Endpoint payload (what it returns)

  • discountIncentiveId: Unique identifier for the offer. Pass this value into the Apply Instant Winback Offer API to accept the offer.
  • cancelReasonTitle: Title text associated with the cancellation (e.g. "Subscription cancelled").
  • shortDescription: Optional short-form description of the offer. May be null.
  • body: Rich HTML content for the offer — includes the customer greeting, offer messaging, and incentive details. Render this directly as HTML (e.g. via dangerouslySetInnerHTML in React) after replacing merge tags.
  • offerExpiryText: HTML snippet showing the offer's expiry messaging. Contains a {{timer}} merge tag.
  • winbackExpiryHours: Number of hours from when the offer is fetched until it expires. Use this to calculate and render the live countdown that replaces {{timer}} in body and offerExpiryText.
  • keepSubscriptionCTAText: Label for the button that reactivates the subscription with the offer applied.
  • cancelSubscriptionCTAText: Label for the button that dismisses the offer and confirms cancellation.
  • mobileBannerUrl: Banner image URL to display on mobile viewports.
  • desktopBannerUrl: Banner image URL to display on desktop viewports.
  • context: Internal configuration describing the discount that will be applied on reactivation (e.g. discount type, value, recurring cycle limit). This is used by Loop's backend when the offer is accepted — it does not need to be rendered on the storefront.

If the subscription is not eligible for any offer, the API returns data: null with the code NO_OFFER_AVAILABLE — no banner should be rendered in this case.

📘

Merge tags in body and offerExpiryText

Before rendering, replace the following placeholders with actual values:

  • {{customer_first_name}} — the customer's first name
  • {{store_name}} — the merchant's store name
  • {{timer}} — a live countdown, calculated from winbackExpiryHours at the time the offer was fetched

Step 2 — Apply the instant winback offer

This endpoint reactivates the cancelled subscription with the winback discount applied, once the customer accepts the offer.

Endpoint payload (what it returns)

  • success: true on a successful reactivation.
  • message: Confirmation message (e.g. "Subscription reactivated with winback offer applied").
  • data: Always null on success — this endpoint does not return subscription details in the response.
  • code: SUCCESS on a successful reactivation.


Implementation notes

  • Call the List Instant Winback Offer API immediately after cancellation — don't wait for a separate customer action to check eligibility.
  • Only render the offer banner when data is non-null. A NO_OFFER_AVAILABLE response is expected behavior if the cancelled subscription does not have any valid instant winback offer, not an error, and should fail silently on the UI.
  • If a cancelled subscription is eligible for multiple winback offers, this API returns only the offer with the highest priority, as configured in your Loop admin portal.
  • Use mobileBannerUrl or desktopBannerUrl based on the customer's viewport.
  • The body field contains sanitized HTML from Loop's admin portal — render it directly, but always replace merge tags first.
  • Use winbackExpiryHours to run a client-side countdown timer. Once it hits zero, treat the offer as expired on the frontend and avoid calling the Apply API — a re-fetch via the List API will confirm whether a new offer is available.
  • The context field reflects the discount configuration Loop will apply on the backend — it's informational and not required for rendering the storefront UI.
  • On a successful Apply call, do not expect subscription details in the response — rely on the SUCCESS code to drive your confirmation UI, and fetch updated subscription data separately if your portal needs to display it.

Did this page help you?