README is a short front page (about 120 lines) with one real visual per section; every former section moved unchanged into docs/*.md with a docs/README.md index (journey, rejections, getting-started, commands, growth, studios, store-art, listing-languages, api, skills, mcp, safety, faq, project). The site tells the same story on one page: the teaser in the hero, real CLI output as text, the desktop app as a section with light/dark captures of eight screens and the per-system downloads (desktop/latest.json); /desktop/ redirects to /#download. New capture tools: tools/site-desktop-shots.ts (fixture-mode desktop frames) and tools/site-term-shots.ts (real CLI output as text and WebP). tests/docs-links.test.ts checks README, docs and site links resolve.
6.9 KiB
| name | description |
|---|---|
| testflight | Run TestFlight for a mobile app or game through the App Store Connect API - beta groups (internal / external, public links), testers from a CSV that never enters git, adding a build by its exact number to groups, "What to Test" per locale from the release notes, Test Information, and the Beta App Review submission the owner confirms. Use when the user wants testers on a build, sets up beta groups, writes What to Test, or asks why external testers do not see a build. |
TestFlight
CLI: storebound apple testflight <status | groups | testers | what-to-test | info | distribute> (or bunx storebound
in a project that has it). Every write has --dry; run it first and show the plan. API references are cited at the top
of cli/lib/testflight.ts (developer.apple.com/documentation/appstoreconnectapi: beta groups, beta testers, beta build
localizations, beta app localizations, beta app review submissions).
Prerequisites
- The build is uploaded and processed (
storebound apple status,VALID), with export compliance answered (storebound apple encryption --build N, export-compliance skill). A build missing it showsMISSING_EXPORT_COMPLIANCEand nobody can test it. - API key role Admin or App Manager.
1. Groups (config, no emails)
apple: {
testflight: {
groups: [
{ name: 'Team', internal: true, allBuilds: true }, // App Store Connect users of your team, no review
{ name: 'Friends' }, // external, by email, Beta App Review first
{ name: 'Public', publicLink: true, publicLinkLimit: 500 },
],
description: { en: 'Match tiles, clear 60 levels.', tr: '...' }, // Beta App Description per language id
reviewNotes: 'No login needed. IAPs are sandbox purchases.',
},
},
storebound apple testflight groups (lists them and what would be created), then groups sync. Groups are matched by name; storebound never renames,
changes or deletes a group.
2. Testers (CSV in .storebound/, never in git)
- Columns:
email,first_name,last_name,groups(groups separated by;). Default path.storebound/testers.csv(gitignored); the config refuses a path outside.storebound/. Never paste tester emails into chat, docs, issues or commits; storebound masks them in every log line (j***@example.com). storebound apple testflight testers --dryshows: new testers, testers added to more groups, (with--remove) testers who leave a managed group. The real run asks first: Apple emails each new tester an invitation once the group has a build.--invite(re)sends invitations to testers stillNOT_INVITED/INVITED(asks again).- Internal groups only take App Store Connect users (Users and Access); add people there first.
- Testers are an owner action: not an MCP tool. An agent prepares the CSV format and the dry run, the owner runs it.
3. What to Test
storebound whats-newdrafts release notes intostore/appstore.md(### whatsNewper locale). For testers you can add a### whatToTestblock per locale: it wins overwhatsNewfor TestFlight.storebound apple testflight what-to-test --build N --dry, then for real.--text "..." --locale en-USsets one text instead. Pick the build by the exact number you uploaded, never "the latest";--version x.y.zwhen the same build number exists for two versions.- Write it for testers: what changed, what to try, known issues. Short; it is read on a phone. Apple documents no maximum; storebound stops at 4000 characters (assumption, see the unverified list in docs/project.md).
4. Test Information (needed for external testing)
storebound apple testflight info --dry: Beta App Description per locale (apple.testflight.description), feedback
email (app.contactEmail), privacy policy and marketing URL, and the Beta App Review contact (from
.storebound/credentials.json reviewContact) plus notes (4000 characters max). Apple requires a description on every
beta app localization before a Beta App Review submission, and fields cannot be cleared once set.
5. Distribute
- Internal:
storebound apple testflight distribute --build N --group Team --dry, then for real (MCP:apple_testflight_distribute, internal groups only). Groups withallBuilds: trueget every build automatically. - External: same command with an external group; it asks first. External testers only get the build after Beta App
Review: add
--submit-review(asks again) or submit in App Store Connect. The first build of a version is reviewed; later builds of the same version are often approved quickly. Beta review states:WAITING_FOR_REVIEW,IN_REVIEW,APPROVED,REJECTED(storebound apple testflight statusshows internal / external build states). - The Beta App Review submission is public-facing and the owner's call: never through MCP; an agent explains and the owner confirms in the terminal.
6. Wait for processing
storebound apple testflight wait --build N [--timeout 60m] [--json] polls the build you just uploaded (20 s, then
x1.5, at most every 2 minutes) and prints a line whenever its state changes: "not listed yet" for the first minutes,
PROCESSING, then VALID. Exit codes: 0 VALID, 1 FAILED / INVALID (read Apple's email, fix, upload a new build
number), 7 timeout (run it again). --dry looks once and does not wait. On VALID it names the next step, e.g. the
export compliance answer when the build shows MISSING_EXPORT_COMPLIANCE. MCP: apple_testflight_wait (read-only).
In CI, run it in the background with a log and report its lines.
7. Expire old builds (asks first)
storebound apple testflight expire --keep 5 --dry (or --older-than 60, or both: beyond the newest 5 AND older than
60 days; --version 1.2.0 limits it to one version). The newest build always stays; builds attached to an App Store
version (in review, live or being prepared) and builds still processing are never expired. The dry run lists every
build with "expire" / "keep" and why; the real run asks first (--yes in scripts the owner runs) because an expired
build cannot be brought back and testers lose it at once. Not an MCP tool. API: PATCH /v1/builds/{id} with
expired: true (cited in cli/lib/build-ops.ts).
Gotchas
- Builds expire after 90 days in TestFlight; upload a new one (
storebound apple next-build). - Up to 100 internal testers and 10,000 external testers per app (TestFlight overview in App Store Connect Help: https://developer.apple.com/help/app-store-connect/test-a-beta-version/testflight-overview).
- A public link (
publicLink: true) lets anyone with it join, up topublicLinkLimit(1-10000). Share it only where the owner wants; it still needs the approved build. - External testers of an app with IAP or subscriptions buy with sandbox accounts automatically (no charge); say so in What to Test.
- Rejected beta review: read the message in App Store Connect > TestFlight, fix, upload a new build, submit again (review-rejections skill for the tone of the reply).