Skip to content
← All posts

By Specita

App Store Connect webhooks: setup, signatures, and Slack

How to set up App Store Connect webhooks, verify the x-apple-signature header, handle review status events, and send useful release alerts to Slack.

A clay block linked by a thin black line to a small paper box with a clay bell, with three terracotta discs along the line.

App Store Connect webhooks send an HTTP POST to your server when something changes on an app, such as a version moving from In Review to Pending Developer Release. You create one under Users and Access, give it a URL and a secret, choose the events, and verify each delivery with the x-apple-signature header. Slack can't verify that signature, so you need a small service between Apple and Slack.

The steps below follow Apple's documentation. Payload details can change, so check Apple's current reference before you ship a handler.

What App Store Connect webhooks can tell you

Apple's help page lists these triggers: a build upload status change, a beta build status change, an app version status change, a change to an Apple-hosted asset pack version, and new TestFlight feedback.

For release tracking, the version status event is the one most teams want. Apple's event reference shows it as APP_STORE_VERSION_APP_VERSION_STATE_UPDATED, with a payload like this:

{
  "data": {
    "type": "appStoreVersionAppVersionStateUpdated",
    "version": 1,
    "attributes": {
      "newValue": "READY_FOR_REVIEW",
      "oldValue": "PREPARE_FOR_SUBMISSION",
      "timestamp": "2025-04-16T05:00:52.745Z"
    },
    "relationships": {
      "instance": { "data": { "type": "appStoreVersions" } }
    }
  }
}

The example above is shortened from Apple's. Our guide to App Store review status explains what each state means for your team.

How to create a webhook

You need the Account Holder, Admin, or App Manager role.

  1. In Users and Access, click Integrations.
  2. Under Additional, click Webhooks, then the add button (+).
  3. Enter a name, the payload URL, and a secret.
  4. Select the app.
  5. Select at least one event trigger, then click Add.

Each webhook applies to one app, and you can create up to ten per app. You can't move an existing webhook to a different app, so someone managing 20 client apps needs 20 webhooks. If that's your situation, use the webhook API to create them from a script.

Use a separate secret per app where you can. One leaked secret then affects one app.

How to verify the signature

Apple hashes the request body with your secret using HMAC-SHA256 and sends the result in the x-apple-signature header, in the form hmacsha256=<hex digest>. Compute the same hash on your side and compare the two.

import crypto from 'node:crypto';

function isFromApple(rawBody, header, secret) {
  const expected = 'hmacsha256=' +
    crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(header ?? '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Hash the raw bytes of the body. If your framework parses the JSON first and you serialize it again, the bytes can differ and every delivery will fail the check. That's the first thing to look at when valid requests get rejected. Reject anything that fails, and log the failure without logging the secret.

How to test a webhook

Open the webhook and click Test in the top right. Apple sends a delivery that appears under Recent Deliveries with the Ping trigger. Your handler should accept Ping and do nothing else with it.

The same table is where you look when an event seems to be missing. It shows what Apple tried to send, and you can resend a delivery from there, whether it failed or succeeded. Test deliveries can't be resent.

Build the handler to tolerate surprises

Webhook delivery can be messy, so write your handler with these cases in mind. These points are our recommendations, based on building this kind of intake.

Respond quickly. Do the signature check, save the event, and answer with a 2xx. Then do the slower work, including the Slack call, in a background job. A slow Slack call shouldn't make your endpoint look broken.

Expect repeats. Apple says a resent delivery creates a new delivery record with the same event ID, so store the IDs you have seen and ignore duplicates. A resent delivery shouldn't produce a second alert.

Expect events out of order. Don't assume that the latest event you received is the latest state. Apple itself suggests using a notification as a prompt to call the API for data. Read the version's current state from the App Store Connect API, and treat the webhook as the signal to look.

Have a fallback. If your endpoint is down for an hour, you can miss a change. A periodic read of the API keeps you from showing a stale status forever.

Sending alerts to Slack

Point the webhook at your service, and have your service call a Slack incoming webhook. Decide which states deserve a message, since a channel that gets every transition soon gets muted.

Version stateAlert?Who needs it
IN_REVIEWMaybeRelease owner
REJECTED, METADATA_REJECTEDYes, with a mentionRelease owner and whoever can reply to App Review
PENDING_DEVELOPER_RELEASEYesWhoever presses the button
READY_FOR_DISTRIBUTIONYesTeam and support

A useful message names the app, the version, the old and new state, and a link to the app in App Store Connect:

Acme iOS 4.2.0: In Review → Rejected. Reply in App Store Connect. Owner: @dana

Keep the Slack webhook URL secret as well. Anyone who has it can post to that channel.

What about App Store Server Notifications?

Those are a separate system. App Store Server Notifications cover in-app purchase and subscription events. Webhooks in App Store Connect cover the app and build events described here. Search results often mix the two, so check which documentation page you're reading.

Specita keeps source changes and reported deliveries for each release in a shared workspace, and its Slack integration delivers release briefs to your team's channel. To connect your Apple account, start with the App Store Connect guide.

Apple guidance checked on November 4, 2026.