onebox

Guide 22 of 30

For your agent: .md · all guides

On this page
  1. What it costs
  2. Before you start
  3. Steps
  4. What happens next
  5. A rejection
  6. Where the values go
  7. Check it works
  8. Common errors

Submit your app for App Review

Runs on: your browser (App Store Connect). The ship-ios:appstore-connect skill can fill most fields from your Mac, with an App Store Connect API key.

App Review is Apple’s check of every app before it goes on the App Store. You fill in the version page in App Store Connect, pick a TestFlight build, and press submit. A person at Apple then installs the build, tries it, and approves or rejects it. This guide walks the version page field by field, in the order that saves you a second round.

Where this guide says “the page for X”, it names the section. Apple moves buttons around, so look for that section rather than a specific button.

What it costs

Nothing extra. Review is part of the Apple Developer Program (apple-developer.md). It takes about an hour the first time, most of it on App Privacy and the review notes.

Before you start

The version page is the one under your app’s iOS version, for example “1.0 Prepare for Submission”. App Store Connect makes it with the app record.

With the skill. One command shows what the page still misses, without changing anything:

node <ship-ios>/skills/appstore-connect/scripts/asc.mjs review-status --app com.example.myapp

Each line says OK, MISSING or CHECK. CHECK means the API cannot see it, and you check it on the web. Run it again after each step below.

Steps

1. App Privacy

App Privacy is the “nutrition label” on your store page: which data the app collects, whether it is linked to the user, and whether it is used for tracking. Apple asks it for every app, also for a free app with no accounts.

The API cannot read or set it. You answer it on the page for App Privacy.

Answer from what the code really sends, not from memory. Look at every SDK and every call to a server. Your answers must match your privacy policy (privacy-and-support-pages.md). A mismatch is a common rejection.

What the usual parts of this setup add:

PartData type to declareLinked to the userTracking
Sign in with Apple, when you ask for the name and emailContact Info: Name, Email AddressYesNo
Your own API with accountsUser ID, plus each kind of content the user saves (for example Other User Content, Photos)YesNo
RevenueCatPurchases: Purchase History. Identifiers: User ID if you set your own app user IDFollow RevenueCat’s pageFollow RevenueCat’s page
Crash reports (Sentry), product analytics (PostHog)See the table in crash-reports.md, and product-analytics.md
An AI providerThe content you send to it (for example Other User Content, Photos)Yes, if your API sends it with the user’s IDNo

The privacy policy URL also goes on this page. The skill’s listing-set privacyPolicyUrl sets it too.

2. Age rating

The age rating is a questionnaire on the page for App Information. Apple computes the rating (4+, 9+, 13+, 16+ or 18+) from your answers. The questions changed in 2025. An app with old answers must answer the new ones before its next submission.

Answer each question for what a user can see or do in the app. Two answers people often get wrong:

With the skill. Write the answers to a file (format in the skill’s references/api.md), then:

node $A age-rating-set age.json --app com.example.myapp --dry-run

$A is the path to asc.mjs. Without --dry-run it sends the answers.

3. Price and availability

On the page for Pricing and Availability:

The skill reads whether a price and the countries are set. It does not set them: do it on the web.

4. Category

On the page for App Information: a primary category and, if you like, a secondary category. The primary one decides where the app shows when people browse the App Store. Pick what the app does for the user, not where there are fewer apps.

With the skill. categories lists the category IDs, for example PRODUCTIVITY. Put them in the version file (step 8) as primaryCategory and secondaryCategory.

On the version page: the year and the owner of the rights, for example 2026 Example Ltd or 2026 Ada Example. No URL. The owner is you, or your company if the developer account is a company’s.

With the skill: copyright in the version file.

6. Content rights

On the page for App Information, Apple asks whether the app shows or uses content from third parties (other people’s text, images, music, video). Say yes if it does, and have the rights to use it. An app that shows only what its users make, or its own content, says no.

With the skill: contentRights in the version file: DOES_NOT_USE_THIRD_PARTY_CONTENT or USES_THIRD_PARTY_CONTENT.

7. The build

On the version page, in the build section, pick the TestFlight build you tested. Only processed builds with the same version number as the page (for example 1.0) show up.

The build needs its export compliance answer. If the app config has ITSAppUsesNonExemptEncryption: false, it is answered already (app-store-connect-setup.md, step 6).

With the skill:

node $A builds --app com.example.myapp                          # find the build ID
node $A attach-build --app com.example.myapp --build <buildId> --dry-run

It refuses a build that is not processed or has another version number.

8. App Review information

On the version page, in the section for App Review Information. A reviewer reads this before they open the app. Most “Information Needed” rejections come from this section.

Review notes. Tell the reviewer how to reach every feature in a few lines. For example:

Sign in with Apple on the first screen. Tap Plan to make a meal plan. The AI suggestions need a network connection. Settings > Delete account deletes the account and its data. Pro features: buy the monthly plan on the paywall (sandbox).

Demo account. Give one only when sign-in needs more than Sign in with Apple, for example an email and password or a code. The reviewer then uses your demo account. Fill it with sample data, so every screen has something to show. With Sign in with Apple as the only sign-in, the reviewer uses their own Apple Account. Then leave the demo account empty, and do not mark sign-in as required. An app with no accounts needs no demo account either.

Subscriptions. The reviewer buys in the sandbox, with test money, in your production build. Make sure of three things:

Contact. A first name, last name, phone number and email of a person Apple can reach during the review. Apple uses it when the app does not work for the reviewer.

With the skill. One file sets the copyright, categories, content rights, review details and the release type (step 9):

{
  "app": "com.example.myapp",
  "copyright": "2026 Example Ltd",
  "primaryCategory": "PRODUCTIVITY",
  "contentRights": "DOES_NOT_USE_THIRD_PARTY_CONTENT",
  "releaseType": "MANUAL",
  "review": {
    "contactFirstName": "Ada",
    "contactLastName": "Example",
    "contactEmail": "[email protected]",
    "contactPhone": "+1 555 0100",
    "notes": "Sign in with Apple on the first screen. ...",
    "demoAccountRequired": false
  }
}
node $A version-set version.json --app com.example.myapp --dry-run

For a demo account, set demoAccountRequired: true, demoAccountName, and demoAccountPasswordRef: the name of the password in your secrets tool (secrets.md). The file never holds the password, and the script never prints it.

9. The release choice

On the version page, in the section for the version release, pick when the approved version goes live:

ChoiceWhat happensUse it for
ManualIt waits for you to press release after approvalThe first release. You pick the moment: the server ready, the landing page live, you awake
AutomaticIt goes live as soon as App Review approves itSmall updates when you do not want to wait
Automatic, not before a dateIt goes live after approval, but not before the date you giveA launch on a set day

Phased release is for updates only. It sends the update to a small share of users with automatic updates first, and to everyone over 7 days. You can pause it for up to 30 days in total. Anyone can still get the update by hand from the App Store. Use it for every update after the first: a bad update then reaches few users before you see the crash reports.

With the skill: releaseType in the version file: MANUAL, AFTER_APPROVAL, or SCHEDULED with earliestReleaseDate. Add "phasedRelease": true for an update.

10. Submit

Run the status once more. Fix every MISSING line. Check the CHECK lines on the web.

On the version page, add the version for review, then submit it. You can also add the first subscription to the same submission there. Add one product only (see “Subscriptions” above).

With the skill:

node $A review-status --app com.example.myapp
node $A submit --app com.example.myapp --dry-run

Without --dry-run, submit sends the version to App Review at once. It refuses when the version has no build, or when a submission is already in review.

What happens next

The version state moves through these steps. Apple emails the account holder at each change.

  1. Waiting for Review. In the queue. Apple says it typically reviews at least 50% of submissions in less than 24 hours and 90% in less than 48 hours (https://developer.apple.com/distribute/app-review/, checked 2026-10-09). A first app can take longer.
  2. In Review. A reviewer has the app. This takes minutes to hours.
  3. Then one of:
    • Pending Developer Release: approved, and you chose manual release. Release it on the version page, or with node $A release --app com.example.myapp --dry-run, then without --dry-run.
    • Ready for Distribution: approved and live. The App Store can take up to a day to show it in every country.
    • Rejected: read on.

Change nothing on the production API that the review build needs while it waits or is in review.

A rejection

A rejection is normal, also for good apps. Read the message on the page for App Review in your app. It names the guideline. The common ones for this kind of app, and what to do, are in start-here.md, Phase 9.

For a critical fix after launch, you can ask for an expedited review on Apple’s App Review contact page. Use it rarely.

Where the values go

ValueWhere
App Privacy answers, price, countries, EU trader statusApp Store Connect only (web)
Age rating answersApp Store Connect. A copy in age.json if you use the skill
Copyright, categories, content rights, review notes, contact, release typeApp Store Connect. A copy in version.json if you use the skill
Demo account passwordYour secrets tool. version.json holds only its name

version.json holds your phone number and email. Keep it out of a public repo: add it to .gitignore.

Check it works

Common errors

Wrong or out of date? Fix it on GitHub.