Verdict

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
GET /api/v1/scans/:id
{
  "store": "ios",
  "score": 62,
  "app": { "name": "Trailhead", "bundleId": "com.example.trailhead" },
  "counts": { "blocker": 1, "warning": 2, "advisory": 3 },
  "findings": [
    {
      "ruleId": "VD-PRIV-01",
      "severity": "blocker",
      "guideline": "Guideline 5.1.1 - Legal - Privacy",
      "title": "Camera API is used but NSCameraUsageDescription is missing",
      "evidence": "The app binary references AVCaptureDevice, which requires
                   a purpose string. Info.plist has no NSCameraUsageDescription.",
      "fix": "Add NSCameraUsageDescription to Info.plist with one sentence
              naming the feature that needs camera access."
    }
  ],
  "engineVersion": "1.1.0"
}

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
Decode a rejection

Pasted from App Store Connect

Guideline 2.1 · Performance · App Completeness
We were unable to review your app as it crashed on launch.

2.1App Completeness

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.

your project

# 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

Mint a token and connect

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.
Read the API docsKeys are minted on your account · 60 scans an hour
.github/workflows/verdict.yml
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 1

Verdict 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

Open your workspaceTasks, the calendar, and the channel are free on every plan

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.

App completeness2.1checked41%
Privacy and data5.1checked19%
Spam and templates4.314%
Inaccurate metadata2.3checked11%
Payments3.1checked9%
Everything elsemisc6%

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

202020212022202320242025

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.

Submitted without checking11 days
3d
1d
2d
5d
In reviewRejectedYou fix itBack in review
Checked with Verdict first3 days
1d
2d
You fix itIn review

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.