# About Statused (/about)
## Overview [#overview]
Statused monitors your app’s release lifecycle across App Store Connect and Google Play. It sends alerts when builds are processed, app versions go live, get rejected, or require action like accepting new agreements. Notifications appear where your team works — Slack or custom webhooks — so everyone stays aligned without constant check-ins.
## Who It’s For [#who-its-for]
Statused is helpful for anyone shipping mobile apps, but it shines the most for teams:
- **QA Teams** — Get notified the moment TestFlight or internal Android builds are ready for testing.
- **Marketing Teams** — Know exactly when releases go live with precise timing for campaign coordination and ASO updates.
- **Project Managers** — Track every build and release with searchable history and team visibility.
- **Developers** — Stop fielding "is the app live yet?" questions and get instant alerts for rejections or required actions.
The larger your team, the more value you’ll see.
## Supported Platforms [#supported-platforms]
- [iOS & more](/integrations/app-store-connect) — Monitors iOS apps via App Store Connect, plus macOS, watchOS, tvOS, and visionOS apps.
- [Android](/integrations/google-play-console) — Monitors Google Play Console apps, including production and testing tracks.
## What You Get [#what-you-get]
* **Real-time notifications** when builds are processed or apps go live
* **Team coordination** through Slack and webhook integrations
* **Complete history** of all status changes your team can search and reference right in Slack
## Data Security [#data-security]
For a full overview of our security controls — encryption, access controls, secure development, infrastructure, and subprocessors — see our [Security Practices page](https://statused.com/security).
### API Key Security [#api-key-security]
* All sensitive information, including API keys, is encrypted in transit and at rest using industry-standard encryption.
* Secrets are write-only and non-retrievable after upload — they cannot be viewed or exported through the dashboard or API.
* Secure key rotation is supported.
### Data Handling [#data-handling]
* Minimal data collection (only status-related information)
* No app content or code access
* No use of private APIs that could put your developer account at risk
* Least-privilege access model: Statused only accesses the data necessary for monitoring
## Performance and Reliability [#performance-and-reliability]
### Monitoring Uptime [#monitoring-uptime]
* 99.9%+ uptime target
* [Public status page](https://status.statused.com) for service monitoring
### Scalability [#scalability]
* Designed to handle monitoring for hundreds of apps
* Efficient API usage to respect platform limits
## Learn More [#learn-more]
Ready to dive deeper? See [How Statused Works](/how-it-works) for a look under the hood and follow the [Quickstart](/quickstart) to get set up.
For pricing details and other questions, check our [FAQ](/faq).
# Product Updates (/changelog)
### AI-friendly documentation [#ai-friendly-documentation]
v4.14.0 · Improvement · June 14, 2026
The docs now work hand in hand with AI tools. Every page has a “Copy Markdown” button and an “Open” menu that sends the page straight to ChatGPT, Claude, or Cursor. The full site is also published as [`llms.txt`](/llms.txt) and [`llms-full.txt`](/llms-full.txt), and you can append `.md` to any docs URL to fetch its raw Markdown.
We also gave the documentation a broader refresh, including a framework upgrade and assorted UI polish and content fixes throughout.
***
### No more duplicate notifications from forwarded emails [#no-more-duplicate-notifications-from-forwarded-emails]
v4.13.2 · Bug Fix · June 14, 2026
When the same Google Play email reached Statused more than once — common when several addresses forward notifications to us — a single status change could trigger the same Slack and webhook notification multiple times. Statused now sends one notification per actual status change, no matter how many copies of the email arrive.
***
### Reliably detect Google Play rejection emails [#reliably-detect-google-play-rejection-emails]
v4.13.1 · Bug Fix · June 11, 2026
Google Play “app rejected” emails sometimes weren’t being parsed correctly, and would fail to emit the “app rejected” notification. This has been fixed and recent notifications of this kind have been sent with a small delay (less than 1 hour).
***
### New dashboard summarizing what Statused has done for you [#new-dashboard-summarizing-what-statused-has-done-for-you]
v4.13.0 · New Feature · May 31, 2026
Once you’re done setting up your first workflow, the home page now shows a dashboard with a running tally of your team’s release activity since getting started. Check out the new “Dashboard” tab to see how many releases we’ve monitored, notifications sent, and more. It’s an easy way to see the impact Statused is having on your release process over time.
Also, the profile menu now includes quick links to the Documentation and Changelog, making it easier to find the latest updates and guides.
***
### Published a Security Practices page [#published-a-security-practices-page]
v4.11.0 · Improvement · May 31, 2026
Statused now has a public [Security Practices page](https://statused.com/security) documenting how we protect customer data: encryption in transit and at rest, least-privilege store access, our secure development and AI-assisted coding process, infrastructure, monitoring, data retention, and subprocessors. We also tightened up several processes that improve the overall security of the platform.
***
### Send notifications to Claude Code Routines [#send-notifications-to-claude-code-routines]
v4.10.0 · New Feature · May 16, 2026
Statused now supports Claude Code Routines as a first-class destination type alongside Slack and custom webhooks. Provide a routine's API trigger URL and bearer token, and Statused will fire the routine on every matching app or build status change.
***
### Fix Google Play notification queue stalling on duplicate “update is live” emails [#fix-google-play-notification-queue-stalling-on-duplicate-update-is-live-emails]
v4.9.4 · Bug Fix · April 25, 2026
A duplicate or metadata-only “update is live” email could repeatedly fail and block all subsequent Google Play emails for the same account from being processed. Each email is now processed independently, and concurrent emails for the same Google Play package are also serialized to avoid out-of-order notifications.
***
### Fix App Store build status notifications and 100% rollout detection [#fix-app-store-build-status-notifications-and-100-rollout-detection]
v4.9.3 · Bug Fix · April 25, 2026
* Build status notifications now pair the build number with the marketing version it actually belongs to. Previously, when the latest TestFlight upload belonged to a newer in-flight version than the latest App Store release, notifications combined a stale marketing version with the new build number.
* Fixed an issue where App Store phased rollouts wouldn't emit a notification when reaching 100% rollout.
***
### Fix premature release notifications from unpropagated Play Store data [#fix-premature-release-notifications-from-unpropagated-play-store-data]
v4.9.2 · Bug Fix · April 17, 2026
v4.9.1 introduced a regression, causing Google Play notifications to be sent prematurely for users with Google Play Email Forwarding enabled. This has been fixed.
***
### Recover missed Google Play release notifications [#recover-missed-google-play-release-notifications]
v4.9.1 · Bug Fix · April 17, 2026
When a "Your update is live" email arrived before Google Play's Publisher API had propagated the new release, Statused could silently miss both the initial rollout and any subsequent notifications for that version. This has been fixed by being more resilient to timing issues between email and API data, ensuring that all relevant notifications are sent even if the API data isn't immediately available.
***
### App Store phased release tracking [#app-store-phased-release-tracking]
v4.9.0 · New Feature · April 8, 2026
Statused now tracks Apple's phased release progression for iOS apps. You'll get notified as your rollout advances through each day of Apple's 7-day schedule (1% → 2% → 5% → 10% → 20% → 50% → 100%), and when it's paused, resumed, or completed. This brings feature parity with the existing Google Play staged rollout monitoring.
***
### Fixed missing notifications when multiple workflows monitor the same app [#fixed-missing-notifications-when-multiple-workflows-monitor-the-same-app]
v4.8.4 · Bug Fix · March 24, 2026
When multiple workflows monitored the same app (e.g., one sending to `#eng-deploys` and another to `#announcements`), only the first workflow received notifications. This affected both Google Play and App Store workflows, across both email-triggered and polling-based status checks. All matching workflows now receive notifications independently.
***
### Fixed Gmail forwarding auto-confirmation [#fixed-gmail-forwarding-auto-confirmation]
v4.8.3 · Bug Fix · February 20, 2026
Fixed a bug where Gmail forwarding verification emails were marked as confirmed but the forwarding was never actually activated. If auto-confirmation fails, users now receive a fallback email with a link to verify manually.
***
### Fixed repeated welcome messages [#fixed-repeated-welcome-messages]
v4.8.2 · Bug Fix · February 13, 2026
Fixed welcome message incorrectly being sent repeatedly.
***
### Fixed duplicate notifications on status transition [#fixed-duplicate-notifications-on-status-transition]
v4.8.1 · Bug Fix · February 6, 2026
Fixed duplicate notifications when app status transitions from "live" to "completed".
***
### Monitor app versions beyond final statuses [#monitor-app-versions-beyond-final-statuses]
v4.8.0 · Improvement · February 3, 2026
App versions are now monitored beyond previously assumed "final" statuses, catching cases where apps could be halted even though their distribution was already at 100%.
***
### Fixed stale rollout percentage [#fixed-stale-rollout-percentage]
v4.7.3 · Bug Fix · January 31, 2026
Fixed stale rollout percentage data in phased rollout notifications.
***
### Fixed repeated pending agreement notifications [#fixed-repeated-pending-agreement-notifications]
v4.7.2 · Bug Fix · January 13, 2026
Fixed pending agreement notifications being sent repeatedly.
***
### Fixed caching causing missed status updates [#fixed-caching-causing-missed-status-updates]
v4.7.1 · Bug Fix · January 9, 2026
Fixed aggressive caching that could cause missed status updates.
***
### Billing management improvements [#billing-management-improvements]
v4.7.0 · Improvement · December 29, 2025
The billing portal now connects directly with Chargebee for a smoother account management experience.
***
### Fixed duplicate release notifications [#fixed-duplicate-release-notifications]
v4.6.2 · Bug Fix · December 26, 2025
Fixed edge case causing duplicate release notifications.
***
### Fixed halted status for non-phased rollouts [#fixed-halted-status-for-non-phased-rollouts]
v4.6.1 · Bug Fix · December 9, 2025
Fixed halted status handling for apps not using phased rollouts.
***
### Google Play halted rollout notifications [#google-play-halted-rollout-notifications]
v4.6.0 · New Feature · December 7, 2025
Notifications are now sent when Google Play automatically halts a phased rollout because a newer version was uploaded.
***
### Improved Google Play API reliability [#improved-google-play-api-reliability]
v4.5.1 · Improvement · November 18, 2025
Improved Google Play API reliability with fewer requests and automatic retry on known transient failures.
***
### Editable destinations [#editable-destinations]
v4.5.0 · New Feature · November 11, 2025
Destinations can now be edited directly — rename them or update the webhook URL without recreating them.
Also includes minor bug fixes.
***
### Fixed Gmail email verification for non-English languages [#fixed-gmail-email-verification-for-non-english-languages]
v4.4.1 · Bug Fix · November 8, 2025
Fixed Gmail automatic email verification process for non-English languages.
***
### Developer documentation and Google Play email forwarding [#developer-documentation-and-google-play-email-forwarding]
v4.4.0 · Announcement · July 2, 2025
Developer documentation is now available at [docs.statused.com](https://docs.statused.com), covering setup guides, tutorials, integration details, and platform-specific documentation.
This release also introduces **Google Play Console email forwarding** (private beta), a new integration method that provides more consistent and timely Android app status notifications by processing emails directly from Google Play. Includes a managed publishing setting for workflows using email forwarding. Learn more at [docs.statused.com/email-forwarding](https://docs.statused.com/email-forwarding).
***
### Workflow improvements [#workflow-improvements]
v4.3.0 · New Feature · June 17, 2025
* Added a new option to **notify only for specific statuses**, reducing noise from irrelevant updates.
* Workflows are now fully editable. You can update the name, destinations, and notification filters at any time.
* Added a toggle to enable or disable individual workflows.
* Improved error handling when the API is temporarily unavailable.
***
### Simplified onboarding [#simplified-onboarding]
v4.2.0 · Improvement · June 6, 2025
Setting up a workflow previously required manually adding your app as a separate step. That step has been removed. When creating a workflow, a dropdown now lists all available apps from your App Store Connect account automatically. For Google Play, you still enter your package name, but the flow is more streamlined.
Additional changes:
* Workflow table now displays app icons and names for easier identification
* Fixed edge cases where webhook destinations failed to deliver notifications
* Added validation when saving API keys, so invalid keys or insufficient permissions are caught immediately
* Patched a security issue related to Google Play integration
***
### Fixed subscription activation for new customers [#fixed-subscription-activation-for-new-customers]
v4.1.1 · Bug Fix · June 2, 2025
Fixed a bug that prevented new customers from starting their subscription from the billing page. The issue was introduced approximately two weeks prior and is now resolved.
***
### Workflow test notifications [#workflow-test-notifications]
v4.1.0 · Improvement · June 2, 2025
You can now send test notifications directly from your workflows to verify setup and debug delivery issues. Find the new button under "actions" in your workflow list.
***
### Statused 4.0 — full rebuild [#statused-40--full-rebuild]
v4.0.0 · Announcement · May 15, 2025
Statused has been rebuilt from the ground up with a new frontend powered by **Next.js**, replacing the original MVP architecture. The rewrite focuses on performance, reliability, and a foundation that supports faster feature development going forward.
Highlights:
* Clearer app management when an app identifier is shared between Android and iOS
* Drag-and-drop support for uploading API key auth files
* Live preview of workflow behavior during creation
* Dark mode
* Various bug fixes
***
### Quality-of-life improvements [#quality-of-life-improvements]
v3.3.0 · Improvement · January 16, 2025
* Added a **test destination** button that sends a sample notification, making it easy to confirm delivery is working.
* Added quick links to the dashboard header for FAQs and system status.
* Minor bug fixes.
***
### Outgoing webhooks [#outgoing-webhooks]
v3.2.0 · New Feature · January 13, 2025
Statused now supports outgoing webhooks as a destination type. While Slack notifications are designed for human consumption, webhooks enable programmatic automations triggered by the same app status events.
See the [webhook integration documentation](/integrations/webhooks) for more details.
***
### First changelog entry [#first-changelog-entry]
v3.1.0 · Announcement · January 13, 2025
This is the first entry in the Statused product changelog. Going forward, all notable changes will be documented here: features, improvements, and fixes.
For context, Statused at this point supports:
* Monitoring app review statuses across Google Play Store and App Store Connect
* Slack notifications for review status changes, TestFlight build availability, and pending App Store Connect agreements
* Multiple workflows routing updates to different Slack channels
* Support for both Slack Incoming Webhooks and Slack Workflows
* Searchable release history within Slack channels
# FAQ (/faq)
## What platforms does Statused support? [#what-platforms-does-statused-support]
Statused fully supports:
* **iOS** apps via App Store Connect
* **Android** apps via Google Play Console
Additionally, Statused should work with:
* **macOS** apps
* **watchOS** apps
* **tvOS** apps
* **visionOS** apps
*Note: Testing for these additional platforms has been limited. If you encounter issues, [let us know](/help) and we'll resolve them quickly.*
## How can I invite my team members? [#how-can-i-invite-my-team-members]
Team management is not available yet, but it's coming soon! Sign up and we'll notify you when team management features are available.
## Does Statused track phased rollouts and staged rollout percentages? [#does-statused-track-phased-rollouts-and-staged-rollout-percentages]
**Google Play: Yes.** Statused monitors staged rollout percentage changes, halted rollouts, resumed rollouts, and rollout completion. You'll get a notification in Slack or your webhook every time the rollout state changes — including the exact percentage. See [Google Play Phased Rollout Monitoring](/integrations/google-play-console#phased-rollout) for details.
**App Store: Yes.** Statused tracks Apple's 7-day phased release progression and notifies you as the rollout advances through each day (1% → 2% → 5% → 10% → 20% → 50% → 100%), as well as when it's paused, resumed, or completed. See [App Store Phased Release](/integrations/app-store-connect#phased-release) for details.
## Does Statused notify when a Google Play rollout is halted or rolled back? [#does-statused-notify-when-a-google-play-rollout-is-halted-or-rolled-back]
Yes. If a phased rollout is halted — whether manually by your team or automatically by Google — Statused sends a notification that includes the percentage the rollout was halted at. You'll also be notified when a halted rollout is resumed.
## Can Statused detect when a Google Play app is approved but not yet published (Managed Publishing)? [#can-statused-detect-when-a-google-play-app-is-approved-but-not-yet-published-managed-publishing]
Google Play's API does not expose a distinct "approved" status when [Managed Publishing](/glossary#managed-publishing) is enabled. The API status remains **Completed** or **In Progress** regardless of whether Google has actually finished reviewing your app. Google also does not guarantee sending an "approved" email in all cases, so there is no reliable signal for this state. This is a [known limitation](/known-limitations#google-play-console-api) of the Google Play ecosystem.
**Workaround:** Some Statused users set their initial rollout to a very small percentage (e.g. 1%) instead of using Managed Publishing. When Google approves the app and it goes live to that 1%, Statused detects the status change and sends a notification. This effectively acts as an "approved and live" signal with minimal user exposure, letting you then decide whether to ramp up to 100%. See [Phased Rollout Monitoring](/integrations/google-play-console#phased-rollout) for how these notifications work.
## Does Statused offer a direct API for fetching app store status? [#does-statused-offer-a-direct-api-for-fetching-app-store-status]
No. If this is something you're interested in, please [let us know](/help) so we can consider it for future development.
Meanwhile, outgoing webhooks provide real-time notifications to your backend, which might meet your needs for integration with other systems. You can read more about it [Webhook](/integrations/webhooks).
# Glossary (/glossary)
## API Key [#api-key]
A unique authentication token that allows Statused to securely access app store APIs (like App Store Connect or Google Play Console) on your behalf. API keys are required to monitor your apps' statuses and retrieve publishing information.
## Destination [#destination]
A configured endpoint where Statused sends notifications about app store status changes. We currently support Slack and custom webhooks as destinations, with more options coming soon.
## Managed Publishing [#managed-publishing]
A setting enabled for your app in Google Play Console that allows your team to control when the app goes live once it's been approved, instead of automatically releasing the app after its approval.
This is a term defined by Google. You can learn more about Managed Publishing in the [Control when app changes are reviewed and published - Google Play Console help](https://support.google.com/googleplay/android-developer/answer/9859654?hl=en).
## Track [#track]
A specific release channel or version stream for your app, such as production, beta, or alpha tracks. Different tracks allow you to manage multiple versions of your app simultaneously and control which users receive which updates.
This is a term defined by Google. You can learn more about tracks in the [APKs and Tracks - Google Play Developer API](https://developers.google.com/android-publisher/tracks).
## Workflow [#workflow]
Workflows connect app updates to destinations. They tell us which apps need to be monitored, how, and which destination to inform when a change is detected.
# Get Help (/help)
Need help, have feedback, or a feature request? Email us at [support@statused.com](mailto:support@statused.com) — we’re happy to help!
We also love hearing how Statused is making your team's life easier! Share your wins, big or small, and we might feature them in our next newsletter or on social media. Your success inspires us to keep improving!
## Response Times [#response-times]
We typically respond to requests within a couple hours, any day of the week.
## Other Ways to Get Help [#other-ways-to-get-help]
* **[FAQ](/docs/faq)**: Quick answers to common questions
* **[System Status](https://status.statused.com)**: Check if there are any ongoing issues
# How Statused Works (/how-it-works)
## Overview [#overview]
Statused keeps your team in the loop by continuously monitoring your mobile apps through official Apple and Google APIs, plus optional Google Play Console email updates. When something changes, we instantly translate those updates into clear, actionable notifications.
You get notifications delivered exactly where your team works — whether that's Slack, webhooks, or custom integrations. Out of the box, we notify you about every status change, but you have complete control to customize notifications based on what matters most to your workflow.
## What We Monitor [#what-we-monitor]
- [App Store Connect](/integrations/app-store-connect)
- [Google Play Console](/integrations/google-play-console)
## Where Notifications Go [#where-notifications-go]
- [Slack](/integrations/slack)
- [Custom Webhooks](/integrations/webhooks)
- [Discord](/integrations/discord)
- **Taking Requests!** — Interested in other integrations? [Let us know](/help)! We can add support for more platforms based on demand.
## Workflows: The Smart Connection Between Apps and Teams [#workflows]
### How Workflows Work [#how-workflows-work]
Think of workflows as smart routing rules that connect your **apps** (and their specific status changes) to the right **destinations** (like Slack channels or webhooks). This gives you precise control over who gets notified about what — no more notification fatigue in that busy Slack channel 😅
#### Real-World Examples [#real-world-examples]
**Scenario 1: QA Team TestFlight Notifications**
```
When: iOS app "MyApp" finishes processing a TestFlight build on Apple's end
Then: Notify #qa-ios-testflight channel
Result: QA team knows immediately when builds are ready for testing
```
**Scenario 2: Marketing Launch Alerts**
```
When: Android app "MyApp" goes live on production track
Then: Notify #marketing-android-releases channel
Result: Marketing team can coordinate launch activities in real-time
```
### Fine-Tune Your Notifications [#fine-tune-your-notifications]
* **Status Filtering**: Choose exactly which status changes matter to each team
* **TestFlight Control**: Decide if beta builds should trigger notifications (iOS apps)
* **Multi-Workflow Setup**: Create multiple workflows per app for different team needs
* **Track Selection**: Monitor specific tracks like production, beta, or internal testing (Android apps)
## Where to go from here [#where-to-go-from-here]
Ready to see Statused in action? Jump into our [Quickstart Guide](/quickstart) to get rolling 🚀
# Statused Documentation (/)
## Welcome to Statused [#welcome-to-statused]
Tired of being asked *"Is the app live yet?"* — or maybe **you** are the one asking that? 😉
Manually checking app stores or waiting around for an email is the worst. Statused fixes that by giving your team real-time notifications about what’s actually happening — so your engineers don’t have to.
No more guessing if your build is ready for testing, got rejected, or wondering if that production release actually made it to users. We monitor your apps and tell your team (or CI system) exactly when things happen and what actions to take.
## Start Here [#start-here]
- [What is Statused?](/about)
- [Get Set Up Fast](/quickstart)
- [How It All Works](/how-it-works)
## What You'll Set Up [#what-youll-set-up]
Getting Statused working for your team involves a few straightforward steps:
### Connect your app stores [#connect-your-app-stores-step]
Generate API keys so we can monitor your apps (don't worry, we only need read access)
### Choose your notification destinations [#choose-your-notification-destinations-step]
Slack channels, Discord, custom webhooks, or whatever works for your team
### Create workflows [#create-workflows-step]
Tell us which apps should notify which teams (QA gets TestFlight builds, marketing gets production releases, etc.)
### Sit back and relax 🌴🏖️ [#sit-back-and-relax-️-step]
No more of being asked "Is the app live yet?" by your peers. We've got you covered.
## Need Help? [#need-help]
Something not working? Check our [Support Hub](/help) or reach out directly. We're here to help you get set up properly.
# Known Limitations (/known-limitations)
### Google Play Console API [#google-play-console-api]
Google Play Console's API was designed to provide statuses that reflect developers' intentions, rather than the actual state of the app as it goes through the review and release process.
This leads to several limitations and discrepancies — for example, the API may show an app as **"Completed"** or **"In Progress"**, even if it hasn't been reviewed by Google yet, is still pending developer release, or has already been rejected.
This is a known issue that affects multiple tools and developers. Google is aware, but they said there's no specific timeline for getting this fixed.
You can support the effort by upvoting the following issues on Google’s public tracker:
* [**Issue #179708468: Status Discrepancy in Google Play API**](https://issuetracker.google.com/issues/179708468)
* [**Issue #208842993: Additional Play API Status Issues**](https://issuetracker.google.com/issues/208842993)
Your +1 helps signal that this is an important issue for the developer community.
> To counteract this, Statused uses a combination of API polling, email parsing, and broader context to provide the most accurate status updates possible. Read more about how to set this up in [Google Play Email Forwarding](/email-forwarding).
### Managed Publishing Approval Detection [#managed-publishing-approval-detection]
When [Managed Publishing](/glossary#managed-publishing) is enabled, Google Play's API does not expose a distinct "approved" status after review. The API status remains unchanged whether the app is still under review or has already been approved and is waiting for you to publish. Google also does not guarantee sending an approval email in all cases, making it impossible to reliably detect the exact moment of approval.
> **Workaround:** Instead of Managed Publishing, set your initial rollout to a small percentage (e.g. 1%). Statused will notify you when Google approves and releases your app to that 1%, effectively acting as an approval signal with minimal user exposure. You can then ramp up to 100% when ready. See [Phased Rollout Monitoring](/integrations/google-play-console#phased-rollout) for details.
### Android App Discovery Limitation [#android-app-discovery-limitation]
We’ve received feedback from users asking us to automatically list the Android apps linked to their Google Play account when creating a new Workflow, instead of requiring manual entry. Unfortunately, this isn’t possible — Google Play Console doesn’t provide an API to list the apps associated with a service account or API key.
As a result, you’ll need to manually enter your app’s **package name** (e.g., `com.example.myapp`) when creating your workflows.
> You can find your app’s package name in the Google Play Console URL when viewing your app, or in your app’s `build.gradle` file under the `applicationId` field.
# Quickstart (/quickstart)
## Overview [#overview]
Manually checking app stores or waiting around for an email is the worst. Statused fixes that by giving your team real-time notifications about what’s actually happening — so your engineers don’t have to.
No more guessing if your build is ready for testing, got rejected, or wondering if that production release actually made it to users. We monitor your apps and tell your team (or CI system) exactly when things happen and what actions to take.
If you're ready to get started, let's get right into it!
## Create Your Account [#create-your-account-step]
[Sign up for Statused](https://statused.com/signup) and start your free trial, if you haven't already.
## Connect Your App Store [#connect-your-app-store-step]
You'll need admin access to your app store console for this step. Choose your platform:
- [iOS Apps (App Store Connect)](/integrations/app-store-connect/create-api-key) — Follow our step-by-step guide to create the API key needed to monitor your Apple platform apps.
- [Android Apps (Google Play Console)](/integrations/google-play-console/create-api-key) — Set up a service account to enable monitoring for your Android apps on Google Play.
> **New to API keys?**
>
> Don't worry, our guides include helpful screenshots and clear step-by-step instructions. Most teams finish this in under 5 minutes! 🚀
## Set Up Slack Notifications [#set-up-slack-notifications-step]
Time to choose where you want notifications delivered. Most teams start with Slack since it's where development conversations already happen.
Head to our [Slack Integration](/integrations/slack) page and follow the integration flow to connect your workspace.
> **Using something else?**
>
> We also support [Discord](/integrations/discord) and [custom webhooks](/integrations/webhooks). If you need another integration, [let us know](/help)! We prioritize based on user requests.
## Create Your First Workflow [#create-your-first-workflow-step]
Here's where the magic happens: setting up your first automated notification workflow.
> **Free trial required:** Make sure you've started your trial at [Billing](https://www.statused.com/app/billing) to create workflows.
1. In your dashboard, navigate to Workflows › Add Workflow
2. Give it a descriptive name like `YourAppName Production → #eng-ios-releases`
3. Select your API key, app, choose your destination, and fill in the other required details
* For Android apps, we recommend informing us whether Managed Publishing is enabled. More on that in our [Google Play Email Forwarding Guide](/email-forwarding).
* Notice that you can see a live preview of your workflow configuration at the bottom of the page, as you configure it.
4. Hit Add Workflow when you're happy with your configuration
## You're Live! What to Expect [#youre-live-what-to-expect]
> **Monitoring is active**
>
> Your workflow is now live and monitoring your app. No more manual status checking required.
You'll receive a welcome message in Slack confirming your workflow is active.
The next time your app status changes (submission, review, rejection, live), you'll get an instant notification with all the details your team needs.
## Next Steps: Level Up Your Setup [#next-steps-level-up-your-setup]
- [Email Forwarding (Android)](/email-forwarding) — Get more accurate status updates than the API alone provides for your Android apps.
- [Webhooks & CI/CD](/integrations/webhooks) — Integrate with your CI/CD pipeline or custom tools using our webhook system.
- [Learn How Statused Works](/how-it-works) — Learn how smart monitoring and targeted notifications keep your team in sync with the right updates.
***
**Need help?** Check our [support hub](/help) or reach out directly — we respond fast and love helping teams get their notifications flowing smoothly.
# Resources (/resources)
## Official App Store APIs [#official-app-store-apis]
* [**App Store Connect API Documentation**](https://developer.apple.com/documentation/appstoreconnectapi)
* [**Google Play Developer API Getting Started**](https://developers.google.com/android-publisher/getting_started)
## Integrations [#integrations]
Set up notifications without requiring Slack admin permissions:
* [**Create Workflows That Start with a Webhook**](https://slack.com/help/articles/360041352714-Create-workflows-that-start-with-a-webhook)
# Configuring Gmail Inbox Forwarding (/email-forwarding/gmail-inbox)
Follow these steps to forward Google Play Console emails from your Google Workspace (Gmail) inbox to Statused. This setup captures status changes that the API might miss, giving you complete visibility into your app's journey through Google's review process.
> **Before you start**
>
> You'll need a Google Play API Key already added to your Statused account. This generates the unique forwarding address you'll configure in the steps below.
## Open Gmail Settings [#open-gmail-settings-step]
In Gmail, click the gear icon
⚙️ in the top right and select
See all settings
## Add Your Forwarding Address [#add-your-forwarding-address-step]
1. Navigate to the
Forwarding and POP/IMAP tab
> **Don't see this tab?**
>
> If you only see a
POP/IMAP Download tab instead, your admin has disabled email forwarding. Ask them to enable it, or use our [Google Workspace email routing guide](/email-forwarding/google-workspace-routing) instead.
2. Click
Add a forwarding address
> **Don't see this button?**
>
> If your tab says
Forwarding and POP/IMAP but you don't see the
Add a forwarding address button, this is a malformed page. Fully refresh the page (close and reopen Gmail) to fix it, or try a different browser. This is a real issue in Gmail that can happen occasionally.
3. Enter your Statused forwarding address:
```
@forward.statused.com
```
You can find your Email Forwarding ID in your [Statused API Keys](https://www.statused.com/app/auths) page by clicking the warning ⚠️ icon next to your Google Play API Key.
> **Keep this address private**
>
> This address is unique to your account and processes emails specifically for your apps. Don't share it publicly.
4. Click Next
## Wait for Confirmation [#wait-for-confirmation-step]
After adding your forwarding address, Gmail sends a verification email to your Statused forwarding address.
**Here's what happens automatically**: Our system verifies the forwarding address for you and sends you a confirmation email once it's active. If automatic verification fails, you'll receive an email with manual instructions.
> **Wait for our confirmation!** Don't proceed to the next step until you receive a confirmation email from Statused. This usually takes less than a minute.
## Create a Filter for Google Play Emails [#create-a-filter-for-google-play-emails-step]
Now create a filter to automatically forward Google Play Console emails:
1. In the Gmail search bar, click the advanced filters icon
2. In the
From field, enter the address shown below
3. Click
Create filter
- Google sends notifications from multiple addresses, so **the safest approach is to forward all emails from any address ending in `@google.com`**. > **We strongly recommend forwarding all emails from @google.com** > > Google occasionally changes the sender addresses used for notifications. If you only forward from specific addresses, you risk missing important updates. However, if your team needs more restrictive filtering, these are the current most common sender addresses: * `noreply-play-developer-console@google.com` * `no-reply-googleplay-developer@google.com`
## Configure the Filter Action [#configure-the-filter-action-step]
On the filter options screen:
1. Check the
Forward it to checkbox ☑️
2. Select the Statused forwarding address you added earlier
3. Click
Create filter

> **You're all set!**
>
> Google Play Console emails will now be automatically forwarded to Statused, giving you more accurate app status updates.
## What Happens Next [#what-happens-next]
New Google Play emails will be forwarded automatically to Statused.
We'll process these emails alongside API data to give you the most complete picture of your app's status.
Our system prevents duplicate alerts by intelligently combining email and API information into single, clear notifications.
## Troubleshooting [#troubleshooting]
**Missing the "Forwarding and POP/IMAP" tab?** Your Google Workspace admin has likely disabled automatic email forwarding.
**Two solutions:**
1. **Ask your admin to enable forwarding** in Google Workspace Admin › Gmail Settings › End User Access (see image below)
2. **Use our alternative method**: Set up [Google Workspace email routing](/email-forwarding/google-workspace-routing) instead (admin-friendly option)
> **Pro tip**
>
> Many admins prefer the email routing method since it gives them more control over which addresses can be forwarded to.
> **Having trouble with the setup?**
>
> We're here to help — just [reach out to us](/help) and we'll get your forwarding working smoothly.
# Configuring Google Workspace Email Routing (/email-forwarding/google-workspace-routing)
This guide is for Google Workspace admins who need to set up email routing at the organization level. Use this method when individual users can't set up Gmail forwarding due to admin restrictions, or when you want a centralized solution that doesn't depend on individual user accounts.
> **Before you start**
>
> **Admin access required**: You'll need Google Workspace admin privileges to complete this setup. If you're not an admin, share this guide with your workspace owner or admin.**Google Play API key required**: You'll need a Google Play API Key already added to your Statused account. This generates the unique forwarding address you'll configure in the steps below.
## Access Gmail Admin Settings [#access-gmail-admin-settings-step]
Open the Google Admin console at [admin.google.com](https://admin.google.com) and navigate to
Apps ›
Google Workspace ›
Gmail ›
Routing
## Create a New Routing Rule [#create-a-new-routing-rule-step]
Click in
Configure to open the routing rule configuration dialog. If you already have routing rules, you may need to click
Add another rule.
## Name Your Rule and Set Direction [#name-your-rule-and-set-direction-step]
1. Enter a descriptive name like **"Forward Google Play emails to Statused"**
2. Under
Email messages to affect, select
Inbound
## Add the Statused Forwarding Address [#add-the-statused-forwarding-address-step]
1. Scroll down to the
Also deliver to section
2. Click
Add more recipients
3. Enter your Statused forwarding address:
```
@forward.statused.com
```
You can find your Email Forwarding ID in your [Statused API Keys](https://www.statused.com/app/auths) page by clicking the warning ⚠️ icon next to your Google Play API Key.
> **Keep this address private**
>
> This address is unique to your account and processes emails specifically for your apps. Don't share it publicly.
## Save and Configure Advanced Options [#save-and-configure-advanced-options-step]
Click
Save, then click
Show options to configure the targeting settings.
## Configure User Targeting and Sender Filtering [#configure-user-targeting-and-sender-filtering-step]
In the advanced options, configure these settings:
* **Account types to affect**: Check the
Users checkbox
* **Envelope filter**:
1. Select
Only affect specific envelope senders
2. Choose
Pattern match
3. Enter this pattern: `.*@google.com`
> **Need more specific filtering?**
>
> Google sends notifications from multiple addresses, so **the safest approach is to forward all emails from any address ending in `@google.com`**.
>
> > **We strongly recommend forwarding all emails from @google.com**
> >
> > Google occasionally changes the sender addresses used for notifications. If you only forward from specific addresses, you risk missing important updates.
>
> However, if your team needs more restrictive filtering, these are the current most common sender addresses:
>
> * `noreply-play-developer-console@google.com`
> * `no-reply-googleplay-developer@google.com`To use this more targeted filtering, use this pattern instead:```
> (noreply-play-developer-console|no-reply-googleplay-developer)@google.com
> ```
## Save and Verify the Rule [#save-and-verify-the-rule-step]
Click
Save to apply the routing rule. You should see a confirmation that the rule was created successfully.
> **Authentication prompt**
>
> Google may ask you to re-authenticate since this is a sensitive admin operation. If this happens, complete the authentication and **verify the rule was actually saved** - some users need to recreate the rule after re-authenticating.
## What Happens Next [#what-happens-next]
* Google Play emails sent to any user in your organization will be automatically forwarded to Statused in addition to reaching their original recipient.
* Statused will process these emails alongside API data for more complete app status tracking.
* Our system automatically handles email deduplication, so you won't see duplicate notifications in Statused.
> **Setup complete!**
>
> Your Google Workspace email routing is now active. Users can continue using the standard Gmail inbox while Statused receives copies of all Google Play notifications.
***
> **Having trouble with the setup?**
>
> We're here to help — just [reach out to us](/help) and we'll get your routing working smoothly.
# Google Play Email Forwarding (/email-forwarding)
## Overview [#overview]
### The Problem [#the-problem]
Google Play Console’s API has a frustrating limitation that’s been around for years. As soon as you submit an app to production, the API changes the status from "draft" to "in progress" or "completed." Sounds good, right? Wrong.
Those are the final statuses the API will ever show you. Whether your app gets pulled for review, sits in managed publishing queue, or successfully launches to users — the API status stays the same. You’re flying blind on what’s actually happening.
### The Solution [#the-solution]
Email forwarding fixes this mess. Google sends emails about your app’s actual journey through review and release, containing the real status updates the API won’t give you.
By forwarding these emails to Statused, we parse them and give you accurate notifications about what’s actually happening. You’ll know when your app is genuinely live, not just when it entered Google’s "just-not-a-draft" state.
> Specifically, this setup allows us to capture these additional statuses that the API misses:- **Phased Rollout Is In Progress**: Google approved your app and it's rolling out to users gradually
> - **Phased Rollout Halted**: You or your team paused the phased rollout
> - **Phased Rollout Finished**: Rollout completed and the app is live for everyone
> - **Live**: Google approved your app and it's available to all users in the Play Store
> - **Rejected**: Google rejected your app during review. Time to fix issues and resubmitMost importantly, you’ll get these notifications at the *right time*, instead of prematurely or not at all.
### How It Works [#how-it-works]
Set up email forwarding for Google Play Console emails, to a special Statused inbox. We’ll monitor these emails and translate them into the status updates you actually need, and deliver them to your configured Destinations. The process is straightforward, and we’ll walk you through the specific steps below.
## Configuring Email Forwarding [#configuring-email-forwarding]
> **Before You Start**
>
> This feature is currently in beta. To request access, [contact us](/help) and we’ll enable it for your account.
You’ll need the information below regardless of your email provider. There are two parts to set up: the *source* (who you’re forwarding emails *from*) and the *destination* (who you’re forwarding them *to*).
### Prerequisites [#prerequisites]
#### Enable Email Notifications in Google Play Console [#enable-email-notifications-in-google-play-console]
Before setting up email forwarding, make sure your Google Play Console account is configured to send app status update emails. Without this, there won’t be any emails to forward.
1. Open [Google Play Console](https://play.google.com/console)
2. Go to **Settings > Email Notifications**
3. Expand each notification category and ensure **"Emails on"** is selected on all of them.
* Don't worry about the volume — Google Play sends very few emails year-round.
* If you prefer the bare minimum, enable at least **Publishing updates > App publishing updates** and **SDK developer reported issues**.
4. Click **Save**
> **Some notifications are optional**
>
> Google Play makes certain email categories mandatory (like policy violations and billing), but **app status update emails are optional** and may be disabled by default. If you only see policy or billing emails from Google Play but no release status emails, this setting is likely the cause.
> **Using Email Recipients for external team members?**
>
> If your organization uses Google Play Console’s [Email Recipients](https://support.google.com/googleplay/android-developer/answer/15771712?hl=en) feature (under **Users and Permissions > Email recipients**), this will not work, as Google doesn’t send emails for "app publishing updates" in any of their supported topics. To solve this, use an actual Google Play Console account to receive email notifications, instead of just setting up Email Recipients.
### Forward Emails From [#forward-emails-from]
Google sends notifications from multiple addresses, so **the safest approach is to forward all emails from any address ending in `@google.com`**.
> **We strongly recommend forwarding all emails from @google.com**
>
> Google occasionally changes the sender addresses used for notifications. If you only forward from specific addresses, you risk missing important updates.
However, if your team needs more restrictive filtering, these are the current most common sender addresses:
* `noreply-play-developer-console@google.com`
* `no-reply-googleplay-developer@google.com`
### Forward Emails To [#forward-emails-to]
Forward all Google Play emails to your unique Statused address:
```
@forward.statused.com
```
You can find your Email Forwarding ID in your [Statused API Keys](https://www.statused.com/app/auths) page by clicking the warning ⚠️ icon next to your Google Play API Key.
> **Keep this address private**
>
> This address is unique to your account and processes emails specifically for your apps. Don't share it publicly.
### Setup Methods [#setup-methods]
Choose the method that works best for your team and email provider:
- [From a specific inbox](/email-forwarding/gmail-inbox) — Forward emails from someone’s inbox to Statused using Filters and Forwarding in Gmail.> - Fastest > - No special permissions required — any individual can set this up > - Fragile: it breaks if the inbox is deleted (e.g. if the person leaves the company)
- [Google Workspace email routing](/email-forwarding/google-workspace-routing) — Use Google Workspace’s email routing to forward emails directly to Statused.> - More robust solution > - Doesn’t depend on a single inbox > - Requires Google Workspace admin permission to set up
- **Other Google Workspace Methods** — Your organization might use Groups, Delegated Inboxes, or other Google Workspace features for email management. If the above methods don’t fit your setup, your Google Workspace admin can help determine the best approach.
There are many ways to set up email forwarding and routing rules within Microsoft 365 depending on your organization’s setup. As these evolve over time, we recommend checking Microsoft’s official documentation for the latest methods, but you will probably need someone in your team with Microsoft 365 admin permissions to set this up.
[All you need to know about automatic email forwarding in Exchange Online - Exchange Team Blog](https://techcommunity.microsoft.com/blog/exchange/all-you-need-to-know-about-automatic-email-forwarding-in-exchange-online/2074888)
Each email provider has its own methods for setting up email forwarding. We recommend checking your provider’s documentation or contact the person responsible for managing your company’s email infrastructure.
## What to Expect [#what-to-expect]
Once email forwarding is active, it may take one full release cycle for Statused to learn your app’s patterns and provide completely accurate notifications. During this initial learning period, some notifications might be less precise as our system builds context about your specific release workflow.
> **Configure managed publishing in your workflows**
>
> If you use Google Play Console’s managed publishing feature, make sure to enable this setting in your Statused workflows. This helps us provide more accurate notification timing and messaging about your release status.You can enable this when creating your workflow or by editing an existing one. Look for the "Managed Publishing" option in the workflow settings.
## Privacy and Security [#privacy-and-security]
We take your privacy seriously. Statused only processes the email headers and content necessary to determine app status updates. We do not store or analyze any personal data from these emails, and we only keep the emails for as long as needed to maintain accurate status tracking and serve your notifications.
# Claude Routines (/integrations/claude-routines)
[Claude Code Routines](https://docs.claude.com/en/docs/claude-code/routines) are Anthropic-managed cloud sessions of Claude Code that run on a schedule, in response to GitHub events, or when called via API. Pointing a Statused destination at a routine lets you turn an app or build status change into real work — auto-investigate a regression the moment a build goes invalid, draft a PR when your app enters review, post a structured summary to your release channel once a rollout completes, or anything else you can describe to Claude.
## Prerequisites [#prerequisites]
* A Claude **Pro**, **Max**, **Team**, or **Enterprise** plan with Claude Code on the web enabled.
* A routine created at [claude.ai/code/routines](https://claude.ai/code/routines) with an **API** trigger configured.
## Setup [#setup]
### Create a routine with an API trigger [#create-a-routine-with-an-api-trigger-step]
In Claude, go to [claude.ai/code/routines](https://claude.ai/code/routines) and create a new routine (or open an existing one). Write the prompt you want Claude to run on each fire — for example, "Investigate why our iOS app's latest build was rejected and draft a fix PR."
Add an **API** trigger to the routine. This is what lets Statused start a run.
### Copy the URL and generate a token [#copy-the-url-and-generate-a-token-step]
From the API trigger's settings:
1. Copy the trigger URL. It looks like `https://api.anthropic.com/v1/claude_code/routines/trig_.../fire`.
2. Click **Generate token** and copy the bearer token. It starts with `sk-ant-oat01-...`.
> **The token is shown only once**
>
> Anthropic displays the bearer token a single time. If you lose it, generate a new one from the same trigger — old tokens can be revoked from the same screen.
### Add the destination in Statused [#add-the-destination-in-statused-step]
1. In your dashboard, go to Destinations › Add Destination.
2. Select the Claude Routines tab.
3. Paste the **Routine URL** and the **Bearer Token**.
4. Hit Add Destination.
### Attach it to a workflow [#attach-it-to-a-workflow-step]
Open or create a workflow under Workflows, pick your app, and select the new Claude Routines destination. From now on, every matching app or build status change will fire the routine.
## How notifications are delivered [#how-notifications-are-delivered]
When a workflow fires, Statused makes a `POST` request to your routine URL with these headers:
| Header | Value |
| :------------------ | :----------------------------------- |
| `Authorization` | `Bearer ` |
| `anthropic-beta` | `experimental-cc-routine-2026-04-01` |
| `anthropic-version` | `2023-06-01` |
| `Content-Type` | `application/json` |
The body is a single field:
```json
{
"text": "The status of your app Markdown Email changed to Ready for Sale"
}
```
Anthropic delivers `text` to your routine as run-specific context alongside the prompt you saved in the routine itself. You can reference it in the prompt as you would any other input — for example, "Use the status change in the trigger context to decide whether to open an issue."
## Rotating the token [#rotating-the-token]
To rotate the bearer token, generate a new one from the routine's API trigger page in Claude, then open the destination's **Edit** page in Statused and paste the new value. The next notification will use the updated token.
## Troubleshooting [#troubleshooting]
> **Beta header may change**
>
> The `anthropic-beta: experimental-cc-routine-2026-04-01` header reflects the current API surface for routines. If Anthropic publishes a new beta version, we'll update it on our side — but if you start seeing issues, [reach out](/help) and we'll make sure your integration is up to date.
## Related [#related]
* [Anthropic — Claude Code Routines](https://docs.claude.com/en/docs/claude-code/routines)
* [Webhooks reference](/integrations/webhooks/reference) — for sending the same status data to your own HTTP endpoint instead.
# Discord (/integrations/discord)
This integration is coming soon! If you're interested in using Discord with Statused, please [let us know](/help) so we can prioritize it.
# Slack (/integrations/slack)
## Supported Integration Methods [#supported-integration-methods]
Statused supports Slack Incoming Webhooks and Slack Workflows.
> **Highlights**
>
> * Requires Slack admin permissions
> * No need to configure anything to format messages
Slack Incoming Webhooks are the easiest way to get started — especially if you already have an internal Slack App set up, which is often the case. You can check if you already have one by accessing your [Slack Apps](https://api.slack.com/apps/) page (admin permissions required).
To integrate Statused with Slack using Incoming Webhooks, follow Slack’s official guide but only through step 3: [Sending messages using incoming webhooks](https://api.slack.com/messaging/webhooks). Create a webhook URL for the channel(s) where you’d like to receive notifications, then [add it as a Destination](https://www.statused.com/app/destinations/add) in your Statused dashboard.
### Preview [#preview]
Once configured, messages will look like this:
> **Highlights**
>
> * No Slack admin permissions required
> * Requires more setup but is a bit more customizable
Slack Workflows let you build custom notification flows without needing Slack admin permissions.
We're specifically interested in the **"Webhook" trigger**, which lets Statused send notifications directly into Slack, formatting messages however you like.
To set it up, follow Slack’s guide: [Create workflows that start with a webhook](https://slack.com/help/articles/360041352714-Build-a-workflow--Create-a-workflow-that-starts-outside-of-Slack). Once you’ve created a webhook URL for your target channel(s), [add it as a Destination](https://www.statused.com/app/destinations/add) in your Statused dashboard.
Then, when configuring your workflow in Slack, use the following variables:
```json
{
message: "The status of your app Markdown Email changed to Ready for Sale",
version: "1.2.3",
status: "Ready for Sale",
track: "Production", # Only present for Android apps
user_distribution: "30%", # Only present for Android apps
store_name: "App Store Connect", # Or "Google Play Console"
status_color: "#1eb6fc", # It's e.g. green, yellow, red, depending on the status
app_name: "Markdown Email",
app_icon_url: "a URL for your app icon",
internal_store_url: "a URL that points to either App Store Connect or Google Play Console",
statused_url: "https://statused.com",
statused_icon_url: "https://statused.com/assets/logo-grayscale.png",
formatted_datetime: "2023-11-14 16:05:34 UTC",
timestamp: "2023-11-14T16:05:34.474Z",
}
```
```json
{
message: "The status of build 1.0 (5) of Markdown Email changed to Valid.",
version: "1.2.3",
status: "Valid",
store_name: "App Store Connect",
status_color: "#1eb6fc", # It's e.g. green, yellow, red, depending on the status
app_name: "Markdown Email",
app_icon_url: "a URL for your app icon",
internal_store_url: "a URL that points to App Store Connect",
statused_url: "https://statused.com",
statused_icon_url: "https://statused.com/assets/logo-grayscale.png",
formatted_datetime: "2023-11-14 16:05:34 UTC",
timestamp: "2023-11-14T16:05:34.474Z",
}
```
```json
{
message: "Action Needed: You Have Pending Agreements.",
status: "Pending Agreement Acceptance",
store_name: "App Store Connect",
status_color: "#1eb6fc", # It's e.g. green, yellow, red, depending on the status
internal_store_url: "a URL that points to App Store Connect",
statused_url: "https://statused.com",
statused_icon_url: "https://statused.com/assets/logo-grayscale.png",
formatted_datetime: "2023-11-14 16:05:34 UTC",
timestamp: "2023-11-14T16:05:34.474Z",
}
```
### Preview [#preview-1]
The messages you receive will depend on how you set up your Slack Workflow, but they can be fully customized to match your team's needs and preferences.
> Statused doesn't have its own Slack App yet. If this is something you're interested in, please [let us know](/help)! It helps us prioritize features that matter to you.
# Creating API Keys for App Store Connect (/integrations/app-store-connect/create-api-key)
This guide walks you through creating API keys for App Store Connect — essential for monitoring your Apple platform apps. We'll cover everything from permissions to the actual key generation process.
## Before You Start [#before-you-start]
Your App Store Connect **Account Holder** must enable API key creation first, if this was never done before (unlikely). This is typically your CTO, founder, or whoever originally set up the account.
You'll need **Admin role** permissions in App Store Connect to complete these steps. If you don't have this access, loop in your App Store Connect Admin.
## Creating Your API Key [#creating]
### Sign in to App Store Connect [#sign-in-to-app-store-connect-step]
Head to [App Store Connect](https://appstoreconnect.apple.com) and select the correct organization from the top-right dropdown if you have multiple.
### Navigate to Team Key settings [#navigate-to-team-key-settings-step]
Go to **Users and Access** › **Integrations** › **App Store Connect API** › **Team Keys**.
### Generate the key [#generate-the-key-step]
Click the
button and give your key a descriptive name like "Statused API Key".
Choose a role with sufficient access:
* ✅ **Admin** (recommended for full compatibility)
* ✅ **App Manager** (works great)
* ✅ **Developer** (minimum required)
* ❌ **Finance** (won't work)
* ❌ **Sales and Reports** (won't work)

> **Pro Tip**
>
> Go with **Admin** role to future-proof your setup. Apple occasionally updates API permissions, and Admin ensures you won't hit any roadblocks down the line. It may also enable additional features in the future without needing to regenerate keys.
### Download and secure [#download-and-secure-step]
Generate the key and immediately download the private key file (`.p8`). **This is your only chance to download it**, so store it somewhere safe.
## What You'll Need for Statused [#what-you-need]
Your App Store Connect API key has three components — grab all three while you're still in the App Store Connect interface:
* **Private Key File**: The `.p8` file you just downloaded
* **Issuer ID**: Found at the top of the API keys page
* **Key ID**: Displayed in the key list after generation
## Next Step: Connect to Statused [#connect]
With your three API key components in hand, you can now connect your App Store Connect account to Statused:
1. Head back to your [Statused dashboard](https://www.statused.com/app) and navigate to API Keys › Add API Key
2. Select App Store Connect as your platform
3. Upload your `.p8` file and fill in the other required details
4. Hit Add API Key — we'll validate everything automatically
> That's it! Your App Store Connect API key is now connected.
## Where to go from here [#where-to-go-from-here]
Now that your App Store Connect account is connected, head back to the [Quickstart](/quickstart) guide to set up your first monitoring workflow.
# App Store Connect Monitoring (/integrations/app-store-connect)
## Overview [#overview]
Statused monitors your Apple apps and turns status changes into clear notifications for your team. We track 4 key areas in App Store Connect to keep you informed at every step of your release process.
## Build Processing Status [#build-processing-status]
Track your builds through Apple's processing pipeline:
* **Processing**: Apple is working on your build (not ready for testing yet)
* **Valid**: Build passed and is ready for TestFlight internal testing
* **Invalid** or **Failed**: Build failed processing (uncommon but it happens)
> Perfect for notifying QA teams when TestFlight builds are ready, or triggering CI webhooks when your app is cleared for App Store submission.
## App Review Progress [#app-review-progress]
The review pipeline has many statuses, but these are the ones that matter:
* **Prepare for Submission**: You're getting your app ready for review
* **Ready for Review**: Confusing name! This means you still need to hit "Submit for Review" in App Store Connect. If you see this status, log in and complete the submission.
* **Waiting for Review**: Your app is in Apple's review queue
* **In Review**: Apple is actively reviewing your app
* **Rejected**: Apple rejected your app and you need to address their feedback
* **Pending Developer Release**: Apple approved your app but you control when it goes live
* **Ready for Sale**: Your app is live on the App Store
> Want the complete list? Check out Apple's [App and submission statuses documentation](https://developer.apple.com/help/app-store-connect/reference/app-and-submission-statuses/).
## Account Health [#account-health]
Keep tabs on your App Store Connect organization:
* **Pending Agreement Acceptance**: New agreements need your signature
* **Agreement Accepted**: All agreements are current
> Critical: Apps can't be submitted or released while agreements are pending. Resolve these immediately to avoid blocking your releases.
## Phased Release [#phased-release]
Apple offers a [phased release](https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases) option that gradually rolls out your update over 7 days following a fixed schedule (1% → 2% → 5% → 10% → 20% → 50% → 100%). Unlike Google Play's staged rollout, you can't set a custom percentage — you can only pause and resume the automatic progression.
Statused tracks your phased release as it progresses through each day. You'll get a notification when:
| Event | Example notification |
| ---------------------- | ------------------------------------------------------------------------------------------------- |
| **Rollout progresses** | "Your phased rollout for MyApp is active, rolling out to **5%** of your users." |
| **Rollout paused** | "Your phased rollout for MyApp has been paused at **5%**." |
| **Rollout resumed** | "Your phased rollout for MyApp has been resumed and is rolling out to **5%** of your users." |
| **Rollout completes** | "Your phased rollout for MyApp is complete and your app update has rolled out to 100% of users." |
> If you use **manual release** (Pending Developer Release) instead of phased release, Statused will notify you when your app is approved and waiting for you to release it, and again when it goes live.
## API Service Health [#api-service-health]
We also monitor App Store Connect API uptime. Apple's official [System Status](https://developer.apple.com/system-status/) page can be slow to reflect issues, so we track the specific endpoints Statused needs in real-time.
During API outages, we won't spam you with notifications, but you can check our [Status Page](https://status.statused.com) for current service health.
# Creating API Keys for Google Play Console (/integrations/google-play-console/create-api-key)
This guide walks you through creating API keys for Google Play Console to enable Android app monitoring. Google's official documentation can be confusing, so we'll break it down step by step with clear instructions.
## Before You Start [#before-you-start]
**What You'll Need:**
* Admin access to your Google Play Console account
* Google Cloud permissions to either create a new project (**Project Creator** role) or manage service accounts in an existing one (**Service Account Creator** role)
> **Language Tip**
>
> If you see Google Play Console in your local language, add `&hl=en` at the end of any URL (before `#...`) to switch to English. All links in this guide already include this parameter.
## Creating Your API Key [#creating]
### Note Your Developer Account ID [#note-your-developer-account-id-step]
Head to [Google Play Console](https://play.google.com/console/?hl=en), click **Account Details**, and write down your **Developer Account ID**. You'll need this to verify you're working with the correct project later.
### Enable the Google Play Developer API [#enable-the-google-play-developer-api-step]
Before creating API keys, you need to enable the Google Play Developer API:
1. Go to [Google Play Developer API](https://console.developers.google.com/apis/api/androidpublisher.googleapis.com/?hl=en) in Google Cloud Console.
2. Choose an existing Google Cloud project or [create a new one](https://console.cloud.google.com/projectcreate/?hl=en).
> **Pro Tip**
>
> Create a dedicated project called "Google Play Console Project" for all your Google Play API needs. This keeps things organized and makes permission management easier.
3. Click ENABLE to activate the Google Play Developer API for your selected project.
### Create a Service Account [#create-a-service-account-step]
> **Best Practices**
>
> If you're already using a service account for tools like *fastlane*, don't reuse it. Create a dedicated one for Statused to prevent API rate limit conflicts and keep your integrations isolated.
1. Open [Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts?hl=en) and select the project you want to use.
2. Click
CREATE SERVICE ACCOUNT at the top of the page.
3. Check that you're in the correct project by looking for your **Developer Account ID** in the light gray text before `.iam.gserviceaccount.com`, or check the project name in the navigation bar.
4. Provide a descriptive name like "statused-service-account".
5. Save the generated email address below the Service account ID field. You'll need this later.
6. Click
DONE (don't click
CREATE AND CONTINUE as the optional steps aren't needed).
### Create Your API Key JSON [#create-your-api-key-json-step]
After creating your service account, you'll see it in the service accounts list:
1. Click the button next to your new service account and select Manage keys
2. Click in ADD KEY › Create New Key
3. Ensure JSON is selected as the key type and click CREATE
4. Download and save the JSON file to your computer. Remember the location as you'll need to upload this to Statused.
### Grant Console Access [#grant-console-access-step]
Next, you'll need to connect the service account you just created to Google Play Console.
1. Go to [Google Play Console](https://play.google.com/console/?hl=en) and select
Users and permissions ›
Invite new users
2. Paste the service account email you saved earlier — the one that ends with `.iam.gserviceaccount.com`.
3. Make sure to **not add** any apps to the
App permissions tab.
4. Instead, open the
Account permissions tab and choose appropriate permissions:
* Select only **View app information and download bulk reports (read-only)**
* This provides read-only access, which is all Statused needs and keeps your account secure
> **Here for fastlane?**
>
> If you're here to set up *fastlane* instead of Statused, select **Admin (all permissions)** to ensure full access. This avoids common permission issues when running *fastlane* actions like `supply` or `validate_play_store_json_key`.

6. Finally, click
Invite user and confirm to send the invitation. This completes the setup.
## Next Step: Connect to Statused [#connect]
With your JSON key file ready, you can now connect your Google Play Console account to Statused:
1. Head back to your [Statused dashboard](https://www.statused.com/app) and navigate to API Keys › Add API Key
2. Select Google Play as your platform
3. Upload your service account `.json` file and fill in the other required details
4. Hit Add API Key — we'll automatically validate the key permissions when saving to ensure everything was set up correctly.
> That's it! Your Google Play Console account is now connected.
## Where to go from here [#where-to-go-from-here]
Now that your Google Play Console account is connected, head back to the [Quickstart](/quickstart) guide to set up your first monitoring workflow.
## Troubleshooting [#troubleshooting]
**Having issues?** This process can be tricky. If you run into problems, [reach out](/help) and we'll help you get set up properly.
Here are some common resources from Google that might help:
* [Getting Started - Google Play Developer API](https://developers.google.com/android-publisher/getting_started)
* [Creating and managing projects - Google Cloud](https://cloud.google.com/resource-manager/docs/creating-managing-projects)
# Google Play Console Monitoring (/integrations/google-play-console)
## Overview [#overview]
Statused keeps tabs on your Android apps and translates status changes into clear notifications for your team. We monitor through Google Play Console APIs, with optional email forwarding to catch updates the API misses.
## How We Track Release Status [#how-we-track-release-status]
You have two monitoring options: Google Play Console API only, or API plus email forwarding for better coverage.
### API-Only Monitoring [#api-only-monitoring]
The Google Play Console API gives us these basic statuses:
* **Draft**: New app update created but not submitted for review yet
* **In Progress**: Phased rollout configured and submitted for Google's review
* **Halted**: Developer paused a phased rollout
* **Completed**: Full rollout configured and submitted for review
Here's the catch: these statuses only show what you configured, not what's actually happening after submission. It's a known limitation in the Google Play ecosystem. Check out [Google Play API Limitations](/known-limitations#google-play-console-api) for the technical details.
#### Get the Full Picture with Email Forwarding [#get-the-full-picture-with-email-forwarding]
Want more accurate status updates? Set up email forwarding from Google Play Console. This unlocks additional statuses we can track:
* **Phased Rollout Is In Progress**: Google approved your app and it's rolling out to users gradually
* **Phased Rollout Halted**: You or your team paused the phased rollout
* **Phased Rollout Finished**: Rollout completed and the app is live for everyone
* **Live**: Google approved your app and it's available to all users in the Play Store
* **Rejected**: Google rejected your app during review. Time to fix issues and resubmit
Follow our step-by-step guide: [Google Play Email Forwarding](/email-forwarding).
## Choose Your Tracks [#choose-your-tracks]
Pick which distribution tracks to monitor: `internal`, `alpha`, `beta`, `production`. Monitor just production for release notifications, or include beta tracks if your QA team needs those updates too.
## Phased Rollout Monitoring [#phased-rollout]
Statused gives you full visibility into Google Play staged rollouts. Whenever the rollout state changes, you'll get a notification in Slack or your webhook with the details.
### What Gets Tracked [#what-gets-tracked]
| Event | Example notification |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Rollout starts** | "Your phased rollout is active and you are currently rolling out your app update to **5%** of your users." |
| **Percentage increases** | Same as above, with the updated percentage (e.g. 5% → 20% → 50%) |
| **Rollout halted** | "Your phased rollout has been halted at **20%** and your update is no longer rolling out to users." |
| **Rollout resumed** | "Your phased rollout has been resumed and your update is rolling out to users again. You are currently rolling out your app update to **20%** of your users." |
| **Rollout completes (100%)** | "Your phased rollout is complete and your app update has rolled out to 100% of users." |
### Tip: Use a Small Initial Rollout Instead of Managed Publishing [#small-rollout-tip]
If you use [Managed Publishing](/glossary#managed-publishing) to control when your app goes live after approval, be aware that Google's API does not expose a distinct "approved" status — so there's no reliable way to detect that moment programmatically. See [Known Limitations](/known-limitations#google-play-console-api) for details.
A common workaround is to skip Managed Publishing and instead set your initial rollout to a very small percentage (e.g. 1%). When Google approves your app and it goes live to that 1%, Statused detects the change and sends a notification. This gives you a near-instant "approved" signal with minimal user exposure, and you can then decide whether to ramp up to 100%.
# Configure Webhooks (/integrations/webhooks/configure)
Ready to start automating your app release workflow? Let's set up your first webhook endpoint to receive real-time notifications from Statused.
## Prerequisites [#prerequisites]
Before setting up webhooks, ensure you have:
* A publicly accessible HTTPS endpoint that can receive POST requests
* Basic understanding of your backend framework for processing webhooks
> **New to webhooks?**
>
> Check out our [webhook reference](/integrations/webhooks/reference) to understand the payload format and event types you'll receive.
## Create Your Webhook Endpoint [#create-your-webhook-endpoint-step]
First, create an HTTP endpoint that can receive POST requests from Statused. The implementation details depend on your stack, but here's what your endpoint needs to handle:
* **Accept POST requests** with JSON payloads following the CloudEvents specification (see [Reference](/integrations/webhooks/reference))
* **Return 2xx status codes** within 30 seconds to confirm receipt
* **Handle retries gracefully** by being idempotent (we'll retry failed deliveries)
* **Validate request signatures** using the webhook secret (see [Security & Authentication](/integrations/webhooks/security))
> **HTTPS required**
>
> Statused only sends webhooks to HTTPS endpoints. HTTP endpoints without SSL will not receive webhook requests in production.
> **Need code examples?**
>
> Check out our [Webhook Examples](/integrations/webhooks/examples) for ready-to-use code snippets in popular frameworks like Express.js, Ruby on Rails, and Django.You can also find guides to integrate with CI/CD pipelines and other common use cases not necessarily tied to your backend.
## Add The Webhook in Statused [#add-the-webhook-in-statused-step]
1. Navigate to Destinations › Add Destination in your Statused dashboard
2. Select Webhook
3. Enter your webhook URL
4. **Add a secret token** for verification (strongly recommended)
5. Hit Add Destination
> **Always use secrets**
>
> While secrets are optional, they're essential for production security. Without secrets, anyone could send fake webhook requests to your endpoint.
## Test Your Webhook [#test-your-webhook-step]
After adding your webhook, test it to ensure everything works correctly:
1. In your Destinations list, find your new webhook destination
2. Click the menu and select Send Test Notification
3. Check your endpoint logs to verify the test payload was received
### What to Test [#what-to-test]
* ✅ **Endpoint receives the request**
* ✅ **Signature validation works** (if using secrets)
* ✅ **Response time is under 30 seconds**
* ✅ **Returns appropriate status codes**
## Create Your Workflow [#create-your-workflow-step]
Now connect your webhook to an app to start receiving notifications:
> **Free trial required:** Make sure you've started your trial at [Billing](https://www.statused.com/app/billing) to create workflows.
1. In your dashboard, navigate to Workflows › Add Workflow
2. Give it a descriptive name like `YourAppName Production → Webhook`
3. Select your API key, app, choose your webhook destination, and fill in the other required details
* For Android apps, we recommend informing us whether Managed Publishing is enabled. More on that in our [Google Play Email Forwarding Guide](/email-forwarding).
* Notice that you can see a live preview of your workflow configuration at the bottom of the page, as you configure it.
4. Hit Add Workflow when you're happy with your configuration
## You're Live! What to Expect [#youre-live-what-to-expect]
> **Monitoring is active**
>
> Your workflow is now live and monitoring your app.
The next time your app status changes (submission, review, rejection, live), your webhook will be called, triggering the automated actions you've set up in your backend.
## What's Next? [#whats-next]
Now that your webhook is configured, explore these guides to maximize its potential:
- [Secure Your Webhooks](/integrations/webhooks/security) — Implement signature validation and security best practices.
- [Implementation Examples](/integrations/webhooks/examples) — Ready-to-use code for GitHub Actions, CI/CD, and popular frameworks.
- [Webhook Reference](/integrations/webhooks/reference) — Complete payload documentation and event type reference.
> **Need configuration help?**
>
> Webhook setup can be tricky depending on your infrastructure. If you need assistance with configuration or troubleshooting, [reach out to us](/help) — we're happy to help get your webhooks working smoothly.
# Code Examples (/integrations/webhooks/examples)
Get started quickly with these webhook implementation examples. All examples include proper security validation and error handling.
## Backend Framework Examples [#backend-framework-examples]
```javascript title="webhook.js"
app.use('/webhook', express.raw({type: 'application/json'}));
function validateSignature(payload, signature, secret) {
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhook', async (req, res) => {
const signature = req.headers['x-statused-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!validateSignature(req.body, signature, secret)) {
return res.status(401).send('Unauthorized');
}
const event = JSON.parse(req.body);
const { type, data } = event;
switch (type) {
case 'com.statused.app.status.updated':
await handleAppStatusChange(data);
break;
case 'com.statused.build.status.updated':
await handleBuildStatusChange(data);
break;
}
res.status(200).send('OK');
});
async function handleAppStatusChange(data) {
switch (data.status) {
case 'Ready for Sale':
await notifyTeam(`🎉 ${data.app_name} v${data.version} is live!`);
break;
case 'In Review':
await toggleFeatureFlags(data.app_name, false);
break;
case 'Rejected':
await createIncident(data);
break;
}
}
```
```ruby title="webhooks_controller.rb"
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :validate_signature
def statused
event = JSON.parse(request.raw_post)
case event['type']
when 'com.statused.app.status.updated'
handle_app_status_change(event['data'])
when 'com.statused.build.status.updated'
handle_build_status_change(event['data'])
end
head :ok
end
private
def validate_signature
signature = request.headers['X-Statused-Signature']
secret = Rails.application.credentials.webhook_secret
body = request.raw_post
expected_signature = 'sha256=' + OpenSSL::HMAC.hexdigest('SHA256', secret, body)
unless ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
head :unauthorized and return
end
end
def handle_app_status_change(data)
case data['status']
when 'Ready for Sale'
NotificationService.notify_team("🎉 #{data['app_name']} is live!")
when 'In Review'
FeatureFlagService.toggle(data['app_name'], enabled: false)
when 'Rejected'
IncidentService.create_from_rejection(data)
end
end
end
```
```ruby title="routes.rb"
post '/webhooks/statused', to: 'webhooks#statused'
```
```python title="views.py"
@csrf_exempt
@require_http_methods(["POST"])
def statused_webhook(request):
signature = request.META.get('HTTP_X_STATUSED_SIGNATURE')
if not validate_signature(request.body, signature):
return HttpResponseBadRequest("Invalid signature")
event = json.loads(request.body)
event_type = event.get('type')
data = event.get('data', {})
if event_type == 'com.statused.app.status.updated':
handle_app_status_change(data)
elif event_type == 'com.statused.build.status.updated':
handle_build_status_change(data)
return HttpResponse("OK")
def validate_signature(payload, signature):
if not signature:
return False
secret = settings.WEBHOOK_SECRET
expected_signature = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
def handle_app_status_change(data):
status = data.get('status')
if status == 'Ready for Sale':
notify_release.delay(data.get('app_name'))
elif status == 'In Review':
toggle_features.delay(data.get('app_name'), False)
elif status == 'Rejected':
create_incident.delay(data)
```
```python title="urls.py"
path('webhooks/statused/', views.statused_webhook, name='statused_webhook'),
```
## CI/CD System Integration [#cicd-system-integration]
Most CI/CD systems don’t natively accept generic webhooks from external services like Statused. However, you can integrate Statused with popular mobile-focused CI/CD platforms using their APIs or plugin systems:
| Platform | Native Webhook Support | Integration Method | Documentation |
| ------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Bitrise** | ❌ No | Use Bitrise REST API to trigger builds (requires API token) | [Triggering Builds via API – Bitrise Docs](https://devcenter.bitrise.io/en/api/triggering-and-aborting-builds.html) |
| **GitHub Actions** | ❌ No | External trigger via Repository Dispatch event (GitHub REST API) | [Repository Dispatch API – GitHub Docs](https://docs.github.com/en/rest/repos/repos#create-a-repository-dispatch-event) |
| **Jenkins** | ✅ Yes (with plugin) | Direct webhook trigger using a plugin (e.g. **Generic Webhook Trigger**) or via intermediate service | [Generic Webhook Trigger Plugin – Jenkins](https://plugins.jenkins.io/generic-webhook-trigger/) |
| **GitLab CI** | ✅ Yes | Built-in Pipeline Triggers (trigger token & API call) | [Pipeline Triggers – GitLab Docs](https://docs.gitlab.com/ee/ci/triggers/) |
| **Azure DevOps** | ✅ Yes | Incoming Webhook triggers (YAML pipeline webhook resource) | [Webhook Resource Triggers – Azure DevOps Docs](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/resources?view=azure-devops#webhook-triggers) |
| **CircleCI** | ❌ No | Trigger via CircleCI API (e.g. Pipeline Trigger endpoint) | [Trigger Pipeline via API – CircleCI Docs](https://circleci.com/docs/triggers-overview#trigger-a-pipeline-using-the-api) |
Each “No” above means you’ll need to set up a small backend (or use a serverless function) to receive the Statused webhook and then call the CI/CD’s API. For platforms marked “Yes”, you can configure them to accept incoming webhooks directly without an extra proxy in between.
### Recommended Integration Pattern [#recommended-integration-pattern]
For platforms without native webhook support, we recommend implementing a **backend proxy**:
1. **Statused** → **Your Backend** → **CI/CD Platform API**
2. Your backend receives the webhook, validates it, and triggers the appropriate CI/CD action
3. This gives you full control over authentication, filtering, error handling, and custom logic
## Backend Proxy Example [#backend-proxy-example]
```javascript title="webhook.js"
async function handleBuildStatusChange(data) {
if (data.status === 'Valid') {
// Build is valid - trigger GitHub Actions to submit for review
await fetch(`https://api.github.com/repos/${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}/dispatches`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.GITHUB_TOKEN}`,
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"Content-Type": "application/json",
},
body: JSON.stringify({
event_type: "submit-for-review",
client_payload: {
app_name: data.app_name,
build_version: data.build_version,
}
})
});
} else {
// Build failed - notify team via Slack/Teams
await notifyTeam(`⚠️ Build processing failed for ${data.app_name} v${data.build_version}. Please investigate.`);
}
}
```
```ruby title="webhooks_controller.rb"
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
# …
def handle_build_status_change(data)
if data['status'] == 'Valid'
# Build is valid - trigger GitHub Actions to submit for review
trigger_github_actions(data)
else
# Build failed - notify team via Slack/Teams
NotificationService.notify_team("⚠️ Build processing failed for #{data['app_name']} v#{data['build_version']}. Please investigate.")
end
end
private
def trigger_github_actions(data)
uri = URI("https://api.github.com/repos/#{ENV['GITHUB_OWNER']}/#{ENV['GITHUB_REPO']}/dispatches")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV['GITHUB_TOKEN']}"
req["Accept"] = "application/vnd.github+json"
req["X-GitHub-Api-Version"] = "2022-11-28"
req.content_type = "application/json"
req.body = {
event_type: "submit-for-review",
client_payload: {
app_name: data["app_name"],
build_version: data["build_version"],
}
}.to_json
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
end
end
```
```python title="views.py"
def handle_build_status_change(data):
if data.get('status') == 'Valid':
# Build is valid - trigger GitHub Actions to submit for review
trigger_github_actions(data)
else:
# Build failed - notify team via Slack/Teams
notify_team.delay(f"⚠️ Build processing failed for {data.get('app_name')} v{data.get('build_version')}. Please investigate.")
def trigger_github_actions(data):
url = f"https://api.github.com/repos/{settings.GITHUB_OWNER}/{settings.GITHUB_REPO}/dispatches"
headers = {
"Authorization": f"Bearer {settings.GITHUB_TOKEN}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28"
}
payload = {
"event_type": "submit-for-review",
"client_payload": {
"app_name": data["app_name"],
"build_version": data["build_version"],
}
}
requests.post(url, json=payload, headers=headers, timeout=10)
```
### GitHub Actions workflow [#github-actions-workflow]
```yaml title=".github/workflows/submit-for-review-webhook.yml"
name: Submit App for Review
on:
repository_dispatch:
types: [submit-for-review]
jobs:
submit-for-review:
runs-on: ubuntu-latest
steps:
- name: Submit app for review
run: |
echo "🚀 Submitting ${{ github.event.client_payload.app_name }} \
v${{ github.event.client_payload.build_version }} for review..."
# e.g. fastlane deliver --submit_for_review
```
Key references if you need further details or up-to-date API information:
* [Create a repository dispatch event – GitHub REST API](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#create-a-repository-dispatch-event)
* [Events that trigger workflows (`repository_dispatch`) - GitHub Docs](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#repository_dispatch)
## Feature Flag Integration [#feature-flag-integration]
You can use your backend to toggle feature flags based on app status changes. Check your feature flagging service documentation for their API or SDK usage.
## Support [#support]
> **Want direct CI/CD integration?**
>
> Missing your CI/CD platform or want us to build a direct integration? [Reach out to us](/help) — we prioritize new integrations based on user demand and can help you get set up quickly.
> **Need help with implementation?**
>
> These examples should get you started, but every integration is unique. If you need help adapting these examples to your specific use case, [reach out to us](/help) — we're happy to help with your webhook implementation.
# Webhooks (/integrations/webhooks)
Webhooks let you receive real-time notifications about your app status changes directly in your own systems. Perfect for triggering CI/CD workflows, notifying your team, or automating release processes.
## Why Use Webhooks? [#why-use-webhooks]
- **Save CI Minutes** — Stop waiting in CI for iOS build processing. Get notified when your app is ready and resume your workflow automatically.
- **Instant Automation** — Trigger custom actions the moment your app status changes — notify QA, submit for review, or update your deployment dashboard.
- **Avoid App Rejections** — Toggle feature flags when your app enters review, and automatically switch back once it's approved.
- **CI/CD Integration** — Seamlessly integrate with GitHub Actions, Jenkins, GitLab CI, or any system that can receive HTTP requests.
## Smart CI/CD Workflows [#smart-cicd-workflows]
The most powerful use case: **stop burning expensive CI minutes** while waiting for Apple to process your builds.
### Traditional Workflow (Wasteful) [#traditional-workflow-wasteful]
```
Submit App → Wait in CI (15min - 3hrs) → Process Complete → Continue Pipeline
↑ Expensive idle time
```
Some teams have varying approaches, all of which with their own drawbacks:
* **Manual work**: CI generates build, engineer is notified when processing is done, then manually submits the app for review
* **CI timeouts**: CI systems usually have built-in timeouts to prevent indefinite waiting and may fail the job after a certain period, before the build is processed
* **Day-after submission**: App is submitted for processing and only the next day the app is submitted for review, delaying releases
### Smart Webhook Workflow (Efficient) [#smart-webhook-workflow-efficient]
```
Submit App → Stop CI Job → Webhook Notification → Resume Pipeline
↑ Zero idle cost
```
This approach can save **15 minutes to several hours** of CI time per release, depending on app store processing times. For teams releasing frequently, this adds up to significant cost savings.
## Avoid App Rejections with Feature Flags [#avoid-app-rejections-with-feature-flags]
Here's the reality: App Store reviewers sometimes flag features that have been working perfectly for months, or get confused by A/B tests that show different experiences, or stumble upon edge cases that aren't even related to your current update.
**The industry-standard workaround** *(that Apple would prefer you didn't know about, but every experienced team uses)*: **present a vanilla experience during review.**
The goal isn't to hide bugs — it's to create a consistent, predictable experience that won't trigger a reviewer's "something's fishy" radar.
* **When app enters review** → Disable experimental features
* **When app is approved** → Re-enable all features
This ensures reviewers get the most straightforward version of your app, whether they're testing with a reviewer account, as an anonymous user, or in some edge case scenario you never anticipated. Because the last thing you need is a rejection for a feature that's been stable for months but happened to confuse the reviewer.
This pattern works especially well for:
* A/B testing features that might confuse reviewers who expect consistent behavior
* Beta features that aren't ready for prime time (or prime time reviewers)
* Features that require specific user data or permissions to function properly
> **Need feature flags?**
>
> This assumes you already have a feature flagging system in place. Statused handles the "when to toggle" part by tracking your app's review status — you bring the flags, we'll trigger the automation. Works with any system: 3rd party services, your homegrown solution, or even a simple config file.
## Other Popular Automation Ideas [#other-popular-automation-ideas]
### Team Coordination [#team-coordination]
* **QA notifications**: Alert your QA team when TestFlight builds are ready
* **Release announcements**: Inform stakeholders when apps go live
* **Issue escalation**: Page on-call engineers for rejected submissions
> **Need something simpler?**
>
> For pure messaging without automation, Slack notifications often work just as well and require less setup. [Learn more about our Slack integration](/integrations/slack).
### Release Orchestration [#release-orchestration]
* **Feature phased rollout**: Start feature flag rollouts when your app is approved or released
* **Multi-platform releases**: Coordinate iOS and Android releases
* **Backend synchronization**: Deploy backend changes when mobile apps are approved
* **Rollback automation**: Trigger rollback procedures for failed releases
## Get Started [#get-started]
Ready to automate your release workflow? Here's what you need to set up:
- [Configure Webhooks](/integrations/webhooks/configure) — Set up your webhook endpoints and configure which events to receive.
- [Security & Authentication](/integrations/webhooks/security) — Learn how to secure your webhooks with secrets and signature validation.
- [Webhook Reference](/integrations/webhooks/reference) — Complete reference for event types, payload formats, and CloudEvents specification.
- [Code Examples](/integrations/webhooks/examples) — Ready-to-use examples for GitHub Actions, CI/CD pipelines, and popular frameworks.
***
**Ready to save CI costs and automate your releases?** Start with [configuring your first webhook](/integrations/webhooks/configure).
# Webhook Reference (/integrations/webhooks/reference)
Statused webhooks follow the [CloudEvents specification](https://cloudevents.io/) and provide detailed information about app status changes. This reference covers all available event types and their payload structures.
## Event Types [#event-types]
Statused sends three types of webhook events:
| Event Type | Trigger | Example |
| ------------------------------------- | ------------------------------- | ----------------------------------------------------------- |
| `com.statused.app.status.updated` | App review status changes | "In Review" → "Approved" |
| `com.statused.build.status.updated` | Build processing status changes | "Processing" → "Valid" |
| `com.statused.account.status.updated` | Account status changes | Apple releases new developer agreement requiring acceptance |
## CloudEvents Structure [#cloudevents-structure]
All webhook payloads follow the CloudEvents v1.0 specification:
```json
{
"specversion": "1.0",
"type": "com.statused.app.status.updated",
"source": "statused",
"id": "6af15d63-dc69-4088-8adf-cdbb7d204eb4",
"time": "2025-01-12T22:12:03Z",
"datacontenttype": "application/json",
"data": {
// Event-specific payload
}
}
```
### CloudEvents Headers [#cloudevents-headers]
| Field | Description |
| ----------------- | ------------------------------------------------ |
| `specversion` | CloudEvents specification version (always "1.0") |
| `type` | Event type identifier |
| `source` | Event source (always "statused") |
| `id` | Unique event identifier (UUID) |
| `time` | Event timestamp in ISO 8601 format |
| `datacontenttype` | Content type of the data field |
## Payload Formats [#payload-formats]
### App Status Updated [#app-status-updated]
Sent when an app's review status changes:
```json
{
"specversion": "1.0",
"type": "com.statused.app.status.updated",
"source": "statused",
"id": "6af15d63-dc69-4088-8adf-cdbb7d204eb4",
"time": "2025-01-12T22:12:03Z",
"datacontenttype": "application/json",
"data": {
"message": "The status of your app Markdown Email changed to Ready for Sale",
"version": "1.2.3",
"status": "Ready for Sale",
"track": "Production",
"user_distribution": "30%",
"store_name": "App Store Connect",
"status_color": "#1eb6fc",
"app_name": "Markdown Email",
"app_icon_url": "https://example.com/app-icon.png",
"internal_store_url": "https://appstoreconnect.apple.com/...",
"timestamp": "2025-01-12T22:12:03Z"
}
}
```
#### App Status Fields [#app-status-fields]
| Field | Type | Description |
| -------------------- | ------ | -------------------------------------------- |
| `message` | string | Human-readable status change description |
| `version` | string | App version number |
| `status` | string | New app status |
| `track` | string | Release track (Android only) |
| `user_distribution` | string | Rollout percentage (Android only) |
| `store_name` | string | "App Store Connect" or "Google Play Console" |
| `status_color` | string | Hex color for status visualization |
| `app_name` | string | Application name |
| `app_icon_url` | string | URL to app icon image |
| `internal_store_url` | string | Link to store console |
| `timestamp` | string | Event timestamp in ISO 8601 format |
### Build Status Updated [#build-status-updated]
Sent when a build's processing status changes:
```json
{
"specversion": "1.0",
"type": "com.statused.build.status.updated",
"source": "statused",
"id": "7bf25e74-ed70-5199-9bee-eeccdd8e295f",
"time": "2025-01-12T22:15:47Z",
"datacontenttype": "application/json",
"data": {
"message": "The status of build 1.0 (5) of Markdown Email changed to Valid",
"version": "1.2.3",
"status": "Valid",
"store_name": "App Store Connect",
"status_color": "#1eb6fc",
"app_name": "Markdown Email",
"app_icon_url": "https://example.com/app-icon.png",
"internal_store_url": "https://appstoreconnect.apple.com/...",
"timestamp": "2025-01-12T22:15:47Z"
}
}
```
#### Build Status Fields [#build-status-fields]
Build status payloads include the same fields as app status, except:
* No `track` field (not applicable to builds)
* No `user_distribution` field (not applicable to builds)
* `message` refers to build instead of app
### Account Status Updated [#account-status-updated]
Sent when your App Store Connect account status changes:
```json
{
"specversion": "1.0",
"type": "com.statused.account.status.updated",
"source": "statused",
"id": "8cg36f85-fe81-6200-acff-ffddeeff9f406g",
"time": "2025-01-12T22:20:15Z",
"datacontenttype": "application/json",
"data": {
"message": "Action Needed: You Have Pending Agreements",
"status": "Pending Agreement Acceptance",
"store_name": "App Store Connect",
"status_color": "#ff6b6b",
"internal_store_url": "https://appstoreconnect.apple.com/...",
"timestamp": "2025-01-12T22:20:15Z"
}
}
```
#### Account Status Fields [#account-status-fields]
| Field | Type | Description |
| -------------------- | ------ | ----------------------------------------- |
| `message` | string | Human-readable account status description |
| `status` | string | Account status |
| `store_name` | string | Always "App Store Connect" |
| `status_color` | string | Hex color for status visualization |
| `internal_store_url` | string | Link to App Store Connect |
| `timestamp` | string | Event timestamp in ISO 8601 format |
## Status Values [#status-values]
See platform-specific statuses below:
- [App Store Connect](/integrations/app-store-connect)
- [Google Play Console](/integrations/google-play-console)
## HTTP Headers [#http-headers]
Statused includes additional headers with webhook requests:
| Header | Description |
| ---------------------- | ------------------------------------------------- |
| `Content-Type` | `application/json` |
| `User-Agent` | `Statused-Webhooks/1.0` |
| `X-Statused-Signature` | HMAC-SHA256 signature (when secret is configured) |
## Delivery & Retries [#delivery--retries]
* Events are delivered within 30 seconds for iOS and 150 seconds for Android (due to API rate limits)
* Failed deliveries are automatically retried with exponential backoff up to 5 times
* Webhooks timeout after 30 seconds and expect a 2xx response code
## Test Your Webhook [#test-your-webhook]
After adding your webhook, test it to ensure everything works correctly:
1. In your Destinations list, find your new webhook destination
2. Click the menu and select Send Test Notification
3. Check your endpoint logs to verify the test payload was received
### What to Test [#what-to-test]
* ✅ **Endpoint receives the request**
* ✅ **Signature validation works** (if using secrets)
* ✅ **Response time is under 30 seconds**
* ✅ **Returns appropriate status codes**
> **Questions about the webhook format?**
>
> If you need clarification about any payload fields or have questions about implementing webhook parsing, [reach out to us](/help) — we're happy to help with your integration.
# Security & Authentication (/integrations/webhooks/security)
Always secure your webhook endpoints to ensure only genuine requests from Statused are processed. When configuring your webhook in Statused, you can provide an optional secret key used to generate an HMAC-SHA256 signature included in the webhook headers.
> **Always use secrets in production**. Without signature validation, malicious actors could potentially send fake webhook requests to your endpoints.
## Common Security Issues [#common-security-issues]
### Missing Signature Validation [#missing-signature-validation]
**Problem**: Accepting all webhook requests without verification allows malicious actors to send fake requests.
**Solution**: Always validate the `X-Statused-Signature` header using HMAC-SHA256 signature verification.
```javascript title="webhook.js"
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const secret = process.env.WEBHOOK_SECRET;
// [!code focus:17][!code ++:17]
const signature = req.headers['x-statused-signature'];
if (!signature) {
return res.status(401).send('Missing signature');
}
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
const payload = JSON.parse(req.body);
res.status(200).send('OK');
});
```
```ruby title="webhooks_controller.rb"
class WebhookController < ApplicationController
skip_before_action :verify_authenticity_token
# [!code focus][!code ++]
before_action :validate_signature
def receive
# Process the webhook
payload = JSON.parse(request.raw_post)
render json: { status: 'ok' }, status: :ok
end
private
# [!code focus:16][!code ++:16]
def validate_signature
secret = ENV['WEBHOOK_SECRET']
signature = request.headers['X-Statused-Signature']
if signature.blank?
render json: { error: 'Missing signature' }, status: :unauthorized
return
end
expected_signature = 'sha256=' +
OpenSSL::HMAC.hexdigest('SHA256', secret, request.raw_post)
unless ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
render json: { error: 'Invalid signature' }, status: :unauthorized
end
end
end
```
```python title="views.py"
@csrf_exempt
@require_http_methods(["POST"])
def webhook(request):
# [!code focus:2][!code ++:2]
if not validate_signature(request.body, request.META.get('HTTP_X_STATUSED_SIGNATURE')):
return HttpResponseBadRequest("Invalid signature")
# Process the webhook
payload = request.body
return HttpResponse("OK")
# [!code focus:12][!code ++:12]
def validate_signature(payload, signature):
if not signature:
return False
secret = os.environ['WEBHOOK_SECRET']
expected_signature = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
```
### Timing Attacks [#timing-attacks]
**Problem**: Using simple string comparison for signatures allows attackers to determine the correct signature through timing analysis.
**Solution**: Use timing-safe comparison functions that take constant time regardless of input differences.
```javascript title="webhook.js"
const crypto = require('crypto');
function validateSignature(payload, signature, secret) {
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// [!code focus:2][!code --:2]
// ❌ Bad: Vulnerable to timing attacks
// return signature === expectedSignature;
// [!code focus:5][!code ++:5]
// ✅ Good: Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
```
```ruby title="webhooks_controller.rb"
def validate_signature
secret = ENV['WEBHOOK_SECRET']
signature = request.headers['X-Statused-Signature']
expected_signature = 'sha256=' + OpenSSL::HMAC.hexdigest('SHA256', secret, request.raw_post)
# [!code focus:2][!code --:2]
# ❌ Bad: Vulnerable to timing attacks
# unless signature == expected_signature
# [!code focus:4][!code ++:4]
# ✅ Good: Timing-safe comparison
unless ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
render json: { error: 'Unauthorized' }, status: :unauthorized
end
end
```
```python title="views.py"
import hmac
def validate_signature(payload, signature):
if not signature:
return False
secret = os.environ['WEBHOOK_SECRET']
expected_signature = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
# [!code focus:2][!code --:2]
# ❌ Bad: Vulnerable to timing attacks
# return signature == expected_signature
# [!code focus:2][!code ++:2]
# ✅ Good: Timing-safe comparison
return hmac.compare_digest(signature, expected_signature)
```
### Verbose Error Messages [#verbose-error-messages]
**Problem**: Returning detailed error information to webhook requests can leak sensitive information about your system.
**Solution**: Log detailed errors internally for debugging, but return simple status codes to external requests.
```javascript title="webhook.js"
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
try {
const signature = req.headers['x-statused-signature'];
const secret = process.env.WEBHOOK_SECRET;
// [!code focus:8][!code ++:8]
if (!signature) {
// ✅ Good: Log details internally, return simple response
console.error('Webhook request missing signature header', {
ip: req.ip,
userAgent: req.get('User-Agent')
});
return res.status(401).send('Unauthorized');
}
// [!code focus:9][!code ++:9]
if (!validateSignature(req.body, signature, secret)) {
// ✅ Good: Log security event
console.error('Invalid webhook signature', {
ip: req.ip,
signature: signature,
expectedStart: expectedSignature.substring(0, 10)
});
return res.status(401).send('Unauthorized');
}
// Process webhook
const payload = JSON.parse(req.body);
res.status(200).send('OK');
} catch (error) {
// [!code focus:2][!code ++:2]
// ✅ Good: Log error details internally
console.error('Webhook processing error:', error);
// [!code focus][!code --]
// ❌ Bad: return res.status(500).send(`Error: ${error.message}`);
// [!code focus:2][!code ++:2]
// ✅ Good: Return simple error response
res.status(500).send('Internal Server Error');
}
});
```
```ruby title="webhooks_controller.rb"
class WebhookController < ApplicationController
def receive
begin
validate_signature
# Process webhook
payload = JSON.parse(request.raw_post)
Rails.logger.info "Processed webhook: #{payload['type']}"
render json: { status: 'ok' }, status: :ok
rescue JSON::ParserError => e
# [!code focus:5][!code ++:5]
# ✅ Good: Log details internally
Rails.logger.error "Webhook JSON parsing failed: #{e.message}", {
ip: request.remote_ip,
body_preview: request.raw_post[0..100]
}
# [!code focus][!code --]
# ❌ Bad: render json: { error: "JSON parsing failed: #{e.message}" }
# [!code focus:2][!code ++:2]
# ✅ Good: Return simple error
render json: { error: 'Bad Request' }, status: :bad_request
rescue => e
# [!code focus:2][!code ++:2]
Rails.logger.error "Webhook processing error: #{e.message}"
render json: { error: 'Internal Server Error' }, status: :internal_server_error
end
end
private
def validate_signature
signature = request.headers['X-Statused-Signature']
# [!code focus:5][!code ++:5]
if signature.blank?
Rails.logger.warn "Webhook missing signature", { ip: request.remote_ip }
render json: { error: 'Unauthorized' }, status: :unauthorized
return
end
# Validation logic...
end
end
```
```python title="views.py"
import logging
import json
logger = logging.getLogger(__name__)
@csrf_exempt
@require_http_methods(["POST"])
def webhook(request):
try:
signature = request.META.get('HTTP_X_STATUSED_SIGNATURE')
# [!code focus:7][!code ++:7]
if not signature:
# ✅ Good: Log security event
logger.warning('Webhook missing signature', extra={
'ip': request.META.get('REMOTE_ADDR'),
'user_agent': request.META.get('HTTP_USER_AGENT')
})
return HttpResponseBadRequest("Unauthorized")
# [!code focus:5][!code ++:5]
if not validate_signature(request.body, signature):
logger.warning('Invalid webhook signature', extra={
'ip': request.META.get('REMOTE_ADDR')
})
return HttpResponseBadRequest("Unauthorized")
# Process webhook
payload = json.loads(request.body)
logger.info(f"Processed webhook: {payload.get('type')}")
return HttpResponse("OK")
except json.JSONDecodeError as e:
# [!code focus:2][!code ++:2]
# ✅ Good: Log details internally
logger.error(f"Webhook JSON parsing failed: {e}")
# [!code focus][!code --]
# ❌ Bad: return HttpResponseBadRequest(f"JSON error: {e}")
# [!code focus:2][!code ++:2]
# ✅ Good: Return simple error
return HttpResponseBadRequest("Bad Request")
except Exception as e:
# [!code focus:2][!code ++:2]
logger.error(f"Webhook processing error: {e}")
return HttpResponseServerError("Internal Server Error")
```
## Additional Security Best Practices [#additional-security-best-practices]
* **Rate Limiting**: Implement reasonable rate limits on your webhook endpoints to prevent abuse.
* **Monitoring**: Set up alerts for failed webhook deliveries and security events.
* **Idempotency**: Handle duplicate webhook deliveries gracefully since Statused will retry failed deliveries.
> **Need help securing your webhooks?**
>
> Security is critical for webhook implementations. If you need assistance with setup or have questions about best practices, [reach out to us](/help) — we're here to help keep your integrations secure.