Win back subscribers
Offer a discount to people who turn off renewal or whose subscription ended.
A win-back offer is a discounted price, such as 50% off three months, shown to someone who turned off renewal or whose subscription ended. You create the discount in each store, map it to your product as an offer, and present it when Voidhash reports the cancellation.
What you need
- A subscription product, such as
pro_monthly, mapped on the App Store and Google Play. - The App Store's In-App Purchase key in your App Store settings; Voidhash signs promotional offers with it. See Connect the App Store (Help center).
- A way to reach the person: your app, a push notification or your own email.
Create the offer in each store
- App Store: add a promotional offer to the subscription in App Store Connect. Apple shows promotional offers only to people who have subscribed before, which fits win-back.
- Google Play: add a developer offer to the base plan, with eligibility you choose in Play Console.
Map it to your product
In Studio, open the product, edit its App Store mapping and add a row under Promotional offers;
edit its Google Play mapping and add a row under Offers. Give both rows the same Offer ID,
such as winback_50, and enter each store's own offer identifier beside it.
Decide when to offer it
Voidhash records $subscription.canceled when someone turns off renewal and
$subscription.expired when access ends. React to them in one of three ways:
- In the app: when a returning person has no active
premiumperk, show the offer on your upgrade screen. The store decides whether they are eligible. - With an automation: start a flow on the Event trigger
$subscription.canceled, wait two days, check the subscription is still not renewing, and send a push that opens your offer screen. Add$subscription.auto_renew_resumedunder Leave the flow on so nobody who resubscribes gets the push. See Automations. - From your backend: subscribe a webhook to
subscription.cancelledandsubscription.expired, and send your own email.
Automations and push notifications are in early access.
Buy the offer
Pass the offer ID to the SDK's purchase call:
import Voidhash
import VoidhashCore
func buyWinBack(_ product: VoidhashProduct) async {
do {
try await Voidhash.shared?.purchase(product: product, offerId: "winback_50")
} catch let error as VoidhashStoreError where error.code == "OFFER_UNAVAILABLE" {
// Not eligible: show the regular price instead.
} catch {
print("Purchase failed: \(error)")
}
}import com.voidhash.sdk.VoidhashException
try {
voidhash.purchase(activity, product, offerId = "winback_50")
} catch (error: VoidhashException) {
if (error.code == "OFFER_UNAVAILABLE") showRegularPrice() else reportError(error)
}const result = await voidhash.client.purchase(product, { offerId: "winback_50" });
if (result.isErr() && result.error.code === "OFFER_UNAVAILABLE") showRegularPrice();A paywall can sell the offer as well: an AI agent connected to the MCP server can set the offer on the paywall's purchase. A paywall build fails if no product mapping lists the offer.
When the offer can't be bought
Buying an offer either buys exactly that offer or fails with OFFER_UNAVAILABLE. It never falls back
to the regular price, so the person is never charged more than you showed. It fails when:
- the store doesn't offer it to this person, for example because they never subscribed (App Store) or don't meet the eligibility you set (Google Play);
- the product's mapping on this store lists no such offer ID;
- the App Store isn't set up to sign offers, for example without an In-App Purchase key.
Handle it by showing the regular price, or nothing. Don't retry the same offer.
Check that it worked
A redeemed offer is an ordinary purchase of the product, so its perks return as usual. The webhook
follows the store's rules: an App Store resubscription continues the old subscription with
subscription.renewed, while Google Play starts a new one with subscription.created. Voidhash
also records $subscription.offer_redeemed for App Store redemptions. Test the whole path
with a sandbox account that has an expired subscription; see Test purchases (Help center).
Stripe and the web
Stripe discounts are coupons and promotion codes on the Checkout Session your server creates, not
Voidhash offers. Create the session with the discount and the buyer's voidhash_distinct_id; see
Where Voidhash reads the ID.
Next steps
- Offers: offers in detail.
- Subscription lifecycle: when each event fires.
- Automations
Something wrong or missing on this page? Tell us by email or on Discord.