Two things shipped today, and they come from the same habit: read what Apple actually sends, then fix the places where Yeethook passed the raw version along.
First, device names in TestFlight feedback. Second, the App Store Server Notification types Apple added for Advanced Commerce, plus a bug that left two event types with a headline and nothing under it.
Part one: iPad17,2 is an iPad Pro
Apple's App Store Connect API does not send "iPad Pro 11-inch (M5)." It sends iPad17_2. Crash logs use the comma form, iPad17,2. Either way, that is not a name your team can scan in Slack.
Yeethook already enriches TestFlight crash and screenshot events with the device model. Today we updated the identifier map so those messages use the marketing name, with the raw identifier in parentheses.
iPad17_2 now reads as iPad Pro 11-inch (M5, WiFi+Cellular) (iPad17,2).
Why this showed up now
We looked at the device identifiers already sitting in production crash and screenshot events. Three of them had no friendly name, so they rendered as the raw ID:
iPad17,2, iPad Pro 11-inch (M5, Cellular). One of the most common devices we see.iPhone18,5, iPhone 17e.iPad16,11, iPad Air 13-inch (M4, Cellular).
If your testers are already on that hardware, those Slack messages were doing the hard part (crash log, tester, OS) and then dropping a board ID at the end. That is the opposite of enrichment.
What we mapped
The identifiers Apple already ships, plus the ones announced this month that will start showing up in TestFlight as soon as testers get the hardware:
iPhone. iPhone 17e. iPhone 18 Pro and 18 Pro Max. iPhone Duo.
iPad. iPad Air (M4) in 11-inch and 13-inch. iPad Pro (M5) in 11-inch and 13-inch. We also fixed a bad label: the M4 iPad Pro is 13-inch, not 12.9-inch.
Apple Watch. Series 12 and Ultra 4.
visionOS and tvOS. Apple Vision Pro (M2 and M5), tvOS Apple TVs, and HomePods. TestFlight distributes those builds too, so the identifiers can land in feedback events.
Apple-silicon iPads now use chip names (M1 through M5) instead of mixed ordinal generations. That is how Apple markets them, and it is how you tell an M4 Pro from an M5 Pro at a glance.
Unknown identifiers still fall back to the raw ID. A new device will never blank out the Device field. We just will not invent a name for hardware Apple has not confirmed yet.
Part two: three new Apple events
Same exercise, different API. We diffed Yeethook's event list against Apple's notificationType documentation and the enums in Apple's official server library. Three App Store Server Notification types were missing.
METADATA_UPDATE fires when you change subscription metadata. MIGRATION fires when you migrate a subscription to the Advanced Commerce API. PRICE_CHANGE fires when you change a subscription price.
All three reach only apps that use Apple's Advanced Commerce API, and all three are triggered by your own server calls rather than by something a customer did. They live in a new Advanced Commerce category in the event selector, switched off by default. If you use Advanced Commerce, turn them on. If you do not, your event list stays as quiet as it was.
One naming note. Apple's changelog calls the migration type MIGRATE. The value Apple actually ships is MIGRATION, so that is what we match.
App Store Connect webhooks needed nothing. Apple's list is still the same 12 event types, and Yeethook already covered all of them.
The bug we found on the way
This one was quietly costing you detail.
A V2 notification payload has four sibling objects, not one: data, summary, externalPurchaseToken, and appData. Yeethook's field extractor only read data, and gave up when data was not there.
Two event types do not use data. RENEWAL_EXTENSION puts its results in summary. EXTERNAL_PURCHASE_TOKEN puts its details in externalPurchaseToken. Both arrived in Slack as a headline with an empty body.
The extractor now reads all four. A batch renewal extension shows succeeded and failed counts, the request ID, and the storefronts it covered. An external purchase token shows the token ID, its type, and when it expires.
Sharper headlines, more fields
While we were in there:
OFFER_REDEEMEDnow says whether the offer was an upgrade or a downgrade.RENEWAL_EXTENSIONseparates a completed batch from a single subscriber that failed.EXTERNAL_PURCHASE_TOKENtells a freshly created token apart from a reminder (warning) and an unreported one (critical). The unreported case is the one you want to see loudly.- Win-back offers read as "Win-back offer" instead of
4. Apple added that offer type and our lookup table had not caught up. CONSUMPTION_REQUESTnow includes Apple's stated reason: unintended purchase, fulfillment issue, and the rest. You have 12 hours to respond, so the reason is worth having in the message.REFUNDandREVOKEshow the revocation type and percentage when Apple sends them.
The Slack version
A crash from an M5 iPad Pro used to look like a board number. A batch renewal extension used to look like a headline with no body.
Those are the same failure. The webhook told you something happened, then made you go find out what. The name tells you what to reproduce it on. The fields tell you how big it was.
That is the whole point of enrichment.
