--- name: appstore-first-release description: Step-by-step first (and later) App Store release of a mobile app or game - App Store Connect API key, bundle id, app record, signing, build and upload with the API key (apple signing, apple build, apple upload), TestFlight, version + listing text in many locales, screenshots, age rating, App Privacy, review details, in-app purchases with the first version, and submitting for review. Use when the user wants to publish, release, upload or set up an iOS app or game on the App Store or TestFlight, or hits App Store Connect API errors. --- # App Store: first release The CLI is `storebound` (on PATH while the plugin is enabled; otherwise `bunx storebound` in a project that has it, or `bun /cli/index.ts`; agents can also use the `storebound mcp` tools). Config: `storebound.config.ts`; credentials: `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_P8` (env or `.storebound/credentials.json`, gitignored). All commands are idempotent and have `--dry`. Run `storebound check` first. **Do it, don't ask** (first rule in AGENTS.md / `docs/agents-core.md` of the toolchain): you run every step below yourself, API steps with storebound and UI steps in App Store Connect with your browser tool, and verify each. The owner only presses Submit for Review, Release, App Privacy Publish, cancels a submission or accepts agreements, and answers the compliance facts no file proves, all in one batch at the start. ## Steps (API = storebound, UI = App Store Connect, driven by you with your browser tool; Local = this machine) 1. **UI · API key.** App Store Connect > Users and Access > Integrations > App Store Connect API > Team Keys > + (Admin or App Manager). Download `AuthKey_.p8` once, keep it in `~/.appstoreconnect/private_keys/`, note the Key ID and Issuer ID. The same key signs builds (`-allowProvisioningUpdates`). 2. **API · Bundle id.** `storebound apple bundle` registers it (Certificates, Identifiers & Profiles). 3. **UI · App record.** Apps > + > New App: iOS, name, primary language, the bundle id, SKU. **No API for this.** The name is unique on the App Store: when App Store Connect says it is "already being used", ask the owner for a longer App Store name (30 characters at most, e.g. "STAR DASH: Stone Age to 2100"), set it as `apple.name` and fill again (`storebound console fill apple-app-record`). `app.name` stays the Play and on-device name; `apple listing` sends `apple.name` for every locale without its own `### name` block, and `storebound check` flags a name over 30 characters. Then `storebound apple status` finds it (put the Apple ID in `apple.appId` to skip the lookup). 4. **Local · Info.plist.** `ITSAppUsesNonExemptEncryption` with the true answer (`export-compliance` skill; `NO` only when the app uses nothing beyond OS / HTTPS encryption) so builds arrive answered; `NSUserTrackingUsageDescription` if ads use the IDFA; privacy policy reachable inside the app (guideline 5.1.1). 5. **Local · Sign, build + upload** (Mac with Xcode; the API key signs, no Xcode account login needed). Details and the manual-signing route: `build-and-sign` skill. ```sh storebound apple signing status # capabilities, certificate with a key on this Mac, profiles storebound apple build --version 1.0.0 --dry # project + scheme found, next build number, ExportOptions.plist storebound apple build --version 1.0.0 # xcodebuild archive + exportArchive -> build/ios/export/-/*.ipa storebound apple upload --dry # then without --dry (asks): API build upload, waits for VALID ``` `apple build` takes the build number from `storebound apple next-build` unless `--build-number N` is given, and prints it at the end: **note it**, every later step selects that exact build, never "the latest". The generated ExportOptions.plist uses `method` app-store-connect, `destination` export, your team and the signing style of `apple.signing.style` (automatic by default). Processing takes 5-30 min; `apple upload` waits for it. 6. **API · TestFlight.** Put an internal group with `allBuilds: true` in `apple.testflight.groups` and run `storebound apple testflight groups sync`: every new build reaches the team automatically. External testers, What to Test, Test Information and Beta App Review: the testflight skill. Sandbox testers stay UI: Users and Access > Sandbox. 7. **API · Version + text.** Fill `store/appstore.md`, then `storebound apple listing --version 1.0.0 --build 3` (version, copyright, MANUAL release, the build with exactly that number, name / subtitle / privacy URL, description / keywords / promo / URLs). Run it with `--dry` first and show the owner the plan: the version decision (keep, update the editable version's string, create, or refuse with the reason), the build, and per locale create / update with old -> new per field. Locales still marked `status=draft` / `status=translate` are skipped until `listing reviewed` (`--include-drafts` only when the owner says so). Then run it yourself with `--yes`: it applies exactly that plan. Then `storebound apple release --phased on`: manual release + phased release is the default for every version (`release-management`). 8. **API · Screenshots.** `store-screenshots` skill, then `storebound apple screenshots > .storebound/asc-shots.log 2>&1 &` (6.9" + 6.5" per locale, MD5-idempotent). 9. **API · App info.** `storebound apple appinfo` (`--dry` first: old -> new per field): categories, age rating declaration, content rights, Free price, all territories. The **first** availability record of a new app may be refused by the public API (an error on POST /v2/appAvailabilities); then set it once in App Store Connect > Pricing and Availability with your browser tool and re-run (later runs only read it). 10. **API · Review details.** `storebound apple review-details` (contact from `.storebound/credentials.json` `reviewContact`, or `--contact-from `; notes from the config). Apps behind a sign-in: the demo account from `.storebound/` (`review-access` skill). Apps with accounts: in-app deletion (`account-deletion` skill). 11. **API · In-app purchases and subscriptions.** `store-iap-setup` and `store-subscriptions` skills. First IAPs and subscriptions go to review **with** this version: `storebound apple iap-submit --with-version --dry`, then (after the owner's yes, since it submits) `storebound apple iap-submit --with-version --yes`. On a real first release (2026-10-12) it added the IAPs to the draft submission that already held version 1.0.0 and submitted them, first try. Not the console: its IAP "Add for Review" menu did not react to scripted clicks, and the version page no longer has an "In-App Purchases and Subscriptions" section. 12. **UI · App Privacy.** `store-declarations` skill (nutrition labels; you fill and save them), then the owner presses **Publish**. 13. **Preflight.** `storebound validate --apple` until it shows no blocker (`store-preflight`). 14. **Submit.** `storebound apple submit --build ` (dry run: the plan table with the build you uploaded, by its number, the version and the ready IAPs in one review submission), then the owner's yes for the same command with `--yes`; with IAPs `apple iap-submit --with-version --yes` does the same without attaching a build (step 11). In the desktop app the owner presses Releases > Submit for review (plan, then a system dialog). Release type MANUAL means the owner presses "Release" after approval. 15. **Verify + wait.** `storebound apple status`: version WAITING_FOR_REVIEW, IAPs WAITING_FOR_REVIEW, one review submission. `storebound apple status --wait-review` polls until approved (exit 0) or rejected (exit 1, `review-rejections`). Approved: the owner releases (`release-management`). ## Gotchas - The app record cannot be created via API; the bundle id can. - First consumable / non-consumable IAPs must be submitted together with an app version. The deprecated `inAppPurchaseSubmissions` answers `FIRST_*_MUST_BE_SUBMITTED_ON_VERSION`; `storebound apple iap-submit --with-version` puts both into one review submission through `reviewSubmissionItems` (worked on a real first release, 2026-10-12). The console route is broken for agents: the IAP "Add for Review" menu ignored scripted clicks and the version page has no "In-App Purchases and Subscriptions" section any more. If the version was already submitted without them: cancel the review submission (`storebound apple status --cancel-submission `, the owner's yes), wait a minute, then `apple iap-submit --with-version` (pitfall 28). - Rejected (Invalid Binary, a crash, ...): the review submission stays open with UNRESOLVED_ISSUES and keeps the version and its IAPs. Upload a fixed build, then `storebound apple resubmit --build ` (dry run with the plan table), and after the owner's yes `--build --yes`: it attaches build n, reuses that submission (no second draft), adds only missing items, marks the REJECTED version item resolved (Apple refuses every submit with "not ready to be submitted yet" until it is; IAP items with `--resolve-iaps`) and resubmits, retrying only while nothing is left REJECTED (pitfall 30, `review-rejections`). `apple iap-submit` reuses it the same way. An empty draft (0 items) cannot be canceled and is harmless. - App Privacy (data types, purposes, linked, tracking) is UI-only and must be **published**, not just saved. - Export compliance unanswered = "Missing Compliance": `storebound apple encryption` (`export-compliance`). - ITMS-91053 / 91061 privacy manifest emails after upload: `privacy-manifest` skill. - Screenshot sizes: 6.9" 1320 x 2868 (`APP_IPHONE_67`) is required; 6.5" 1284 x 2778 (`APP_IPHONE_65`) recommended. PNG without alpha. Up to 10 per set. Apple processes uploads async; failures show as FAILED later. - App Store icon (1024) must have **no alpha channel** (`storebound icons` writes rgb24). - Limits: name 30, subtitle 30, promotional text 170, keywords 100 characters (not bytes; no spaces after commas), description 4000. No "What's New" on a first version. - Guideline 5.1.1: the privacy policy link must be inside the app too (Settings > Privacy), not only in the listing. - `xcodebuild` with Capacitor needs a UTF-8 locale (`export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8`). - 500 / 503 from the API happen; storebound retries with backoff.