Not Every Apple Event Deserves a Ping

Subscribe to everything, forward almost nothing. Most Apple webhooks are state chatter. Here's how to tell the events worth interrupting a channel for from the ones that belong in a log.

Cover Image for Not Every Apple Event Deserves a Ping

Apple's webhook catalog is not a notification list. It is a state dump.

Every build upload reports that it started processing and then that it finished. Every version record announces each step of its walk through review. If you turn all of that into Slack messages, you get a channel nobody reads, and the one event that actually needed a human scrolls past between two "processing" updates.

The fix is not subscribing to less. Capture everything, because the event you skipped is the one you will want next month. Forward almost nothing.

Severity is already in the event

The useful sorting key is not the event type. It is the state the event carries.

Yeethook assigns a level to each state, and the split is not subtle:

App version states. READY_FOR_DISTRIBUTION is a success. REJECTED is critical. DEVELOPER_REJECTED is a warning, because your team did that on purpose. Everything else in the pipeline — PREPARE_FOR_SUBMISSION, WAITING_FOR_REVIEW, IN_REVIEW, PENDING_DEVELOPER_RELEASE — is info.

Build upload states. VALID is a success. INVALID and FAILED are critical. PROCESSING is info.

External TestFlight states. READY_FOR_BETA_TESTING is a success. BETA_REJECTED and EXPIRED are warnings. The review states in between are info.

Read that list again and notice the shape of it. In every family, the majority of possible states are info, and info is most of your volume. Four event types can produce a hundred messages a week where maybe five of them change what anyone does today.

So route on the state, not just the type. Subscribe to BUILD_UPLOAD_STATE_UPDATED, then decide that FAILED and INVALID go to the channel your engineers watch and PROCESSING goes to a log channel or nowhere.

The events worth interrupting someone for

Three earn a real ping in a channel people actually read.

BETA_FEEDBACK_CRASH_SUBMISSION_CREATED. A tester hit a crash and chose to send it. That is a human deciding your build is broken enough to report. It is also the cheapest bug report you will ever receive, and it expires: testers move on, and a crash nobody looked at for a week is a crash you will reproduce from scratch.

BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED. Same logic, lower severity, higher volume. A tester annotated something and typed a sentence about it. Worth a channel. Not worth a mention.

APP_STORE_VERSION_APP_VERSION_STATE_UPDATED at REJECTED. This one is critical for a reason. A rejection stops the release, starts a clock, and usually needs a decision from someone who is not watching App Store Connect. It is the single event most worth waking a channel up for.

Everything else on the list is context you want to be able to look up, not an interruption.

The firehose

BUILD_UPLOAD_STATE_UPDATED is the highest-volume App Store Connect event for most teams, and it is structurally noisy: CI uploads builds, every upload reports processing, and processing almost always succeeds.

A successful build finishing is not news. It is the expected outcome of a thing you already knew you did. If your team is pushing builds through a pipeline, those messages arrive in batches and train everyone to skim the channel.

The failures are the opposite. FAILED and INVALID mean the upload will not ship, and the payload does not tell you why, so somebody has to open App Store Connect. That is exactly when a Slack message earns its place.

The same reasoning applies to APP_STORE_VERSION_APP_VERSION_STATE_UPDATED. The version walking from PREPARE_FOR_SUBMISSION through WAITING_FOR_REVIEW to IN_REVIEW is four messages describing one submission. Keep the endpoints. Drop the middle.

Where severity will not save you

Two event families do not carry usable severity, and it is worth knowing before you build routing rules around it.

Background asset events. All four of them post at info with a fixed headline. There is no state-to-severity mapping, so a FAILED asset and a PROCESSING asset arrive at the same level, with the transition visible only in the message body.

Alternative distribution events. All three post at info as well. A package being withdrawn reads exactly like a package version being created.

For these, filter on the event type itself and read the state in the message. Severity-based rules will treat a real problem as routine.

Subscription events are a different audience

Everything above is the development lifecycle. App Store Server Notifications are the revenue lifecycle, and they should almost never share a channel with crashes.

The volume pattern is also inverted. DID_RENEW fires constantly and individually means nothing — it is a number you look at weekly, not a message you read. The ones worth a ping are the ones that indicate a decision or a break:

  • DID_CHANGE_RENEWAL_STATUS is the earliest churn signal Apple gives you. The subscription is still active. The customer already decided to leave.
  • DID_FAIL_TO_RENEW means a payment method failed, and the subscriber may not know.
  • CONSUMPTION_REQUEST comes with a 12-hour response window, which makes it the rare subscription event with a deadline attached.

Renewals belong in a revenue channel as a stream. Churn signals and deadlines belong where someone will act on them.

A default worth starting from

If you want one setup to begin with and refine later:

  • Crashes and screenshot feedback to the channel your engineers already read.
  • Rejections and releases to a release channel. That is REJECTED, DEVELOPER_REJECTED, and READY_FOR_DISTRIBUTION — the endpoints, not the walk.
  • Build failures to the engineering channel. Successful processing to a log channel or off.
  • Churn and billing failures to a revenue channel. Renewals as a stream in the same place.
  • Everything else captured and searchable, forwarded nowhere.

The mechanics of splitting events across channels are in Route Events Like a Pro. This post is the layer above it: not where an event goes, but whether it should say anything at all.

The test

For every event type you forward, ask what someone does differently in the five minutes after it arrives.

If the answer is "nothing, but I would want to find it later," that is a log, and you should still capture it. If the answer is "open App Store Connect" or "look at the crash," that is a notification.

A channel where every message passes that test is a channel your team keeps reading. That is the only measure of a good alerting setup, and it has nothing to do with how many event types you turned on.