What does the App Store notification DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED mean?

The App Store subtype AUTO_RENEW_DISABLED means auto-renew was turned off for a subscription that has not expired yet.

Quick facts#

Notification notificationType DID_CHANGE_RENEWAL_STATUS, subtype AUTO_RENEW_DISABLED
Where App Store Server Notifications v2 (responseBodyV2DecodedPayload)
Sent by The App Store server, to your notification URL
What Apple says The customer turned off subscription auto-renewal, or the App Store turned it off after the customer requested a refund.

What triggers it#

  • The customer cancelled in the App Store Subscriptions settings.
  • The system turned auto-renew off because the customer started a refund request through your app with the refund request API.
  • The subscription itself is not over: it expires at the end of the paid period and EXPIRED with subtype VOLUNTARY follows then.

What your server should do#

  1. Verify the signedPayload with Apple's App Store Server Library (SignedDataVerifier.verifyAndDecodeNotification) before you act on anything in it.
  2. Keep access until expiresDate.
  3. Show a message that the subscription ends on that date, and offer a way back.
  4. Do not mark the customer as churned until EXPIRED arrives.
  5. Answer HTTP 200 (any code from 200 to 206) once you have stored the notification. Apple retries other answers five times, at 1, 12, 24, 48 and 72 hours after the previous attempt, so make your handler safe to run twice (the payload carries a notificationUUID).

Example#

JavaScript
// Run on the decoded payload, after you verified Apple's signature.
const notification = {"notificationType":"DID_CHANGE_RENEWAL_STATUS","subtype":"AUTO_RENEW_DISABLED","data":{"environment":"Sandbox","bundleId":"com.example.app"}};

function decide(n) {
  const off = n.notificationType === "DID_CHANGE_RENEWAL_STATUS" && n.subtype === "AUTO_RENEW_DISABLED";
  return off ? "mark renewal off, keep access until expiresDate, not churned yet" : "ignore";
}

console.log(decide(notification));

Run-checked: npm run check:snippets runs this snippet with Node and compares its output with not churned yet (checked 2026-10-03).

How RevenueDot handles it#

RevenueDot verifies Apple's signature, checks the bundle ID, stores the notification and answers 200. A purchase RevenueDot has not seen is applied only when the app's Track new purchases from server-to-server notifications setting is on. The type override stores the purchase as unsubscribed. RevenueDot sends one CANCELLATION event with a cancel_reason (UNSUBSCRIBE unless the store or a billing issue says otherwise) and keeps expiration_at_ms unchanged. The EXPIRATION event follows when the period ends.

Source#

Checked: 2026-10-03

Edit this page on GitHub ↗ View as Markdown Last updated