expired

Learn about the EXPIRED App Store Server Notification V2. Fired when a subscription expires and is no longer active.

App Store Server Notifications V2Subscriptions
expired

The EXPIRED notification fires when a subscription has expired and the customer no longer has access. This is the final state in the churn path — the subscription is over.


When does it fire?


This notification triggers when a subscription period ends without a successful renewal. Subtypes indicate the reason:


  • Voluntary — the customer disabled auto-renew and the period ended
  • Billing retry period over — billing retries exhausted after a payment failure
  • Price increase declined — the customer didn't consent to a price increase

Payload highlights


The signed transaction info includes the final expiration date and the original purchase date. The signed renewal info may include the expiration intent, showing why the subscription expired.


How Yeethook helps


Yeethook delivers expiration events with clear context about why the subscription ended. This helps your team segment churned subscribers by reason and take appropriate action (win-back offers for voluntary churn, payment update prompts for billing failures).


Common use cases


  • Track churn rates and segment by expiration reason
  • Trigger win-back offer campaigns for recently expired subscribers
  • Monitor the effectiveness of billing retry and grace period policies
  • Audit subscription lifecycle from acquisition to expiration

Example payload

The decoded body for a subscription that ran out after the customer turned off auto-renew. status 2 marks it as expired, and the matching expirationIntent is inside signedRenewalInfo rather than in this object.

{
  "notificationType": "EXPIRED",
  "subtype": "VOLUNTARY",
  "notificationUUID": "4f7c92d1-1ea8-4b30-86d5-c0392b7f8e14",
  "version": "2.0",
  "signedDate": 1789630517000,
  "data": {
    "appAppleId": 1234567890,
    "bundleId": "com.example.app",
    "bundleVersion": "142",
    "environment": "Production",
    "signedTransactionInfo": "eyJhbGciOiJFUzI1NiIsIng1YyI6...",
    "signedRenewalInfo": "eyJhbGciOiJFUzI1NiIsIng1YyI6...",
    "status": 2
  }
}

What it looks like in Slack

Warning

Subscription expired (cancelled)

Product
com.example.app.pro.monthly
Transaction
2000000798765432
Expires
2026-11-12T09:34:13.000Z
Auto-renew
Off
Expiration intent
Customer cancelled
Subscription status
Expired
Environment
Production

The same event arrives with different values depending on what changed. Yeethook rewrites the headline to match:

  • BILLING_RETRY→ Subscription expired (billing failed)
  • PRICE_INCREASE→ Subscription expired (price increase declined)
  • PRODUCT_NOT_FOR_SALE→ Subscription expired (product not for sale)

Questions about expired

Which field tells me why a subscription expired?

expirationIntent in signedRenewalInfo, where 1 is customer cancelled, 2 billing error, 3 price increase declined, 4 product unavailable and 5 other. Yeethook prints it as an Expiration intent row, and the notification subtype mirrors the same reason.

Can I rely on EXPIRED arriving exactly at expiry time?

No. Gate entitlements on the expiresDate inside the transaction, not on when the webhook lands, because notification delivery is asynchronous and Apple retries failed deliveries over several hours. Treating arrival time as expiry time creates a window where a lapsed customer still has access.

What causes the PRODUCT_NOT_FOR_SALE subtype?

The product was removed from sale, or is no longer available in the customer's storefront, so Apple could not bill the next period even though the customer wanted it. It corresponds to expirationIntent 4 (Product unavailable) and is worth alerting on separately, since the cause is on your side of the catalogue.

Is EXPIRED the last notification for that subscriber?

For that subscription period, yes, but not for the transaction. Apple can still send REFUND, REFUND_REVERSED or CONSUMPTION_REQUEST against those old transactions, and a later return arrives as SUBSCRIBED with subtype RESUBSCRIBE.

Get this event in Slack or Discord

Connect Yeethook to receive expired notifications in your team's Slack or Discord channels.

Get Started Free