WWDC Quick Look đź’“ By SwiftGGTeam
Explore Retention Messaging in App Store Connect

Explore Retention Messaging in App Store Connect

Watch original video

Highlight

Apple adds Retention Messaging to App Store Connect, letting developers show retention messages and offers when users cancel subscriptions. It also opens the Real-time Retention Messaging API, allowing a server to return personalized messages, downgrade options, or promotional offers during the real-time cancellation request. Apps already using this feature saw average save rate increase by 82%, and promotional-offer mode increased it by 223%.

Core Content

Anyone who builds a subscription app shares the same pain: a user silently opens system settings and cancels, and you never get a chance to speak. Previously, you could only place a “manage subscription” entry inside the app, but users who truly intend to cancel usually never go through your flow.

This year Apple opens the cancellation confirmation step in the subscription management page. (00:15)

Static configuration in App Store Connect: start with no backend

For developers without a backend team, or who do not want to write code, Retention Messaging can be configured directly in App Store Connect. Open the subscription management page, click “Get Started” to create a retention message, enter a title and description, choose an image from Asset Library, and bind it to specific subscription products. One message can be associated with multiple subscriptions, and you can configure multiple Retention Offers. The App Store automatically chooses the best offer for eligible users. (02:43)

After configuration, when a user taps cancel subscription, the system shows your configured message on the cancellation confirmation page. Apple’s data is direct: apps using Retention Messaging increased average save rate by 1.4 percentage points, an 82% relative lift. When paired with promotional offers, save rate increased by 5.5 percentage points, a 223% relative lift. (02:06)

Real-time Retention Messaging: precise personalization

For teams with backend capabilities, the main feature is Real-time Retention Messaging. When a user taps cancel, the App Store sends an HTTP request to your server. Your server can use user data to decide in real time what to show. (06:48)

Real-time responses support three formats:

  • Return a message, with text only or text plus an image.
  • Return a downgrade option (alternateProduct) that recommends another product in the same subscription group.
  • Return a promotional offer (promotionalOffer) with a time-limited discount.

With this mode, you can use activity level, usage duration, payment history, and other data to decide whether to discount, recommend a lower-priced plan, or simply show a reminder message. This is much smarter than sending the same coupon to everyone. (08:57)

Three-level fallback mechanism

The real-time interface has strict response-time requirements. If your server times out or returns an error, the App Store falls back in this order: (10:10)

  1. Use the real-time response first.
  2. Fall back to static Retention Messaging configured in App Store Connect.
  3. Finally fall back to the default message configured through the Retention Messaging API.

This means users will not see a blank page even if the real-time interface has a problem.

Details

Retention Messaging API endpoint overview

The real-time feature is served through https://api.storekit.apple.com/inApps/v1/messaging, with these core endpoints: (07:50)

// URL configuration
PUT  /realtime/url          // Set the real-time callback URL
GET  /realtime/url          // Query the current URL
DELETE /realtime/url        // Delete the URL

// Message configuration
PUT    /message/{messageIdentifier}     // Create or update a message
DELETE /message/{messageIdentifier}     // Delete a message
GET    /message/list                    // List all messages
PUT    /default/{productId}/{locale}    // Set the default message
DELETE /default/{productId}/{locale}    // Delete the default message
GET    /default/{productId}/{locale}    // Query the default message

// Image configuration
PUT    /image/{imageIdentifier}         // Upload an image
DELETE /image/{imageIdentifier}         // Delete an image
GET    /image/list                      // List all images

// Performance test (Sandbox only)
POST /performanceTest                  // Start a performance test
GET  /performanceTest/result/{requestId} // Query test results

Key points:

  • All configuration operations are server-to-server and do not require client participation.
  • Messages, images, and default configurations are independent and can be combined flexibly.
  • The Sandbox environment must pass /performanceTest before Production can be enabled.

Real-time request format from the App Store

When a user taps cancel, the App Store sends the following JSON to your callback URL: (08:34)

{
    "originalTransactionId": "123456789",
    "appAppleId": 6745974591,
    "productId": "Yoga_summer_2026",
    "userLocale": "en-US",
    "requestIdentifier": "c03248af-dd76-4e9b-9c1e-4489cd19a768",
    "environment": "Production",
    "signedDate": 1780920000000
}

Key points:

  • originalTransactionId locates the user’s subscription record.
  • userLocale tells you which language to use in the response.
  • requestIdentifier is used for tracing and debugging.
  • environment distinguishes Sandbox from Production.

Response format 1: return a message

{
    "message": {
        "messageIdentifier": "551ee7c0-c097-418e-9dd5-2a98533a7390"
    }
}

Key points:

  • messageIdentifier must already be configured through PUT /message/{id}.
  • A message can show text alone or pair it with an image uploaded through PUT /image/{id}.
  • If the same message is also configured in App Store Connect, the two remain consistent.

Response format 2: return a downgrade product

{
    "alternateProduct": {
        "messageIdentifier": "ed7f25fc-5741-46a3-8502-062e0fb8afd0",
        "productId": "Yoga_summer_2026_annual"
    }
}

Key points:

  • productId must be in the same Subscription Group as the subscription being canceled.
  • When the user taps it, they switch directly to the new product without going through purchase again.
  • This is useful for users who “do not need premium features”; you keep the user’s LTV with a lower-priced plan.
  • The new 12-month commitment monthly payment plan in iOS 26.5 can also be specified through the billingPlanType field. (11:11)

Response format 3: return a promotional offer

{
    "promotionalOffer": {
        "messageIdentifier": "80135e2b-ae15-4ec4-8c5c-9ecc8045c0dc",
        "promotionalOfferSignatureV2": "eyJhbGciOiJFUzI..."
    }
}

Key points:

  • You must use the V2 promotional offer signature. V1 signatures do not work.
  • Signature generation follows the StoreKit 2 specification.
  • This is suitable for high-value users who are about to churn, using a time-limited discount for precise retention.

Identify Retention Offer transactions

When a user accepts a retention offer, the transaction contains new markers: (06:08)

{
    "bundleId": "com.example.app",
    "productId": "Yoga_summer_2026",
    "type": "Auto-Renewable Subscription",
    "transactionReason": "RENEWAL",
    "offerType": 5,
    "offerIdentifier": "Yoga_2026_cancel_free_3m",
    "offerDiscountType": "FREE_TRIAL",
    "offerPeriod": "P3M",
    "originalTransactionId": "1000011859217"
}

Key points:

  • offerType: 5 is the new Retention Offer type.
  • Previously, offerType only had values 1-4, so backend parsing logic needs an enum update.
  • offerIdentifier, offerDiscountType, offerPeriod, and related fields match regular offers.

Comparing the two approaches

DimensionStatic configuration in App Store ConnectReal-time Retention Messaging
Decision methodApp Store chooses automaticallyDeveloper controls in real time
Configuration entryApp Store Connect or ASC APIRetention Messaging API
Offer typeRetention OfferPromotional Offer
View supportMessage / image / offerMessage / image / offer / downgrade option
Server requiredNoYes
Performance requirementNoneMust pass Sandbox performance test

(11:52)

Key Ideas

1. Configure static first, then move to real time

Teams without a backend, or with limited backend resources, should first configure static Retention Messaging in App Store Connect. Apple’s data already shows that even a text-only message can increase average save rate by 82%. This is zero code, zero cost, and should be done immediately.

  • What to build: Configure one retention message and one image for each subscription product in ASC.
  • Why it is worth doing: No development cost, with direct access to the 82% save-rate lift.
  • How to start: Open App Store Connect > Subscriptions > Retention Messaging > Get Started.

2. Use alternateProduct for smart downgrades

Do not offer a discount every time someone cancels. Analyze why the user is cancelling. If the reason is “I do not need premium features,” return a lower-priced basic subscription in the same group. This preserves LTV and avoids training users to expect a discount whenever they cancel.

  • What to build: Return alternateProduct in the real-time response based on user profile data.
  • Why it is worth doing: It is more elegant than discounting and causes less margin loss.
  • How to start: Create a lower-priced tier in the subscription group, then configure a downgrade message through the Retention Messaging API.

3. Cache user profiles in Redis

The real-time interface is extremely latency-sensitive. Do not query a database or third-party service after receiving the request. Precompute user activity, payment history, usage preferences, and similar data, store it in Redis, and return directly from cache using originalTransactionId.

  • What to build: A low-latency retention decision service.
  • Why it is worth doing: The performance test is a launch gate, and latency directly affects user experience.
  • How to start: Use originalTransactionId as the key and pre-warm user profile data into cache.

4. Combine with Win-back for a full lifecycle loop

Retention Messaging intercepts users who are cancelling right now. Win-back brings back users who already cancelled. Together they close the subscription lifecycle loop.

  • What to build: Use Retention to intercept during cancellation and Win-back to recover users after cancellation.
  • Why it is worth doing: Covers both critical churn moments in the subscription lifecycle.
  • How to start: Launch Retention Messaging first, then integrate Win-back Offer.

Comments

GitHub Issues · utterances