All articles
Deep dive8 min read

Apple's Retention Messaging API puts your words on the cancel screen, but refund requests never pass through it

Apple's Retention Messaging API lets you show a message, a plan switch or a promotional offer when a subscriber taps Cancel. Here's how it works, what it costs to run, and why a refund request reaches Apple without ever touching that screen.

A person at a cafe table holding an iPhone open to a subscription settings screen, the moment Apple's Retention Messaging API is built for

Key takeaways

  • Apple's Retention Messaging API lets a developer choose the message an iPhone subscriber sees after tapping Cancel Subscription, using one of four types: text, text with an image, a switch-plan suggestion, or a promotional offer.
  • Apple's documentation still describes the Retention Messaging API as a pre-release that developers ask to join. Version 1.0 shipped on July 16, 2025, and version 1.5 followed on April 27, 2026.
  • In the real-time flow, Apple calls the developer's Get Retention Message endpoint when an active subscriber views a subscription details page with a Cancel button, and the endpoint must answer within 700 ms in production or Apple falls back to a default message.
  • A retention message only affects future renewals. A refund request goes to Apple through reportaproblem.apple.com or the in-app refund sheet, and the cancel screen where retention messages appear is not part of that path.
  • When a customer requests a refund inside an app through StoreKit's refund request API, Apple turns off auto-renew itself and sends DID_CHANGE_RENEWAL_STATUS with the subtype AUTO_RENEW_DISABLED, the same type and subtype as a cancellation from the Subscriptions settings page.
  • The developer's only input on an Apple refund is the CONSUMPTION_REQUEST, answered within 12 hours with Send Consumption Information, and only if the customer consented to share that data.
  • Google Play has no documented equivalent of a developer-chosen cancel-screen message. Developers get a SUBSCRIPTION_CANCELED notification afterward, and the userInitiatedCancellation field tells them why the subscriber left.

Subscribers who tap Cancel on an iPhone can now be shown a message you chose, a cheaper plan, or a discount, before they confirm. That's Apple's Retention Messaging API, and it's the first time a developer gets a say on Apple's own cancellation screen. It's still a pre-release you have to ask to join. It also has a hard boundary that matters if you care about refunds: it speaks to people who are about to stop paying, not to people asking for their money back. Those requests go to Apple by a different road.

What Apple's Retention Messaging API does

The Retention Messaging API is a server-to-server service. You upload message text and images to Apple ahead of time, Apple reviews them, and then you pick which approved message a subscriber sees when they're about to cancel an auto-renewable subscription. Apple's documentation lists four message types:

  • A text message, which can include bullet points.
  • A text message with an image.
  • A switch-plan message, which suggests another subscription in the same subscription group.
  • A promotional-offer message, which offers a discounted price at the same or a different tier, using a promotional offer you set up in App Store Connect and sign on your server.

The message appears after the customer taps Cancel Subscription on a subscription details page. Apple then shows a Confirm Cancellation page. The customer can still cancel, tap Keep Subscription, or take the offer or plan you suggested. You can't hide the way out, and Apple tells developers not to upload content that is misleading or inaccurate.

Default messages and the real-time flow

There are two ways to run it. The simple one is default messages: text, with or without an image or bullets, assigned to each product and locale. The richer one is the real-time flow, where Apple calls a Get Retention Message endpoint on your server and you answer with a message ID. Only the real-time flow can show switch-plan and promotional-offer messages.

Defaults aren't optional in practice. Apple uses them as the fallback whenever a real-time call fails, and it shows no retention message at all for a product in any locale that lacks a default.

ItemWhat Apple's documentation says
StatusPre-release, developers express interest to join
ProductsAuto-renewable subscriptions
Message typesText, text with image, switch plan, promotional offer
Real-time response limit700 ms in production
Production accessPass a sandbox performance test first, about 700 ms
Header and bullet text66 characters each, up to five bullets
Body text144 characters
Image alt text150 characters
Messages per app2,000
ReviewNew messages start PENDING and show only once APPROVED
DevicesiOS 15.1, iPadOS 15.1, visionOS 1, macOS 14 or later

What Apple tells your server in the real-time call

The request is a JWS signed by the App Store. Decoded, it carries the originalTransactionId, appAppleId, productId, userLocale, a requestIdentifier UUID, the environment, and signedDate. Apple tells you to check that appAppleId matches your app and not to respond if it doesn't. The originalTransactionId is the useful part, because it lets you look up what this subscriber actually did in your app before you choose what to say.

Where the retention message appears, and where it doesn't

Apple places the message on one screen, the Confirm Cancellation step after the customer taps Cancel Subscription, for example from Apple Account > Subscriptions or a subscription's page on the App Store. That screen is about whether the next renewal happens. It doesn't touch money already collected.

A refund request is a separate act. Apple's support page sends customers to reportaproblem.apple.com, where they choose "Request a refund," pick a reason and an item, and wait 24 to 48 hours for an update. Inside your app, StoreKit's beginRefundRequest opens Apple's refund sheet, and Apple says the App Store takes up to 48 hours to approve or deny. Neither path runs through the Confirm Cancellation screen.

MomentWhere it happensWhat's at stakeWho decidesYour input
CancelSubscription details page, then Confirm CancellationFuture renewalsThe customerA retention message you choose, answered within 700 ms
Refund requestreportaproblem.apple.com or your in-app refund sheetMoney already paidAppleCONSUMPTION_REQUEST data within 12 hours, if the customer consented

Can a retention message stop a refund request?

No. The refund request never reaches the screen where the message lives. There's a detail in Apple's notification docs that makes this concrete. When a customer requests a refund through your app with the refund request API, Apple turns off auto-renew on its own and sends DID_CHANGE_RENEWAL_STATUS with the subtype AUTO_RENEW_DISABLED. A customer who cancels from the Subscriptions settings page produces the same type and subtype. Your server sees one notification for two very different customers, and only one of them saw your retention message.

If the refund request goes ahead, the evidence channel is the CONSUMPTION_REQUEST. Apple sends it for refund requests on any product type, and you have 12 hours to answer with Send Consumption Information. You may only answer if the customer gave consent. After that, Apple's decision arrives as a REFUND notification, or as REFUND_DECLINED if Apple declines a refund the customer started in your app.

Does Google Play have a cancellation message API?

Not in Google's subscription lifecycle documentation. A Google Play subscriber cancels from the Play subscriptions center, keeps access until the end of the current billing cycle, and can tap Resubscribe to undo it before then, which Google calls a restore. You hear about it afterward through a SUBSCRIPTION_CANCELED notification, and the userInitiatedCancellation field in the subscription resource tells you why the user left. Pause, which Google says is enabled by default for all subscriptions, is its main built-in way to cut voluntary churn.

On the refund side, Google Play has its own evidence channel. A PendingRefundReviewNotification arrives when a chargeback needs developer review, and you have 24 hours to answer through orders.reviewrefund. For orders placed after August 3, 2026, Google passes the purchase price, less Play's service fee, plus the bank's chargeback fees to the developer when a chargeback goes through.

What the cancel screen costs you in money

Retention messaging looks free. It isn't, and the bills land in three places.

Compute on every page view

Apple calls your endpoint when an active subscriber views a subscription details page with a Cancel button, not only when they cancel. Every visit is a request your server has to authenticate, look up and answer within 700 ms, or Apple falls back to the default. That rules out a cold-starting function that sometimes takes two seconds. You're paying for capacity that stays warm, plus whatever database reads and paid third-party API calls your decision logic makes per request.

A developer at a desk watching server response time graphs on a monitor beside a laptop and an iPhone, the kind of always-on endpoint Apple's Retention Messaging API expects

The discount is revenue you chose not to collect

A promotional offer that keeps a subscriber is real money kept. One that goes to someone who would have stayed anyway is pure discount. Here's an illustration, not a benchmark. A $9.99 monthly plan offered at half price for three months gives up $14.99 per accepted offer. If one in three people who accept would have stayed at full price, a thousand acceptances cost about $5,000 in discount handed to people who never needed it. The other side of the ledger is the subscribers who really were leaving.

The refund that a rough exit invites

Apple keeps the Cancel Subscription button on the page, so you can't trap anyone, but you can still be annoying. A customer who feels pushed has another option, which is asking Apple for the last charge back. When a refund does come, the money goes back, and the compute, storage, API calls and any affiliate payouts tied to that subscriber stay spent. Those are costs no retention message recovers.

How to use retention messaging without raising your refund rate

Treat the cancel screen as a place to be accurate, not clever. A few rules keep it on the right side of the refund line:

  • Decide per subscriber. Use the originalTransactionId in Apple's request to look up recent usage. Offer a discount or a cheaper plan to people who use the product. Give a short, honest message to people who haven't opened it in weeks.
  • Keep the answer fast. Precompute the decision when your usage data changes, so the request itself is a single lookup that fits well inside 700 ms.
  • Write a default message for every product and locale. Without one, a failed call means no message at all.
  • Watch AUTO_RENEW_DISABLED closely. If a CONSUMPTION_REQUEST follows for the same originalTransactionId, you're looking at a refund request, not a cancellation, and the 12-hour clock has started.
  • Answer every CONSUMPTION_REQUEST you're allowed to. RefundHalt sends Apple accurate consumption data inside the 12-hour window and answers Google Play's chargeback review inside 24 hours, because those are the only two moments either store asks for your side.

Is it worth asking Apple for access?

If you sell auto-renewable subscriptions and already handle App Store Server Notifications V2, probably yes. The cancel screen is the last place you can reach a subscriber before they go, and a plan switch costs you less than losing them outright. Just budget for an endpoint that answers on every subscription page view, and keep your refund defenses where refunds actually happen.

Frequently asked questions

What is Apple's Retention Messaging API?
It's a server-to-server API that lets a developer choose the message a subscriber sees after tapping Cancel Subscription on an auto-renewable subscription. Apple supports four types: text, text with an image, a switch-plan suggestion and a promotional offer. Apple's documentation still describes it as a pre-release that developers ask to join.
How fast does my Get Retention Message endpoint need to respond?
Within 700 ms in production. If your server is slower or fails, Apple shows the default message configured for that product and locale, and shows nothing if no default exists. You also have to pass a sandbox performance test, at about 700 ms, before Apple lets you set a production URL.
Does a retention message appear when a customer requests a refund?
No. Apple shows retention messages on the Confirm Cancellation screen after a customer taps Cancel Subscription. Refund requests go through reportaproblem.apple.com or the in-app refund sheet opened by beginRefundRequest, and the only developer input there is the CONSUMPTION_REQUEST, answered within 12 hours if the customer consented.
How can I tell a refund request from a cancellation in App Store Server Notifications?
Not from the renewal notification alone. A cancellation from the Subscriptions settings page and a refund request made through your app both produce DID_CHANGE_RENEWAL_STATUS with the subtype AUTO_RENEW_DISABLED. A CONSUMPTION_REQUEST for the same transaction, and later a REFUND or REFUND_DECLINED notification, shows that a refund request is in play.
Does Google Play let developers show a message when users cancel?
Google's subscription lifecycle documentation doesn't describe one. Developers receive a SUBSCRIPTION_CANCELED notification after the fact and can read the userInitiatedCancellation field to see why the user canceled. Pause, enabled by default for all subscriptions, is Google Play's main built-in tool against voluntary churn.

Sources and further reading

RefundHalt

The refund autopilot for the App Store and Google Play

Keep reading

The next refund request is already on its way.

Set up RefundHalt in the time it takes to read another support email about a refund you didn't get to contest.