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:
| notificationType | When Apple sends it |
|---|---|
SUBSCRIBED | Someone subscribes for the first time (INITIAL_BUY) or subscribes again (RESUBSCRIBE), including by redeeming an offer code |
DID_RENEW | A subscription renews; subtype BILLING_RECOVERY when a failed renewal later succeeds |
ONE_TIME_CHARGE | A consumable, non-consumable or non-renewing subscription is purchased (or redeemed with an offer code) |
OFFER_REDEEMED | A customer with an active subscription redeems an offer |
REFUND | Apple refunded a transaction |
REFUND_REVERSED | Apple 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:
- In-app purchases only. The service covers in-app purchase events. A paid-upfront app download is not an in-app purchase, so it produces no notification — those sales show up only in App Store Connect's reports.
- Price, not proceeds.
priceis the price you configured, recorded on the purchase date, after any subscription-offer discount and multiplied by quantity for consumables. It is not what you will be paid: Apple says to use the App Store Connect reporting tools for financial and accounting purposes. Usestorefront, notcurrency, to tell where the sale happened. - Family Sharing is not a sale.
SUBSCRIBEDandONE_TIME_CHARGEare also sent when a family member gains access through Family Sharing. The transaction'sinAppOwnershipTypeisFAMILY_SHAREDin that case, andPURCHASEDfor the buyer. - One URL per environment. Each app has one production URL and one sandbox URL (step 2).
Step 1: an HTTPS endpoint
Your endpoint must:
- use HTTPS with TLS 1.2 or later — and if the URL names a port, it must be
443or at least1024; - answer HTTP
200–206once it has processed the notification, and40xor50xwhen Apple should retry; - accept requests from Apple's
17.0.0.0/8subnet if your firewall uses an allow list.
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.
- In Apps, select your app.
- In the sidebar, under General, click App Information.
- Scroll to App Store Server Notifications.
- Under Production Server URL, click Set Up URL, enter your URL, choose Version 2 and click Save.
- 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
- Count
SUBSCRIBED,DID_RENEW,ONE_TIME_CHARGEandOFFER_REDEEMEDas income (once per transaction; see the next point),REFUNDas a negative andREFUND_REVERSEDas income again. - Key each sale by
transactionId. Several notifications can carry the same transaction (for exampleOFFER_REDEEMEDfor an active subscription);notificationUUIDonly identifies a redelivered notification. - Skip family access: ignore transactions whose
inAppOwnershipTypeisFAMILY_SHARED. - Keep sandbox out of your totals: the decoded data carries its
environment. - Show
price / 1000incurrency, and group bystorefront.
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:
- With the App Store Connect API key you already give it — stored only in your Mac's Keychain — it sets the notification URL for the apps you tick. Apps whose URL points at RevenueCat or another server start unticked, with a warning, so nothing is switched behind your back.
- RevBell's server verifies Apple's signature and keeps only the transaction identifiers, product, price, currency, storefront, dates and environment, for 45 days — never the raw notification or the app account token.
- Your Mac is told when a sale arrives and shows it in the notch, usually within 1–2 minutes. Sale details — app, amount, product, country — are part of RevBell Pro; the free plan tells you that a sale happened.
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.