---
title: "What does the App Store notification EXPIRED mean?"
description: "EXPIRED means a subscription ended. The subtype says why: VOLUNTARY, BILLING_RETRY, PRICE_INCREASE or PRODUCT_NOT_FOR_SALE. Remove access and keep the reason for win-back."
url: https://revenuedot.app/docs/notifications/apple-expired
---

# What does the App Store notification EXPIRED mean?

The App Store notification EXPIRED means a subscription has expired and the customer should lose access.

## Quick facts

| | |
|---|---|
| Notification | `notificationType` `EXPIRED` |
| Where | App Store Server Notifications v2 (`responseBodyV2DecodedPayload`) |
| Sent by | The App Store server, to your notification URL |
| What Apple says | The subscription expired. The subtype gives the reason: VOLUNTARY, BILLING_RETRY, PRICE_INCREASE or PRODUCT_NOT_FOR_SALE. With no subtype it expired for another reason. |

## What triggers it

- `VOLUNTARY`: the customer turned off renewal and the period ended.
- `BILLING_RETRY`: billing retry ended without a successful charge.
- `PRICE_INCREASE`: the customer did not consent to a price increase that needs consent.
- `PRODUCT_NOT_FOR_SALE`: the product was not available for purchase when the renewal was due. No subtype means some other reason.

## 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. Remove access to the paid features and mark the subscription as ended.
3. Use the subtype to choose the win-back message: a payment problem needs a billing prompt, a voluntary cancel needs an offer.
4. Keep the customer's data; they can resubscribe, which arrives as `SUBSCRIBED` with subtype `RESUBSCRIBE`.
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":"EXPIRED","data":{"environment":"Sandbox","bundleId":"com.example.app"}};

function decide(n) {
  if (n.notificationType !== "EXPIRED") return "ignore";
  const why = { VOLUNTARY: "offer a discount", BILLING_RETRY: "ask for a new payment method", PRICE_INCREASE: "explain the new price", PRODUCT_NOT_FOR_SALE: "offer another plan" }[n.subtype];
  return "remove access, " + (why ?? "ask why they left");
}

console.log(decide(notification));
```

*Run-checked: `npm run check:snippets` runs this snippet with Node and compares its output with `remove access, ` (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. Type overrides set the stored state from the subtype: `VOLUNTARY` turns renewal off and clears any billing issue, `BILLING_RETRY` marks a billing issue, `PRICE_INCREASE` stores the cancel reason `PRICE_INCREASE`. The `EXPIRATION` webhook event is recorded by RevenueDot's scheduler once access, grace period included, has ended, with `expiration_reason` taken from the stored state.

## Related

- [What does the App Store notification EXPIRED with subtype VOLUNTARY mean?](https://revenuedot.app/docs/notifications/apple-expired-voluntary.md)
- [What does the App Store notification EXPIRED with subtype BILLING_RETRY mean?](https://revenuedot.app/docs/notifications/apple-expired-billing-retry.md)
- [What does the App Store notification SUBSCRIBED mean?](https://revenuedot.app/docs/notifications/apple-subscribed.md)
- [Which webhook events does RevenueDot send?](https://revenuedot.app/docs/api/webhook-events.md)
- [Set up App Store notifications in RevenueDot](https://revenuedot.app/docs/guides/app-store.md)

## Source

- [Apple: notificationType](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype)
- [Apple: Responding to App Store Server Notifications](https://developer.apple.com/documentation/appstoreservernotifications/responding-to-app-store-server-notifications)
- [Apple: app-store-server-library-node (SignedDataVerifier)](https://github.com/apple/app-store-server-library-node)
- [RevenueDot server: stores/apple/notifications.ts (how the notification is applied)](https://github.com/revenuedot/revenuedot/blob/main/apps/server/src/stores/apple/notifications.ts)
- [RevenueDot server: stores/apple/map.ts (how the transaction becomes a stored purchase)](https://github.com/revenuedot/revenuedot/blob/main/apps/server/src/stores/apple/map.ts)
- [RevenueDot core: events.ts (how a change becomes a webhook event)](https://github.com/revenuedot/revenuedot/blob/main/packages/core/src/events.ts)
- [RevenueDot server: services/tick.ts (the scheduled EXPIRATION)](https://github.com/revenuedot/revenuedot/blob/main/apps/server/src/services/tick.ts)

Checked: 2026-10-03
