Back to Blog

App Store Server Notifications V2 in 2026: A Step-by-Step Tutorial for Refunds, Renewals, and Consumption Requests

App Store Server Notifications V2 in 2026: A Step-by-Step Tutorial for Refunds, Renewals, and Consumption Requests

Apple has closed the version 1 path: version 1 notifications and the App Store Receipts endpoint are deprecated, and the instruction now reads "implement the version 2 endpoint on your server" instead. For an app with live subscriptions this is not a cosmetic migration — the notification stream is how your server learns that a renewal succeeded, that a refund was issued, that billing is being retried, or that Apple is asking you to justify a refund request. This tutorial walks the full 2026 setup: enabling version 2 in App Store Connect, verifying the JWS, mapping every notification type and subtype, and using the App Store Server API for the state that a notification only describes at a point in time.

The one-line version: App Store Connect POSTs a JWS-signed JSON blob to your HTTPS URL; verify the signature chain, decode signedPayload, act on notificationType plus subtype, answer 200-206 quickly, and call the App Store Server API whenever you need the current state rather than the event's state.

Step 1 — Fix the two endpoints before you touch App Store Connect

You need one HTTPS URL for production and, optionally, a second one for sandbox; Apple explicitly allows you to use the same URL for both environments. Your server must support TLS 1.2 or later. If the URL contains a port, it must be either 443 or 1024 and above — https://example.com:1234/notifications is a valid shape, a low privileged port is not. And if your edge terminates behind an IP allow list, add Apple's 17.0.0.0/8 subnet, which applies to sandbox and production alike. All of this is documented in Enabling App Store Server Notifications.

Step 2 — Turn on version 2 per environment

In App Store Connect open your app, then App Information > App Store Server Notifications. Production and sandbox are configured separately, and each environment independently chooses version 2 or the deprecated version 1 — which is what lets you run the new format in production while the old one still has to work elsewhere, or the reverse. Apple's stated migration path is to test version 2 in the sandbox environment first and only then move production. One behaviour worth knowing before you start debugging: the TEST notification is always sent in the version 2 format, no matter which version your configured URL expects.

Step 3 — Answer with the right status code, then work asynchronously

Respond with an HTTP status code of 200-206 if the POST was handled successfully. If it was not handled, send a 50x or 40x to have the App Store retry the notification. The practical pattern is: read the body, verify it, enqueue it, reply 200, and do the real work in a worker. Do not hold the connection open while you call your own database, your email provider, or the App Store Server API. Retries are there to protect you from infrastructure failure, not from a slow handler — and every retry you trigger is a duplicate event you then have to suppress.

Step 4 — Decode the payload

The body is a responseBodyV2 object whose only meaningful field is signedPayload, a JWS. Parse it into its header, payload and signature representations, then Base64URL-decode the payload to get the responseBodyV2DecodedPayload. At that level you get notificationType, subtype, version, signedDate and notificationUUID. Exactly one of four data objects is present: data for in-app purchase events, carrying a signedTransactionInfo and — for auto-renewable subscriptions — a signedRenewalInfo; summary, which appears only for a RENEWAL_EXTENSION with the SUMMARY subtype; appData; or externalPurchaseToken, which appears only on an EXTERNAL_PURCHASE_TOKEN notification.

Step 5 — Verify every signature, not just the outer one

All three signed representations — signedPayload, signedTransactionInfo and signedRenewalInfo — carry their own JWS signature, and each has to be validated; use the algorithm named in the header's alg parameter. In practice that means checking the x5c certificate chain up to Apple's published root, which you download from the Apple certificate authority page (the current root for this service is AppleRootCA-G3.cer). Do not hand-roll this: Apple publishes the App Store Server Library for Python, Java, Node.js and Swift, which performs the verification and the decode for you. A handler that Base64-decodes the payload and skips verification is trivially spoofable — anyone who learns your notification URL can grant themselves a subscription.

Step 6 — Map notificationType and subtype onto your entitlements

The version 2 enum defines 23 notification types, and 19 subtypes that qualify some of them; both lists are published on notificationType and subtype. The ones that decide whether a paying customer keeps access:

The full catalogue with the current additions lives in the notifications changelog — Apple adds fields and values over time, so re-read it before you assume your handler is complete.

Step 7 — Answer a CONSUMPTION_REQUEST inside 12 hours

When a customer requests a refund for any product type — consumable, non-consumable, non-renewing or auto-renewable — Apple sends a CONSUMPTION_REQUEST. If the customer consented, you can respond by calling Send Consumption Information with the usage data Apple asks for, and Apple documents a 12-hour response window from the moment the notification arrives. If the customer did not consent, you do not respond at all. Two rules are easy to get wrong: the consent you need for this API is separate from the consent you collected for tracking or advertising, and you are the party responsible for obtaining valid consent before sharing usage data with Apple.

Step 8 — Use the App Store Server API for state, not for the event

A notification describes a moment. Before you write entitlements, resolve the present:

Step 9 — Prove the pipe works before you rely on it

Call Request a Test Notification and the App Store sends a TEST notification to your endpoint. That call returns a token you pass to Get Test Notification Status within six months to see how your server actually answered. Separately, if your endpoint was down at 3am, Get Notification History returns the version 2 notifications the App Store attempted to send: 180 days in production, 30 days in the sandbox, 20 records per page with a continuation token, and full notification contents. Remember what that history is: each record reflects the state at the time the notification was originally sent, not the current state.

Step 10 — The operational rules that keep the pipeline correct

Sources

Running developer accounts at scale? We handle the operational side.

Click to choose a file · right-click → Paste, or press Ctrl/⌘+V to paste a screenshot

You can also drag a file onto this box.

We reply to the email you provide. Your details stay with KappS.

✔ Submitted

We received your message and will reply within 24 hours.

Or email expert@kapps.store
← Back to edit