# App Store Connect testFlight feedback notifications on your phone

> Get a push notification when a TestFlight tester sends screenshot feedback or reports a crash.

- Company: App Store Connect (https://www.justpush.io/recipes/app-store-connect)
- Event: `BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED` (BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED)
- Tags: Developer tools, Releases
- Install: https://studio.justpush.io/recipes/app-store-connect/testflight-feedback
- Web page: https://www.justpush.io/recipes/app-store-connect/testflight-feedback

## Setup

1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
2. In App Store Connect, go to Users and Access > Integrations > Webhooks and add a webhook for your app.
3. Use the URL from the setup notes below, which adds your app's name so the notification can show it.
4. Select the BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED event, enter any secret, and save.
5. Send a test ping from the webhook to confirm it arrives.
6. Use this as the webhook URL, replacing the app name and ID with your own:
7. ```
8. your webhook URL (shown in Studio after install)?app=My%20App&app_id=1234567890
9. ```
10. App Store Connect only sends IDs and states, so `app` is what the notification calls your app (`%20` is a space). `app_id` is your app's **Apple ID**, found under **App Information**; it makes the button open your app instead of the App Store Connect home page. Both are optional. Select both `BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED` and `BETA_FEEDBACK_CRASH_SUBMISSION_CREATED` to hear about crashes too.
11. App Store Connect asks for a secret and signs each request with it, but JustPush doesn't check that signature yet, so keep your endpoint URL private.

## Code

Studio calls `handleRequest(request)` with the incoming webhook (`{ method, headers, body, query, raw }`) and sends the message object it returns. Returning `null` sends nothing.

```js
// App Store Connect → testflight feedback
// Fires when a TestFlight tester sends feedback with a screenshot, or reports a crash.
// Docs: https://developer.apple.com/documentation/appstoreconnectapi/webhook-events
//
// Add an App Store Connect webhook for the BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED events
// pointing at your integration's endpoint URL plus ?app=My%20App&app_id=1234567890.

// Feedback event types, and how to describe them.
const FEEDBACK = {
    betaFeedbackScreenshotSubmissionCreated: { title: "💬 New TestFlight feedback", text: "A tester sent feedback on" },
    betaFeedbackCrashSubmissionCreated: { title: "💥 TestFlight crash report", text: "A tester reported a crash in" },
}

// App Store Connect only sends IDs and states, so the app's name and numeric ID come
// from query parameters you add to the webhook URL: ?app=My%20App&app_id=1234567890
function appFrom(request) {
    const query = request.query || {}
    const name = typeof query.app === "string" && query.app.trim() ? query.app.trim() : null
    const id = /^\d+$/.test(String(query.app_id ?? "")) ? String(query.app_id) : null

    return {
        name,
        link: id ? `https://appstoreconnect.apple.com/apps/${id}` : "https://appstoreconnect.apple.com/",
    }
}

function handleRequest(request) {
    const data = (request.body || {}).data
    const app = appFrom(request)

    // "Test" in App Store Connect sends a ping; confirm the connection with a quiet push.
    if (data?.type === "webhookPingCreated") {
        return {
            title: "🔔 App Store Connect connected",
            message: `Webhook for ${app.name ?? "your app"} is working`,
            topic: "App Store Connect",
            priority: -1,
        }
    }

    // Guard: only handle this event type, so other App Store Connect events stay quiet.
    if (!Object.prototype.hasOwnProperty.call(FEEDBACK, data?.type)) {
        console.log(`Ignoring App Store Connect event "${data?.type ?? "unknown"}"`)
        return null
    }

    const feedback = FEEDBACK[data.type]

    return {
        title: feedback.title,
        message: `${feedback.text} ${app.name ?? "your app"}`,
        topic: "App Store Connect",
        priority: 0, // Normal — worth reading soon, but it'll keep
        buttons: [{ cta: "Open App Store Connect", url: app.link }],
    }
}
```

## App Store Connect BETA_FEEDBACK_SCREENSHOT_SUBMISSION_CREATED webhook payload (sample)

```json
{
  "data": {
    "id": "4a9eacca-e53f-4006-85db-aa18c515663a",
    "type": "betaFeedbackScreenshotSubmissionCreated",
    "version": 1,
    "attributes": {
      "timestamp": "2026-09-28T09:29:36.16Z"
    },
    "relationships": {
      "instance": {
        "data": {
          "id": "AD8JvKbr0BK0Cj9OnM6WO6I",
          "type": "betaFeedbackScreenshotSubmissions"
        },
        "links": {
          "self": "https://api.appstoreconnect.apple.com/v1/betaFeedbackScreenshotSubmissions/AD8JvKbr0BK0Cj9OnM6WO6I"
        }
      }
    }
  }
}
```

## FAQ

### Does this work on iPhone and Android?

Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification.

### Do I need to write code?

No. Install the recipe in Studio, paste your webhook URL into App Store Connect and you are done. The code is there if you want to change the text, the sound or the buttons.

### Can I change what the notification says?

Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save.

### What does it cost?

JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.
