What the engine looks at.
Every check is a hand written rule with an explicit condition, cited to the guideline it enforces. Below is what those rules read, who can act on the result, and how to run the same pass on every pull request.
We take the build apart the way the store does
Verdict unzips the archive and reads the parts a reviewer's tooling reads: Info.plist or the binary AndroidManifest, the Mach-O load commands, entitlements out of the code signature blob, the provisioning profile inside its CMS envelope, every embedded framework, and the DEX string table. Seconds, not hours, because nothing is ever executed.
- Accepts .ipa, .apk, .aab, a zipped macOS .app, or a Windows .msix, up to 1000MB
- Static analysis on Linux, no simulator, no device
- The build is deleted the moment the report exists
Every finding arrives with its receipt
A warning that says something is wrong is the easy half. Verdict names the guideline a reviewer would cite, links the official Apple or Google document so you can check the claim yourself, quotes the exact evidence pulled out of your binary, and gives you the specific key, capability, or line to change — ordered by how much score each fix gives back for the work it costs.
- Cited and linked to the published guideline or ITMS code
- Evidence quoted from your build, not paraphrased
- Paste ready patches, ordered by the fastest path to 90
- A ready made brief for Cursor, Claude, or any coding agent
Camera API is used but NSCameraUsageDescription is missing
VD-PRIV-01 · Guideline 5.1.1 · Legal · Privacy
What we found
The app binary references AVCaptureDevice, which requires a purpose string. Info.plist has no NSCameraUsageDescription. Apple flags this as ITMS-90683 at upload.
The fix
Add the key to Info.plist with one sentence naming the feature that needs camera access. Write it for the person reading the permission alert, not for the reviewer.
Already rejected? Start by decoding the message
Rejection notices say what, never why. Paste yours and Verdict identifies the guideline behind it, explains what reviewers actually mean when they cite it, lists the changes that resolve it, and drafts your Resolution Center reply. It runs in your browser and nothing is stored.
- Apple guideline numbers and Play policy names
- A reply draft you can edit and send
- Free, no account, nothing leaves the page
Pasted from App Store Connect
Guideline 2.1 · Performance · App Completeness
We were unable to review your app as it crashed on launch.
The reviewer could not get the app into a working state: a crash, a login they could not pass, or a feature that did nothing on their device.
Reply draft
Thank you for the detailed report. We reproduced the issue and fixed it in this build. We also verified the flow end to end on a clean install with the demo account provided.
Agents
The agent in your terminal can do all of it.
Verdict speaks the Model Context Protocol. Connect it once and Claude Code or Codex can scan the build, read what is wrong with it, and apply the fixes without you leaving the session.
# once
claude mcp add --transport http verdict \
https://appverdict.com/api/mcp
# then, whenever
>Get the fix brief for my latest scan and work top to bottom, applying each fix in this repo. Show me a diff before anything that touches signing, entitlements or Info.plist.
Reading the brief, cheapest fix first
✓ 4 patches applied · rescan to confirm
Six tools, and no more than six
Every one of them sits in the model's context on every message, so a longer list would cost you tokens and make the agent choose worse.
scan_build
Uploads a local .ipa, .apk or .aab and returns its score, gate verdict and findings.
get_fix_brief
The ordered work order: evidence, patches and citations, cheapest fixes first.
get_report
One scan's full findings, filterable by severity.
check_gate
Whether a build may ship under your team's release policy.
list_apps
Every app here with its latest score, blockers and trend.
list_scans
Recent builds, newest first, with the ids the other tools take.
Claude CodeCodexCursor, Zed and anything else that speaks HTTP
Continuous integration
Catch it in the pull request, not in the rejection email.
The expensive rejections are the ones nobody thought to check for. Put Verdict in the pipeline that already builds your artifact and the check stops depending on anyone remembering it.
- Writes the verdict onto the pull requestThe score and every finding land in the thread, where the people reviewing the change already are, instead of inside a CI log nobody opens until it goes red.
- Fails the build on a blockerA non zero exit is the whole feature. The bad build never reaches a reviewer.
- Reads the artifact you already produceNo console credentials, no store API access. It takes the same .ipa, .apk, .aab, zipped macOS .app, or Windows .msix you were going to upload.
- Every build, not just the one before releaseWhich is how a signing or permission regression gets caught the day it lands.
name: App Store gate
on: [pull_request]
permissions:
contents: read
pull-requests: write # lets the job write the verdict onto the PR
jobs:
verdict:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ... your existing build step, producing build/App.ipa
- name: Scan the build
run: |
curl -sS -X POST https://www.appverdict.net/api/v1/scans \
-H "Authorization: Bearer ${{ secrets.VERDICT_API_KEY }}" \
-F "file=@build/App.ipa" > report.json
- name: Write the verdict on this pull request
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
{
jq -r '"### Approval risk \(.score)/100"' report.json
jq -r '.findings[] | "- `\(.severity)` **\(.ruleId)** \(.title)"' report.json
jq -r '"\n[Open the full report](https://www.appverdict.net/scan/\(.id))"' report.json
} > verdict.md
# --edit-last rewrites the bot's previous comment, so ten pushes
# leave one comment that is current, not ten that are stale.
gh pr comment ${{ github.event.number }} \
--body-file verdict.md --edit-last --create-if-none
- name: Fail if this build would be rejected
run: |
jq -e '.gate.pass' report.json > /dev/null && exit 0
jq -r '.gate.failures[] | "::error::" + .' report.json
exit 1Verdict works where you already do.
Nothing here is a roadmap. Every name is somewhere a scan can reach, or a build the engine can read, today.
Plugs into
- Claude Code
- Codex
- GitHub Actions
- GitLab CI
- Slack
- Discord
- Any webhook
Reads builds for
- App Store
- Google Play
- iOS
- Android
- macOS
- Windows
- Linux builds
After the report
Finding the problems is the first half.
A report tells you what is wrong on the day you run it. Shipping is the weeks after that, usually with other people. So the same workspace carries the work: what is left to do, when it has to be done by, and a line in your team's channel every time something moves.
A board that ticks itself off
Turn any finding into a task without retyping it: the rule, the guideline, and the fix come with it. When a later build stops reporting that rule, the task closes itself and says a rescan cleared it.
No more boards that go stale the week after you make them
The date you are working towards
Put your submission date on the month and every scan and task due date lands there beside it. One page answers what is owed this week and what shipped last, without anybody keeping a second calendar in step with this one.
A month at a time, shared with the whole workspace
It shows up where you already talk
Connect a Slack or Discord webhook and scans and tasks announce themselves in the channel. Everyone in the workspace shares one board and one feed, so a teammate's work is simply there when you open it.
On every plan, including free
Rejection is not random. It is a short list.
Apple publishes what it rejects and why. Five categories account for roughly nine in ten rejections, and most of what drives them is visible in the build before anyone submits it.
What Apple rejects apps for
Share of rejections by guideline family. Highlighted rows are categories Verdict inspects directly in your build.
Apple App Store transparency reporting and published rejection analyses, 2025 data.
Rejection rate, year over year
Share of all submissions rejected. Apple reports this annually, so 2025 is the most recent complete year on record.
0%in 2025, the latest full year
Apple App Store transparency reports, 2025 figures published May 2026: 2M+ rejections across 9.1M submissions. Updated when Apple publishes the next report.
What one rejection actually costs
The same release, submitted twice versus checked once. Width is proportional to elapsed days.
Eight days back. Most of a rejection cycle is not your work, it is waiting. Reviewers usually stop at the first problem they hit, so the second pass often fails on something they never mentioned in the first.
Illustrative timeline based on commonly reported review and resubmission windows.