What does the App Store notification GRACE_PERIOD_EXPIRED mean?

The App Store notification GRACE_PERIOD_EXPIRED means the billing grace period ended before the subscription renewed.

Quick facts#

Notification notificationType GRACE_PERIOD_EXPIRED
Where App Store Server Notifications v2 (responseBodyV2DecodedPayload)
Sent by The App Store server, to your notification URL
What Apple says The billing grace period ended without renewing the subscription, so you can turn off access to the service or content.

What triggers it#

  • A renewal failed, Billing Grace Period was on, and the customer did not fix their billing before the grace period ended.
  • Apple says grace periods last 3 or 6 days for weekly subscriptions and 3, 16 or 28 days for monthly and longer ones, and cannot be changed once assigned.
  • The subscription is still in billing retry. Apple continues for 60 days, until the customer fixes billing or cancels.

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. Turn off access now, and keep a message that asks the customer to update their payment method.
  3. Keep listening: a later DID_RENEW with subtype BILLING_RECOVERY means billing recovered, and EXPIRED with BILLING_RETRY means Apple gave up.
  4. 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":"GRACE_PERIOD_EXPIRED","data":{"environment":"Sandbox","bundleId":"com.example.app"}};

function decide(n) {
  if (n.notificationType !== "GRACE_PERIOD_EXPIRED") return "ignore";
  return "turn off access, keep asking for a new payment method";
}

console.log(decide(notification));

Run-checked: npm run check:snippets runs this snippet with Node and compares its output with turn off 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. A type override keeps the billing issue on the stored purchase. Access ends when the grace period passes, and the scheduler then records EXPIRATION with expiration_reason: BILLING_ERROR. The notification itself adds no new event.

Source#

Checked: 2026-10-03

Edit this page on GitHub ↗ View as Markdown Last updated