# Ascelerate

> Use when working with App Store Connect tasks — app submissions, localizations, screenshots, builds, provisioning, in-app purchases, subscriptions, or release workflows. Triggers on App Store, App Store Connect, asc, ascelerate, app review, provisioning profiles, screenshots, localizations, IAP, subscriptions.

- Skill: `keremerkan/ascelerate` (Agent Skill)
- Install (CLI): `npx skillmds@latest add keremerkan/ascelerate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/keremerkan/ascelerate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: keremerkan (https://skillmd.com/u/keremerkan)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/keremerkan/ascelerate

---


# ascelerate

A command-line tool for the App Store Connect API. Use `ascelerate` for all App Store Connect operations instead of the web interface.

## Quick Reference

### App aliases

Use aliases instead of full bundle IDs:

```bash
ascelerate alias add myapp    # Interactive app picker
ascelerate apps info myapp    # Use alias anywhere
```

Any argument without a dot is treated as an alias. Real bundle IDs work unchanged.

### JSON output

Read commands support `--json` for machine-readable output — prefer it over parsing tables:

```bash
ascelerate apps list --json
ascelerate apps info <app> --json
ascelerate apps versions <app> --json
ascelerate apps review preflight <app> --json     # {passed, checks: [{group?, name, passed, detail}]}; exits non-zero on failures
ascelerate apps review status <app> --json        # submissions incl. per-item states
ascelerate builds list --bundle-id <app> --json
ascelerate testflight builds <app> --json
ascelerate testflight status <app> --json
ascelerate reviews list <app> --json              # includes full review bodies + developer responses (no per-review info calls needed)
ascelerate reviews info <review-id> --json
ascelerate iap list <app> --json
ascelerate iap info <app> <product-id> --json     # includes "hasPricing" boolean
ascelerate iap pricing show <app> <product-id> --json
ascelerate sub groups <app> --json
ascelerate sub list <app> --json
ascelerate sub info <app> <product-id> --json     # includes "hasPricing" boolean
ascelerate sub pricing show <app> <product-id> --json
ascelerate rate-limit --json
```

Conventions: list commands emit a top-level JSON array, detail commands a single object; raw API enum values (`WAITING_FOR_REVIEW`, `IOS`); ISO 8601 dates; resource `id` always included; null fields omitted; empty results emit `[]`. `--json` implies non-interactive — ambiguity errors instead of prompting (disambiguate with `--platform` etc.). Errors go to stderr, so stdout is always valid JSON.

### Version management

```bash
ascelerate apps create-version <app> <version>
ascelerate apps copyright <app> --set "2026 Your Name"   # View/update version copyright
ascelerate apps build attach-latest <app>
ascelerate apps build attach <app>        # Interactive build picker
ascelerate apps build detach <app>
```

`--version` targets a specific version. Without it, commands prefer the latest editable version (Prepare for Submission or Waiting for Review).

Universal-purchase apps (one app record, several platforms) can hold the same version per platform. Version-scoped commands (localizations, media, build attach, review, `builds list`/`await-processing`) accept `--platform ios|macos|tvos|visionos` to disambiguate; without it they prompt when ambiguous. `apps create-version` and `apps review submit` default to iOS — pass `--platform macos` for the Mac version/submission.

### Localizations

Two layers: **version-level** (description, what's new, keywords) and **app-level** (name, subtitle, privacy URL).

```bash
# Version localizations
ascelerate apps localizations export <app>
ascelerate apps localizations import <app> --file localizations.json

# App info localizations
ascelerate apps app-info export <app>
ascelerate apps app-info import <app> --file app-infos.json
```

#### Version localization JSON format

```json
{
  "en-US": {
    "description": "App description.",
    "whatsNew": "- Bug fixes",
    "keywords": "keyword1,keyword2",
    "promotionalText": "Promo text",
    "marketingURL": "https://example.com",
    "supportURL": "https://example.com/support"
  }
}
```

#### App info localization JSON format

```json
{
  "en-US": {
    "name": "My App",
    "subtitle": "Best app ever",
    "privacyPolicyURL": "https://example.com/privacy",
    "privacyChoicesURL": "https://example.com/choices"
  }
}
```

Only fields present in the JSON get updated — omitted fields are left unchanged. Import commands create missing locales automatically with confirmation.

### Screenshots & App Previews

```bash
ascelerate apps media download <app>
ascelerate apps media upload <app> media/
ascelerate apps media upload <app> screenshots.zip            # Zip/tar/tar.gz support
ascelerate apps media upload <app>                            # Interactive picker
ascelerate apps media upload <app> media/ --replace           # Replace existing
ascelerate apps media verify <app>                            # Check processing status
ascelerate apps media verify <app> media/                     # Retry stuck items
ascelerate apps media prune <app> media/                      # Delete server sets with no matching local folder
```

#### Folder structure

```
media/
├── en-US/
│   ├── APP_IPHONE_67/
│   │   ├── 01_home.png
│   │   └── 02_settings.png
│   └── APP_IPAD_PRO_3GEN_129/
│       └── 01_home.png
└── de-DE/
    └── APP_IPHONE_67/
        └── 01_home.png
```

Required display types: `APP_IPHONE_67` (iPhone) and `APP_IPAD_PRO_3GEN_129` (iPad). Files sorted alphabetically = upload order. Images become screenshots, videos become previews.

### Review submission

```bash
ascelerate apps review preflight <app>           # Pre-submission checks
ascelerate apps review submit <app>              # Submit (offers to include IAPs/subs)
ascelerate apps review status <app>              # Check status
ascelerate apps review resolve-issues <app>      # After fixing rejection
ascelerate apps review cancel-submission <app>   # Cancel active review
ascelerate apps release <app>                    # Release an approved version in Pending Developer Release
ascelerate apps review info <app>                # View App Review Information
ascelerate apps review info <app> [--contact-email X] [--demo-account-name X] [--demo-account-password X] [--demo-account-required true|false] [--notes X]  # Update (per-version upsert; omitted fields unchanged)
ascelerate apps review attachment list <app> [--version X]
ascelerate apps review attachment upload <app> [--version X] <file>   # reserve/PUT/commit; any file type
ascelerate apps review attachment delete <attachment-id>
```

`preflight` checks build attachment, localizations, app info, screenshots across all locales, plus IAP/subscription state and pricing (warns when an IAP has no price schedule or a sub has no prices). Exits non-zero on failures. With `--json` it emits the structured check list (same exit code) — ideal as a CI gate.

When submitting, the tool detects IAPs and subscriptions and offers to submit them alongside the app version.

### In-app purchases

```bash
ascelerate iap list <app>
ascelerate iap info <app> <product-id>                   # warns if no price schedule set

# Promoted purchases (IAPs or subscriptions, shown on the App Store product page)
ascelerate iap promoted list <app>
ascelerate iap promoted add <app> <product-id> [--visible-for-all true|false] [--enabled true|false]
ascelerate iap promoted remove <app> <product-id>
ascelerate iap promoted reorder <app> <product-id,product-id,...>   # omitted ones appended after
ascelerate iap promoted toggle <app> <product-id> --enabled true|false

ascelerate iap create <app> --name "Name" --product-id <id> --type CONSUMABLE
ascelerate iap update <app> <product-id> --name "New Name"
ascelerate iap delete <app> <product-id>
ascelerate iap submit <app> <product-id>

# Localizations
ascelerate iap localizations view <app> <product-id>
ascelerate iap localizations export <app> <product-id>
ascelerate iap localizations import <app> <product-id> --file iap-de.json

# Pricing — wholesale schedule POST is read-modify-write, preserves overrides
ascelerate iap pricing show <app> <product-id>
ascelerate iap pricing tiers <app> <product-id> --territory USA
ascelerate iap pricing set <app> <product-id> --price 4.99 [--base-territory USA] [--remove-all-overrides]
ascelerate iap pricing override <app> <product-id> --price 5.99 --territory FRA
ascelerate iap pricing remove <app> <product-id> --territory FRA   # revert to auto-equalize
ascelerate iap pricing export <app> <product-id> [--output prices.json]
ascelerate iap pricing import <app> <product-id> [--file prices.json]   # copy schedule from another product/app

# Per-IAP territory availability (independent of app's)
ascelerate iap availability <app> <product-id> [--add CHN,RUS] [--remove ITA] [--available-in-new-territories true]

# Offer codes (one-time-use batches + custom codes)
ascelerate iap offer-code list <app> <product-id>
ascelerate iap offer-code info <app> <product-id> <offer-code-id>
ascelerate iap offer-code create <app> <product-id> --name X --eligibility NON_SPENDER,ACTIVE_SPENDER --price 0.99 [--territory USA] [--equalize-all-territories]
ascelerate iap offer-code toggle <app> <product-id> <offer-code-id> --active true
ascelerate iap offer-code gen-codes <app> <product-id> <offer-code-id> --count 100 --expires 2026-12-31
ascelerate iap offer-code add-custom-codes <app> <product-id> <offer-code-id> --code PROMO2026 --count 1000 --expires 2026-12-31
ascelerate iap offer-code view-codes <one-time-use-batch-id> [--output codes.txt]   # async; may need retry

# Promotional images + App Review screenshot (Apple's 3-step file upload flow)
ascelerate iap images list/upload/delete <app> <product-id> [<file>|<image-id>]
ascelerate iap review-screenshot view/upload/delete <app> <product-id> [<file>]   # one screenshot per IAP
```

IAP types: `CONSUMABLE`, `NON_CONSUMABLE`, `NON_RENEWING_SUBSCRIPTION`. Offer code eligibilities: `NON_SPENDER`, `ACTIVE_SPENDER`, `CHURNED_SPENDER`.

Pricing notes:
- `set` preserves per-territory overrides by default; interactive menu offers to drop them, or `--remove-all-overrides` for non-interactive wipe.
- `override` and `remove` only operate on non-base territories.
- `export`/`import` copy the schedule between products (any app): a JSON file of customer prices keyed by territory, matched to the target's own tiers by price on import. Import replaces the schedule wholesale — territories not in the file revert to auto-equalize; a schedule already matching the file is a no-op.
- One-time-use offer codes generate asynchronously — `gen-codes` returns a batch ID; use `view-codes <batch-id>` to fetch values once ready.

### Subscriptions

```bash
ascelerate sub list <app>
ascelerate sub groups <app>
ascelerate sub info <app> <product-id>                         # warns if no prices set
ascelerate sub create <app> --name "Monthly" --product-id <id> --period ONE_MONTH --group-id <gid>
ascelerate sub update <app> <product-id> --name "New Name"
ascelerate sub delete <app> <product-id>
ascelerate sub submit <app> <product-id>
ascelerate sub submit-group <app>                              # submit whole group for review

# Subscription localizations
ascelerate sub localizations export <app> <product-id>
ascelerate sub localizations import <app> <product-id> --file sub-de.json

# Group management
ascelerate sub create-group <app> --name "Premium"
ascelerate sub update-group <app> --name "Premium Plus"
ascelerate sub delete-group <app>

# Group localizations
ascelerate sub group-localizations export <app>
ascelerate sub group-localizations import <app> --file group-de.json

# Pricing — per-territory; --equalize-all-territories fans out via Apple's equalizations
ascelerate sub pricing show <app> <product-id>
ascelerate sub pricing tiers <app> <product-id> --territory USA
ascelerate sub pricing set <app> <product-id> --price 4.99 [--territory USA] [--equalize-all-territories]
  [--preserve-current | --no-preserve-current]   # required on price increases
  [--confirm-decrease]                           # required on decreases in --yes mode
ascelerate sub pricing export <app> <product-id> [--output prices.json]
ascelerate sub pricing import <app> <product-id> [--file prices.json]   # copy prices from another product/app; same safety flags as set

# Per-subscription territory availability (independent of app's)
ascelerate sub availability <app> <product-id> [--add CHN,RUS] [--remove ITA] [--available-in-new-territories true]

# Introductory offers (free trials + intro discounts for NEW subscribers)
ascelerate sub intro-offer list <app> <product-id>
ascelerate sub intro-offer create <app> <product-id> --mode FREE_TRIAL --duration ONE_WEEK --periods 1
ascelerate sub intro-offer create <app> <product-id> --mode PAY_AS_YOU_GO --duration ONE_MONTH --periods 3 --territory USA --price 0.99
ascelerate sub intro-offer update <app> <product-id> <offer-id> --end-date 2026-12-31
ascelerate sub intro-offer delete <app> <product-id> <offer-id>

# Promotional offers (server-signed offers for EXISTING subscribers)
ascelerate sub promo-offer list/info/delete <app> <product-id> [<offer-id>]
ascelerate sub promo-offer create <app> <product-id> --name X --code LOYALTY50 --mode PAY_AS_YOU_GO --duration ONE_MONTH --periods 3 --price 4.99 --territory USA --equalize-all-territories
ascelerate sub promo-offer update <app> <product-id> <offer-id> --price 5.99 --equalize-all-territories   # only prices can change

# Offer codes (one-time-use batches + custom codes)
ascelerate sub offer-code list/info <app> <product-id> [<offer-code-id>]
ascelerate sub offer-code create <app> <product-id> --name X --eligibility NEW --offer-eligibility STACK_WITH_INTRO_OFFERS --mode FREE_TRIAL --duration ONE_MONTH --periods 1 --price 0 --territory USA --equalize-all-territories
ascelerate sub offer-code toggle <app> <product-id> <offer-code-id> --active true
ascelerate sub offer-code gen-codes <app> <product-id> <offer-code-id> --count 500 --expires 2026-12-31
ascelerate sub offer-code add-custom-codes <app> <product-id> <offer-code-id> --code SUBPROMO --count 1000 --expires 2026-12-31
ascelerate sub offer-code view-codes <one-time-use-batch-id> [--output codes.txt]

# Promotional images + App Review screenshot (Apple's 3-step file upload flow)
ascelerate sub images list/upload/delete <app> <product-id> [<file>|<image-id>]
ascelerate sub review-screenshot view/upload/delete <app> <product-id> [<file>]   # one screenshot per sub
```

Subscription pricing safety:
- **Price increase** in any territory requires `--preserve-current` (grandfather existing subs at old price) OR `--no-preserve-current` (push new price after Apple's notification period). Errors if neither set.
- **Price decrease** prompts interactively; under `--yes`, requires `--confirm-decrease` to acknowledge revenue impact.
- **`--equalize-all-territories`** aggregates the analysis: if any territory in the fan-out is an increase, the preserve flag is required for all.
- **`import`** applies the same aggregated rules; it only touches territories listed in the file and skips ones already at the listed price.
- Transient ASC errors (429/5xx) during multi-territory writes are retried automatically (in-place backoff + end-of-run sweep). Remaining failures are listed with a re-run hint and exit non-zero — re-running retries only the failed territories.

Offer code eligibilities for subs: `NEW`, `EXISTING`, `EXPIRED`. Offer eligibility: `STACK_WITH_INTRO_OFFERS` or `REPLACE_INTRO_OFFERS`. Modes: `FREE_TRIAL`, `PAY_AS_YOU_GO`, `PAY_UP_FRONT`.

NOT YET IMPLEMENTED: `sub win-back-offer` is blocked on asc-swift codegen (the inline price create type is missing required relationships). Custom product page search-keyword linkages are blocked by the same kind of codegen gap (the `AppKeyword` entity exposes no keyword text). `iap hosted-content` is intentionally skipped.

### Customer reviews

```bash
ascelerate reviews list <app> [--rating 1-5] [--territory USA] [--sort recent|oldest|critical|best] [--unanswered] [--limit 50]
ascelerate reviews info <review-id>                       # full text + developer response
ascelerate reviews respond <review-id> --body "..."       # publish/replace developer response
ascelerate reviews delete-response <review-id>
```

Reviews are read-only; only the developer response is writable. Review IDs come from `reviews list`. Use `reviews list --json` to get full review bodies and existing developer responses in one call — no per-review `info` calls needed.

The App Store Connect API does **not** expose aggregate rating counts or the star-rating average/histogram — only individual reviews (above). For download/unit numbers and revenue, use `reports` (below).

### Reports (downloads, proceeds, analytics)

```bash
# Sales & Trends — units/downloads + proceeds. Summarizes units by title/product type.
ascelerate reports sales [--frequency DAILY|WEEKLY|MONTHLY|YEARLY] [--date X] [--type SALES] [--sub-type SUMMARY] [--bundle-id <app>] [--vendor-number X] [--output report.tsv] [--raw]
# Financial report — units + partner proceeds for a fiscal period.
ascelerate reports finance --date YYYY-MM --region US [--type FINANCIAL|FINANCE_DETAIL] [--vendor-number X] [--output X] [--raw]
# App Analytics — downloads, impressions, sessions, etc. (async: creates/reuses a report request, downloads segments).
ascelerate reports analytics <app> [--category APP_STORE_ENGAGEMENT|APP_USAGE|COMMERCE|FRAMEWORK_USAGE|PERFORMANCE] [--granularity DAILY|WEEKLY|MONTHLY] [--report-name X] [--processing-date YYYY-MM-DD] [--ongoing] [--output dir/] [-y]
```

- **Vendor number** is required for `sales`/`finance` (App Store Connect → Payments and Financial Reports). Save it once with `ascelerate configure`, or pass `--vendor-number`.
- `sales`/`finance` default to a parsed **summary**; add `--raw` to print the TSV or `--output PATH` to save it. `--date` defaults to the most recent completed period for sales; for `finance` it's a *fiscal* period `YYYY-MM` (not a calendar month) and is required along with `--region`.
- `analytics` reuses an existing report request or prompts to create one (a one-time snapshot by default; `--ongoing` for recurring). A freshly created snapshot isn't ready immediately — re-run the command after a few minutes to download the segments.

### In-app events

```bash
ascelerate events list <app> [--state PUBLISHED]
ascelerate events info <app> <ref-or-id>
ascelerate events create <app> --reference-name X [--badge SPECIAL_EVENT] [--purpose ATTRACT_NEW_USERS] [--priority HIGH] [--deep-link URL] [--primary-locale en-US] [--territories USA,GBR --publish-start 2026-07-01 --event-start 2026-07-05 --event-end 2026-07-12]
ascelerate events update <app> <ref-or-id> [--badge NONE]   # --badge NONE clears the badge
ascelerate events delete <app> <ref-or-id>

# Localizations (name, shortDescription, longDescription)
ascelerate events localizations view <app> <ref-or-id>
ascelerate events localizations export <app> <ref-or-id>
ascelerate events localizations import <app> <ref-or-id> --file event-locales.json

# Media (png/jpg -> screenshot, mp4/mov -> video clip)
ascelerate events media list <app> <ref-or-id>
ascelerate events media upload <app> <ref-or-id> --locale en-US --asset-type EVENT_CARD|EVENT_DETAILS_PAGE <file> [--preview-frame 00:00:03]
ascelerate events media delete <app> <ref-or-id> <media-id>
```

Badges: `LIVE_EVENT`, `PREMIERE`, `CHALLENGE`, `COMPETITION`, `NEW_SEASON`, `MAJOR_UPDATE`, `SPECIAL_EVENT`. Purposes: `APPROPRIATE_FOR_ALL_USERS`, `ATTRACT_NEW_USERS`, `KEEP_ACTIVE_USERS_INFORMED`, `BRING_BACK_LAPSED_USERS`. Schedule dates: ISO8601 or `yyyy-MM-dd`. Event localization locales must match the app's locales (e.g. `tr`, not `tr-TR`).

#### Event localization JSON format

```json
{ "en-US": { "name": "Summer Sale", "shortDescription": "...", "longDescription": "..." } }
```

### Custom product pages

```bash
ascelerate product-pages list <app>
ascelerate product-pages info <app> <name-or-id>
ascelerate product-pages create <app> --name X --locale en-US [--promotional-text "..."]   # compound create (page + version + first locale)
ascelerate product-pages update <app> <name-or-id> [--name X] [--visible true|false]
ascelerate product-pages delete <app> <name-or-id>
ascelerate product-pages localizations view <app> <name-or-id>
ascelerate product-pages localizations export <app> <name-or-id>
ascelerate product-pages localizations import <app> <name-or-id> --file page-locales.json    # promotional text per locale

# Media (png/jpg -> screenshot by --display-type, mp4/mov -> app preview by --preview-type; set auto-created)
ascelerate product-pages media list <app> <name-or-id>
ascelerate product-pages media upload <app> <name-or-id> --locale en-US --display-type APP_IPHONE_67 shot.png
ascelerate product-pages media upload <app> <name-or-id> --locale en-US --preview-type APP_IPHONE_67 clip.mp4 [--preview-frame 00:00:03]
ascelerate product-pages media delete <app> <name-or-id> <media-id>
```

Pages are referenced by name or ID. `create` issues a compound POST (page + version + localization linked via `${local-id}` inline references).

#### Custom product page localization JSON format

```json
{ "en-US": { "promotionalText": "Limited-time offer" } }
```

### Screenshots (Simulator Capture)

Capture App Store screenshots from simulators using UI tests. Replaces fastlane snapshot.

```bash
ascelerate screenshot init                          # Generate config and helper in ascelerate/ directory
ascelerate screenshot create-helper                 # Generate ScreenshotHelper.swift for UITest target
ascelerate screenshot run                           # Capture screenshots
ascelerate screenshot run -l en-US,tr-TR            # Override languages (subset of configured), comma-separated
ascelerate screenshot frame                         # Frame captured screenshots with device bezels
ascelerate screenshot doctor                        # Check config and environment for problems
```

#### Config (`ascelerate/screenshot.yml`)

```yaml
project: MyApp.xcodeproj
scheme: AppUITests
devices:
  - simulator: iPhone 17 Pro Max
    # frameDevice: true
    # deviceBezel: ./bezels/iPhone 17 Pro Max.png
  - simulator: iPad Pro 13-inch (M5)
    # frameDevice: true
    # deviceBezel: ./bezels/iPad Pro 13-inch (M5).png
languages: [en-US, de-DE]
outputDirectory: ./screenshots
clearPreviousScreenshots: true
localizeSimulator: true
overrideStatusBar: true
# helperPath: AppUITests/ScreenshotHelper.swift
# testWithoutBuilding: true
# headless: true
# darkMode: false
# disableAnimations: true
# waitAfterBoot: 5
# waitAfterEraseAndReboot: 30   # extra wait for first-run system alerts (Apple Intelligence etc.); fires on first language and on retries
# configuration: Debug
# testplan: MyTestPlan
# numberOfRetries: 1
# stopAfterFirstError: false
# reinstallApp: false
# xcargs: SWIFT_ACTIVE_COMPILATION_CONDITIONS=SCREENSHOTS
# framedOutputDirectory: ./screenshots/framed
```

Supports dark mode capture, animation disabling, automatic retries (erases simulator, re-localizes, reboots), custom test plans, Xcode build configurations, and arbitrary xcodebuild arguments.

#### Device framing

Frame screenshots with Apple device bezels (from [Apple Design Resources](https://developer.apple.com/design/resources/)). Per-device: set `frameDevice: true` and `deviceBezel` path to the bezel PNG. Framing runs automatically after capture, or standalone via `screenshot frame`. Output goes to `framedOutputDirectory` (defaults to `{outputDirectory}/framed`). Use `screenshot doctor` to validate config, simulators, bezels, and environment.

#### UITest usage

```swift
override func setUp() {
    setupScreenshots(app)
    app.launch()
}

func testScreenshots() {
    screenshot("01-home")
    app.buttons["Settings"].tap()
    screenshot("02-settings")
}
```

Builds once, runs tests concurrently across devices per language. Output: `screenshots/{language}/{device}-{name}.png`. Errors skip and continue with summary table.

### Builds

```bash
ascelerate builds archive                           # Auto-detects workspace/scheme
ascelerate builds upload MyApp.ipa
ascelerate builds await-processing <app>             # Wait for processing
ascelerate builds list --bundle-id <app>
```

### TestFlight

```bash
# Beta groups
ascelerate testflight groups list <app>
ascelerate testflight groups info <app> "External Testers"        # details + testers + builds
ascelerate testflight groups create <app> --name "Friends" --public-link --public-link-limit 100
ascelerate testflight groups update <app> "Friends" --public-link false
ascelerate testflight groups add-build <app> "Friends" --build 123 # omit --build for latest
ascelerate testflight groups delete <app> "Friends"

# Recruitment criteria (device/OS filters for public links)
ascelerate testflight groups criteria view <app> "Friends" --options
ascelerate testflight groups criteria set <app> "Friends" --filter IPHONE:18.0 --filter IPAD:17.0:26
ascelerate testflight groups criteria clear <app> "Friends"

# Testers
ascelerate testflight testers list <app> [--group "Friends"]
ascelerate testflight testers add <app> --email t@example.com --group "Friends"
ascelerate testflight testers remove <app> t@example.com --group "Friends"  # omit --group to remove from whole app
ascelerate testflight testers invite <app> t@example.com  # re-send invitation email
ascelerate testflight testers import <app> --file testers.csv --group "Friends"  # CSV: email[,first[,last]]

# Builds & distribution
ascelerate testflight builds <app>                        # TestFlight states per build
ascelerate testflight versions <app>                      # pre-release version trains
ascelerate testflight status <app> [--build 123]          # processing/internal/external/beta review
ascelerate testflight expire <app> --build 123
ascelerate testflight notify <app>                        # notify testers of latest build
ascelerate testflight auto-notify <app> --enabled true    # toggle automatic notification

# Tester feedback
ascelerate testflight feedback crashes list <app> [--build 123]
ascelerate testflight feedback crashes log <submission-id> [--output crash.log]
ascelerate testflight feedback screenshots list <app>
ascelerate testflight feedback screenshots download <app> [submission-id] [--output X.zip]  # zip of screenshots + comment; picker if ID omitted
ascelerate testflight feedback crashes|screenshots info <submission-id>    # full device details + comment
ascelerate testflight feedback crashes|screenshots delete <submission-id>

# What to Test (per build, per locale)
ascelerate testflight whats-new view <app>
ascelerate testflight whats-new set <app> --text "Bug fixes" [--locale en-US]  # all locales if omitted
ascelerate testflight whats-new export <app> --output notes.json
ascelerate testflight whats-new import <app> --file notes.json

# Beta review (required for external testing)
ascelerate testflight submit <app> [--build 123]
ascelerate testflight app-info view <app>                 # beta description, feedback email per locale
ascelerate testflight app-info update <app> --locale en-US --feedback-email me@example.com
ascelerate testflight review-info <app>                   # contact + demo account; pass flags to update
ascelerate testflight eula <app> [--file eula.txt | --text "..."]  # --text "" reverts to Apple's standard
```

Build-scoped commands default to the latest non-expired build; pass `--build <number>` (and `--platform` for universal-purchase apps) to target a specific one. What to Test JSON format: `{"en-US": {"whatsNew": "..."}}`.

### Provisioning

All provisioning commands support interactive mode (run without arguments for guided prompts):

```bash
# Devices
ascelerate devices list
ascelerate devices register

# Certificates (auto-generates CSR)
ascelerate certs create --type DISTRIBUTION
ascelerate certs revoke

# Bundle IDs & capabilities
ascelerate bundle-ids register --name "My App" --identifier com.example.MyApp --platform IOS
ascelerate bundle-ids enable-capability com.example.MyApp --type PUSH_NOTIFICATIONS

# Profiles
ascelerate profiles create --name "My Profile" --type IOS_APP_STORE --bundle-id com.example.MyApp --certificates all
ascelerate profiles reissue --all-invalid
```

Note: provisioning commands (devices, certs, bundle-ids, profiles) do NOT support aliases.

### App configuration

```bash
ascelerate apps app-info view <app>
ascelerate apps app-info update <app> --primary-category UTILITIES
ascelerate apps app-info age-rating <app>
ascelerate apps app-info age-rating export <app>
ascelerate apps app-info age-rating import <app> --file age-rating.json
ascelerate apps availability <app> --add CHN,RUS
ascelerate apps encryption <app> --create --description "Uses HTTPS"
ascelerate apps eula <app> --file eula.txt
ascelerate apps phased-release <app> --enable

# App-level subscription grace period (after failed renewal payments)
ascelerate apps subscription-grace-period <app>                         # view
ascelerate apps subscription-grace-period <app> --opt-in true --duration SIXTEEN_DAYS --renewal-type ALL_RENEWALS
ascelerate apps subscription-grace-period <app> --sandbox-opt-in true
```

Grace period durations: `THREE_DAYS`, `SIXTEEN_DAYS`, `TWENTY_EIGHT_DAYS`. Renewal types: `ALL_RENEWALS`, `PAID_TO_PAID_ONLY`.

### Workflow files

Automate multi-step releases with a plain text file:

```
# release.workflow
apps create-version com.example.MyApp 2.1.0
builds archive --scheme MyApp
builds upload --latest --bundle-id com.example.MyApp
builds await-processing com.example.MyApp
apps localizations import com.example.MyApp --file localizations.json
apps build attach-latest com.example.MyApp
apps review preflight com.example.MyApp
apps review submit com.example.MyApp
```

```bash
ascelerate run-workflow release.workflow
ascelerate run-workflow release.workflow --yes   # Skip all prompts (CI/CD)
```

Commands are one per line, without the `ascelerate` prefix. Lines starting with `#` are comments. `builds upload` automatically passes the build version to subsequent commands.

## Adding a new language to an app

When the user asks to add a new language/locale to an app, translate **all** of these:

1. **App info localizations** (name, subtitle, privacy URLs) — always
2. **Version localizations** (description, what's new, keywords, promo text) — always
3. **In-app purchases** — ask the user: translate all IAPs, or specific ones?
4. **Subscription groups** — ask the user: translate all groups, or specific ones?
5. **Subscriptions** — ask the user: translate all subscriptions, or specific ones?

### Workflow

1. Export existing localizations to see the source text:
   ```bash
   ascelerate apps app-info export <app>
   ascelerate apps localizations export <app>
   ascelerate iap localizations view <app> <product-id>       # for each IAP
   ascelerate sub group-localizations export <app>
   ascelerate sub localizations export <app> <product-id>     # for each subscription
   ```
2. Ask the user which IAPs, subscription groups, and subscriptions to translate (or all).
3. Translate the source text into the new locale.
4. Import the translations (use `-y` to skip confirmation prompts):
   ```bash
   ascelerate apps app-info import <app> --file app-infos.json -y
   ascelerate apps localizations import <app> --file localizations.json -y
   ascelerate iap localizations import <app> <product-id> --file iap.json -y
   ascelerate sub group-localizations import <app> --file group.json -y
   ascelerate sub localizations import <app> <product-id> --file sub.json -y
   ```

## Tips

- Add `--yes` / `-y` to skip confirmation prompts (for scripting/CI)
- Add `--json` to read commands for machine-readable output (see JSON output above)
- Use `ascelerate rate-limit` to check API quota (3600 requests/hour)
- Run `ascelerate install-completions` after updates for tab completion
- Only editable versions (Prepare for Submission / Waiting for Review) accept updates
- `promotionalText` can be updated on any version state
- Export JSON → edit → import is the fastest way to update localizations across locales

