--- name: appstore-first-release description: Step-by-step first (and later) App Store release of a mobile game - App Store Connect API key, bundle id, app record, signing and xcodebuild archive/upload with the API key, 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 game/app 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. **Ask the owner before submitting for review, releasing, or canceling a submission.** The final "Submit for Review" of a first version is the owner's click. Shared agent rules: `docs/agents-core.md` of the toolchain. ## Steps (API = storebound, UI = App Store Connect / Xcode by hand) 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.** 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 game 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 · Build + upload** (Mac, no Xcode account login needed, the API key does signing): ```sh K="-allowProvisioningUpdates -authenticationKeyPath $ASC_P8 -authenticationKeyID $ASC_KEY_ID -authenticationKeyIssuerID $ASC_ISSUER_ID" xcodebuild -project ios/App/App.xcodeproj -scheme App -configuration Release -destination generic/platform=iOS \ -archivePath build/App.xcarchive MARKETING_VERSION=1.0.0 CURRENT_PROJECT_VERSION=3 $K archive xcodebuild -exportArchive -archivePath build/App.xcarchive -exportOptionsPlist ExportOptions.plist -exportPath build/export $K ``` ExportOptions.plist: `method` app-store-connect, `destination` upload, `teamID` , `signingStyle` automatic. `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION` overrides beat the project values. Take the build number from `storebound apple next-build` right before archiving and **note it**: every later step selects that exact build, never "the latest". Processing takes 5-30 min (`storebound apple status` shows VALID). 6. **API · TestFlight.** Put an internal group with `allBuilds: true` in `apple.testflight.groups` and run `storebound apple testflight groups`: 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). 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`: 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 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). 11. **API + UI · In-app purchases.** `store-iap-setup` skill. First IAPs go to review **with** this version. 12. **UI · App Privacy.** `store-declarations` skill (nutrition labels), then **Publish**. 13. **Preflight.** `storebound validate --apple` until it shows no blocker (`store-preflight`). 14. **UI · Submit.** Version page > Add for Review (with the IAPs in the same draft submission) > Submit for Review (owner). 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, only from the IAP page ("Add for Review" puts them into the draft submission). The API (`inAppPurchaseSubmissions`) answers `FIRST_*_MUST_BE_SUBMITTED_ON_VERSION`. If the version was already submitted without them: cancel the review submission (`storebound apple status --cancel-submission `, PATCH canceled: true), wait a minute, add the IAPs from their page, then resubmit. - 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 bytes (UTF-8, 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.