What Apple sends — and what it doesn't

Apple describes App Store Server Notifications as “a server-to-server service that sends real-time notifications for in-app purchase events”. When one of those events happens, the App Store sends an HTTP POST to a URL you choose. The JSON body has a single field, signedPayload; decoded, it contains a notificationType, often a subtype, and signed transaction details.

The types that mean money coming in or going out:

notificationTypeWhen Apple sends it
SUBSCRIBEDSomeone subscribes for the first time (INITIAL_BUY) or subscribes again (RESUBSCRIBE), including by redeeming an offer code
DID_RENEWA subscription renews; subtype BILLING_RECOVERY when a failed renewal later succeeds
ONE_TIME_CHARGEA consumable, non-consumable or non-renewing subscription is purchased (or redeemed with an offer code)
OFFER_REDEEMEDA customer with an active subscription redeems an offer
REFUNDApple refunded a transaction
REFUND_REVERSEDApple reversed a refund after a dispute

The money itself lives in the signed transaction (JWSTransactionDecodedPayload): price in milliunits (a $1.99 purchase arrives as 1990), currency, storefront, productId, transactionId, purchaseDate, and offerType when an offer applied.

Four limits to know before you build on it:

Step 1: an HTTPS endpoint

Your endpoint must:

Without a success response, Apple retries a version 2 notification five times: 1, 12, 24, 48 and 72 hours after the previous attempt. Retries happen only in production; in the sandbox Apple tries once. After a longer outage, the App Store Server API's Get Notification History endpoint returns the version 2 notifications you missed.

A serverless function (Cloudflare Workers, AWS Lambda, Google Cloud Run…) is plenty — each notification is one small POST.

Step 2: enter the URL in App Store Connect

Required role: Account Holder, Admin, App Manager or Marketing.

  1. In Apps, select your app.
  2. In the sidebar, under General, click App Information.
  3. Scroll to App Store Server Notifications.
  4. Under Production Server URL, click Set Up URL, enter your URL, choose Version 2 and click Save.
  5. Do the same under Sandbox Server URL. You may use the same URL for both.

Two details from Apple's help page: if you don't provide a sandbox URL, “the App Store will automatically send notifications for both environments to the Production URL”; and if you provide only a sandbox URL, nothing is sent in production.

Already using a subscription platform? If RevenueCat, another service or your own backend already owns the URL, don't overwrite it — each environment has exactly one. Have the server that owns the URL forward notifications to yours instead.

The same setting is on the App Store Connect API's App resource — subscriptionStatusUrl, subscriptionStatusUrlVersion and their …ForSandbox counterparts — which is how tools can set it with your API key.

Step 3: verify and decode

Everything in a notification is a JSON Web Signature (JWS): the outer signedPayload, and inside it data.signedTransactionInfo and, for subscriptions, data.signedRenewalInfo. Verify each signature before you trust it. The JWS header's x5c holds the certificate chain Apple signed with — a signing certificate, an Apple intermediate and an Apple root certificate from Apple PKI.

You don't need to write that yourself. Apple's open-source App Store Server Library (Swift, Java, Python and Node) verifies and decodes notifications, transactions and renewal info. In Node:

import { SignedDataVerifier, Environment } from "@apple/app-store-server-library";

// Apple root certificates from https://www.apple.com/certificateauthority/
// One verifier per environment; appAppleId is required for Production.
const verifier = new SignedDataVerifier(appleRootCAs, true, Environment.PRODUCTION, "com.example.app", 1234567890);

export async function onNotification(request) {
  const { signedPayload } = await request.json();
  const notification = await verifier.verifyAndDecodeNotification(signedPayload);
  const signedTransaction = notification.data?.signedTransactionInfo;
  const transaction = signedTransaction ? await verifier.verifyAndDecodeTransaction(signedTransaction) : null;
  // notification.notificationType, notification.subtype, notification.notificationUUID
  // transaction.price / 1000, transaction.currency, transaction.storefront, transaction.productId
  return new Response(null, { status: 200 });
}

Step 4: send yourself a test

Call the App Store Server API's Request a Test Notification endpoint (authorized with a JSON Web Token from your In-App Purchase key). Apple sends a TEST notification to your production URL when you call the production endpoint, and to your sandbox URL when you call the sandbox one. Get Test Notification Status then tells you how your server responded.

To see real purchase types, buy something in the sandbox with a Sandbox Apple Account — for example, a subscription produces SUBSCRIBED with subtype INITIAL_BUY.

Step 5: turn notifications into sale alerts

From there, fan out however you like: a Slack or Discord webhook, a push notification, or a menu-bar app.

If you'd rather not run a server

RevBell does steps 1–5 for you on a Mac:

FAQ

Does this replace App Store Connect's sales reports?

No. Notifications tell you quickly; the reports are the financial record — proceeds, taxes and currency conversion.

Do paid app downloads trigger a notification?

No. Only in-app purchase events do.

What if my server is down?

Production notifications are retried five times over about six and a half days (1 + 12 + 24 + 48 + 72 hours). After that, use Get Notification History.

Can two services receive the same notifications?

Not directly — there is one production URL and one sandbox URL per app. Forward from the server that owns the URL.

How fast is it?

Apple calls the service real-time and doesn't publish a delivery time. Relayed through RevBell's server to a Mac, sale alerts usually arrive within 1–2 minutes of the purchase.