asc-client is a command-line tool for the App Store Connect API, built in Swift. It covers the entire app release pipeline — from Xcode archive to App Review submission — plus provisioning, metadata management, and everything in between. A single native binary, no runtime dependencies.
Installation
Homebrew (recommended)
brew tap keremerkan/tap
brew install asc-client
Pre-built binary for Apple Silicon. Installation is instant.
Download the binary
Download the latest release from GitHub Releases:
curl -L https://github.com/keremerkan/asc-client/releases/latest/download/asc-client-macos-arm64.tar.gz -o asc-client.tar.gz
tar xzf asc-client.tar.gz
mv asc-client /usr/local/bin/
Since the binary isn’t notarized, remove the quarantine attribute:
xattr -d com.apple.quarantine /usr/local/bin/asc-client
Build from source
git clone https://github.com/keremerkan/asc-client.git
cd asc-client
swift build -c release
strip .build/release/asc-client
cp .build/release/asc-client /usr/local/bin/
Requires Swift 6.0+ and macOS 13+. The release build takes a few minutes because the asc-swift dependency includes ~2500 generated source files covering the entire API surface. strip reduces the binary from ~175 MB to ~64 MB.
Shell completions
Tab completion for all subcommands, options, and flags (zsh and bash):
asc-client install-completions
Restart your shell to activate. The tool detects outdated completions after updates and reminds you to reinstall.
Setup
1. Create an API Key
Go to App Store Connect > Users and Access > Integrations > App Store Connect API and generate a new key with the App Manager role. Download the .p8 private key file.
2. Configure
asc-client configure
This prompts for your Key ID, Issuer ID, and the path to your .p8 file. The private key is copied into ~/.asc-client/ with strict file permissions (owner-only access). Authentication uses ES256 JWT tokens, auto-renewed by the underlying library.
Aliases
Typing full bundle IDs gets old. Aliases map short names to bundle IDs:
asc-client alias add myapp
# Interactive picker shows your apps — select one
# Now use the alias everywhere
asc-client apps info myapp
asc-client apps versions myapp
asc-client builds list --bundle-id myapp
Any argument without a dot is treated as an alias. Real bundle IDs (which always contain dots) work unchanged, so there’s no ambiguity. Aliases are stored in ~/.asc-client/aliases.json.
List or remove aliases:
asc-client alias list
asc-client alias remove myapp
Workflow Files
The most powerful feature. Instead of running commands one by one, write a plain text file with one command per line:
# release.workflow — Ship MyApp v2.1.0
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
Run it:
asc-client run-workflow release.workflow
The workflow displays all steps, asks for confirmation, and runs them sequentially. If any step fails, it stops and tells you where.
Steps are aware of each other. When builds upload finishes, the uploaded build version is automatically passed to subsequent commands — no need to hardcode build numbers. The await-processing step polls until the build finishes processing on Apple’s servers.
Add --yes for CI/CD environments where there’s no one to confirm. Workflows can call other workflows (with circular reference detection). Both .workflow and .txt extensions work. Lines starting with # are comments, blank lines are ignored.
Version Management
Creating versions
asc-client apps create-version com.example.MyApp 2.1.0
asc-client apps create-version com.example.MyApp 2.1.0 --platform ios --release-type manual
The --release-type flag is optional — omitting it uses the previous version’s setting. The command is idempotent: re-running after a partial failure picks up where it left off.
Build management
# Interactively select and attach a build
asc-client apps build attach com.example.MyApp
# Attach the most recent build automatically
asc-client apps build attach-latest com.example.MyApp
# Remove the attached build
asc-client apps build detach com.example.MyApp
If the latest build is still processing, attach-latest prompts to wait. With --yes, it waits automatically.
Phased releases
# View phased release status
asc-client apps phased-release com.example.MyApp
# Enable, pause, resume, or complete
asc-client apps phased-release com.example.MyApp --enable
asc-client apps phased-release com.example.MyApp --pause
asc-client apps phased-release com.example.MyApp --resume
asc-client apps phased-release com.example.MyApp --complete
# Remove phased release entirely
asc-client apps phased-release com.example.MyApp --disable
Phased release starts inactive and activates automatically when the version goes live.
Localizations
App Store Connect has two layers of localizations: version-level (description, what’s new, keywords) and app-level (name, subtitle, privacy policy URL). asc-client handles both with the same export/import pattern.
Version localizations
# View all locales for the latest version
asc-client apps localizations view com.example.MyApp
# Export to JSON
asc-client apps localizations export com.example.MyApp
# Update a single locale via flags
asc-client apps localizations update com.example.MyApp --whats-new "Bug fixes" --locale en-US
# Bulk update from JSON
asc-client apps localizations import com.example.MyApp --file localizations.json
The JSON format:
{
"en-US": {
"description": "App description here.\n\nSecond paragraph.",
"whatsNew": "- Bug fixes\n- New dark mode",
"keywords": "productivity,tools,utility",
"promotionalText": "Try our new features!",
"marketingURL": "https://example.com",
"supportURL": "https://example.com/support"
},
"de-DE": {
"whatsNew": "- Fehlerbehebungen\n- Neuer Dunkelmodus"
}
}
Only fields present in the JSON get updated — omitted fields are left unchanged. This means you can have a whats-new.json that only contains whatsNew fields for each locale, and update just those without touching descriptions or keywords.
App info localizations
# View app info, categories, and per-locale metadata
asc-client apps app-info view com.example.MyApp
# Update fields for a single locale
asc-client apps app-info update com.example.MyApp --name "My App" --subtitle "Best app ever"
asc-client apps app-info update com.example.MyApp --locale de-DE --name "Meine App"
# Update categories
asc-client apps app-info update com.example.MyApp --primary-category UTILITIES
# Export/import in bulk
asc-client apps app-info export com.example.MyApp --output app-infos.json
asc-client apps app-info import com.example.MyApp --file app-infos.json
List all available category IDs with asc-client apps app-info view --list-categories.
Screenshots and App Previews
Managing screenshots across locales and device sizes is one of the most painful parts of App Store Connect. asc-client handles the entire flow with a folder-based approach.
Download and upload
# Download everything to a local folder
asc-client apps media download com.example.MyApp
# The folder structure mirrors the API
# media/en-US/APP_IPHONE_67/01_home.png
# media/en-US/APP_IPAD_PRO_3GEN_129/01_home.png
# media/de-DE/APP_IPHONE_67/01_home.png
# Edit locally, then re-upload
asc-client apps media upload com.example.MyApp --folder media/ --replace
Folder structure
media/
├── en-US/
│ ├── APP_IPHONE_67/
│ │ ├── 01_home.png
│ │ ├── 02_settings.png
│ │ └── preview.mp4
│ └── APP_IPAD_PRO_3GEN_129/
│ └── 01_home.png
└── de-DE/
└── APP_IPHONE_67/
├── 01_home.png
└── 02_settings.png
- Level 1: Locale (
en-US,de-DE,ja, etc.) - Level 2: Display type folder name (see below)
- Level 3: Media files — images (
.png,.jpg,.jpeg) become screenshots, videos (.mp4,.mov) become app previews
Files are sorted alphabetically for ordering. Prefix them 01_, 02_, 03_ and they’ll appear in the right order on the App Store.
Required display types
App Store Connect requires APP_IPHONE_67 (iPhone 6.7″) and APP_IPAD_PRO_3GEN_129 (iPad Pro 12.9″ 3rd gen+) screenshots. All other display types are optional.
Additional display types include APP_IPHONE_61, APP_IPHONE_65, APP_IPHONE_55, APP_IPAD_PRO_3GEN_11, APP_DESKTOP, APP_APPLE_TV, APP_APPLE_VISION_PRO, various Watch sizes, and iMessage variants. Watch and iMessage types support screenshots only — video files in those folders are skipped.
Verify and retry stuck media
Screenshots and previews sometimes get stuck in “processing” after upload. media verify shows the status of every item and can retry stuck ones:
# Read-only status report
asc-client apps media verify com.example.MyApp
# Retry stuck items using local files
asc-client apps media verify com.example.MyApp --folder media/
Without --folder, sets where all items are complete show a one-liner; sets with stuck items expand to show each file and its state. With --folder, it prompts to retry stuck items by deleting and re-uploading from local files, preserving position order.
Pre-Submission Preflight
Before hitting submit, there’s a mental checklist: Is a build attached? Are all localizations filled in? Are descriptions long enough? Are screenshots uploaded for every locale? Missing any of these means a rejection or a wasted review cycle.
The preflight command runs all of these checks automatically:
asc-client apps review preflight com.example.MyApp
It checks the version state, whether a build is attached, and then goes through every locale to verify localization fields (description, what’s new, keywords), app info fields (name, subtitle, privacy policy URL), and screenshots. The output is grouped by locale with pass/fail indicators:
Preflight checks for MyApp v2.1.0 (Prepare for Submission)
Check Status
──────────────────────────────────────────────────────────────────
Version state ✓ Prepare for Submission
Build attached ✓ Build 42
en-US (English (United States))
App info ✓ All fields filled
Localizations ✓ All fields filled
Screenshots ✓ 2 sets, 10 screenshots
de-DE (German (Germany))
App info ✗ Missing: Privacy Policy URL
Localizations ✗ Missing: What's New
Screenshots ✗ No screenshots
──────────────────────────────────────────────────────────────────
Result: 5 passed, 3 failed
Exits with a non-zero status on failures, making it suitable for CI pipelines and workflow files.
Review Submission
# Submit for review
asc-client apps review submit com.example.MyApp
# Check submission status
asc-client apps review status com.example.MyApp
# After rejection: fix issues, reply in Resolution Center, then
asc-client apps review resolve-issues com.example.MyApp
# Cancel an active review
asc-client apps review cancel-submission com.example.MyApp
Builds
# List builds
asc-client builds list --bundle-id com.example.MyApp
asc-client builds list --bundle-id com.example.MyApp --version 2.1.0
# Archive an Xcode project
asc-client builds archive
asc-client builds archive --scheme MyApp --output ./archives
# Validate before uploading
asc-client builds validate MyApp.ipa
# Upload to App Store Connect
asc-client builds upload MyApp.ipa
# Wait for processing to finish
asc-client builds await-processing com.example.MyApp
The archive command auto-detects the .xcworkspace or .xcodeproj in the current directory. It accepts .ipa, .pkg, or .xcarchive files for upload and validate. When given an .xcarchive, it exports to .ipa automatically. Uploads use Apple’s xcrun altool under the hood — the same tool Xcode uses.
App Configuration
Age ratings
# View current age rating
asc-client apps age-rating com.example.MyApp
# Update from a JSON file
asc-client apps age-rating com.example.MyApp --file age-rating.json
The JSON uses the same field names as the API. Only fields present are updated:
{
"isAdvertising": false,
"violenceCartoonOrFantasy": "INFREQUENT_OR_MILD",
"alcoholTobaccoOrDrugUseOrReferences": "NONE"
}
Territory availability
# View available territories
asc-client apps availability com.example.MyApp
asc-client apps availability com.example.MyApp --verbose
# Add or remove territories
asc-client apps availability com.example.MyApp --add CHN,RUS
asc-client apps availability com.example.MyApp --remove CHN
Encryption declarations
# View existing declarations
asc-client apps encryption com.example.MyApp
# Create a declaration (stops the compliance popup on every submission)
asc-client apps encryption com.example.MyApp --create --description "Uses HTTPS for API communication"
Custom EULA
View the current EULA, set one from a text file, or revert to Apple’s standard EULA:
asc-client apps eula com.example.MyApp
asc-client apps eula com.example.MyApp --file eula.txt
asc-client apps eula com.example.MyApp --delete
Provisioning
asc-client covers the full Apple provisioning stack: devices, signing certificates, bundle identifiers, and provisioning profiles. Every command supports interactive mode — run without arguments for guided prompts, or pass all options explicitly for scripting.
Devices
# List registered devices
asc-client devices list
asc-client devices list --platform IOS --status ENABLED
# Register a new device
asc-client devices register
asc-client devices register --name "My iPhone" --udid 00008101-XXXXXXXXXXXX --platform IOS
# Update name or disable
asc-client devices update "My iPhone" --status DISABLED
Signing certificates
Creating certificates is usually a multi-step process: generate a CSR in Keychain Access, upload it to the Developer portal, download the certificate, import it back. asc-client does all of that in one command:
asc-client certs create --type DISTRIBUTION
This auto-generates an RSA key pair and CSR, sends it to the API, downloads the signed certificate, and imports both the private key and certificate into your login keychain. If you already have a CSR, pass it with --csr my-request.pem.
# List and inspect certificates
asc-client certs list --type DISTRIBUTION
asc-client certs info "Apple Distribution: My Team"
# Revoke a certificate (interactive picker if serial omitted)
asc-client certs revoke
Bundle identifiers
# List and register
asc-client bundle-ids list --platform IOS
asc-client bundle-ids register --name "My App" --identifier com.example.MyApp --platform IOS
# View details including capabilities
asc-client bundle-ids info com.example.MyApp
# Enable or disable capabilities
asc-client bundle-ids enable-capability com.example.MyApp --type PUSH_NOTIFICATIONS
asc-client bundle-ids disable-capability com.example.MyApp
After enabling or disabling a capability, asc-client detects existing provisioning profiles for that bundle ID and offers to regenerate them — a step that’s required for the capability change to take effect but easy to forget.
Provisioning profiles
# Create a profile (fully interactive if options omitted)
asc-client profiles create
asc-client profiles create --name "My Profile" --type IOS_APP_STORE \
--bundle-id com.example.MyApp --certificates all
# Download a profile
asc-client profiles download "My App Store Profile" --output ./profiles/
The --certificates all flag automatically selects every certificate of the matching family: distribution certs for App Store profiles, development certs for development profiles, Developer ID certs for Direct Distribution. This matches how Xcode’s automatic signing works.
Reissuing profiles
Provisioning profiles become invalid when a certificate expires or a capability changes. Instead of manually recreating each one:
# Reissue all invalid profiles
asc-client profiles reissue --all-invalid
# Reissue all profiles regardless of state
asc-client profiles reissue --all
# Use specific certificates instead of auto-detect
asc-client profiles reissue --all --to-certs ABC123,DEF456
# Include all enabled devices (for dev/adhoc profiles)
asc-client profiles reissue --all --all-devices
Each profile is deleted and recreated with the same name, bundle ID, devices, and profile type, but with fresh certificates. A summary table is shown before making changes.
In-App Purchases and Subscriptions
Read-only views for now:
# In-app purchases
asc-client iap list com.example.MyApp
asc-client iap list com.example.MyApp --type consumable --state approved
asc-client iap info com.example.MyApp com.example.MyApp.coins100
asc-client iap promoted com.example.MyApp
# Subscriptions
asc-client sub groups com.example.MyApp
asc-client sub list com.example.MyApp
asc-client sub info com.example.MyApp com.example.MyApp.monthly
Filter values are case-insensitive. Types: CONSUMABLE, NON_CONSUMABLE, NON_RENEWING_SUBSCRIPTION.
Automation
Most commands that prompt for confirmation support --yes / -y to skip prompts, making them suitable for CI/CD pipelines and scripts:
asc-client apps build attach-latest com.example.MyApp --yes
asc-client apps review submit com.example.MyApp --yes
asc-client profiles reissue --all-invalid --yes
When using --yes with provisioning commands, all required arguments must be provided explicitly — interactive mode is disabled.
Combined with workflow files and non-zero exit codes from preflight, you can build fully automated release pipelines that validate before submitting.
Rate limit
asc-client rate-limit
Hourly limit: 3600 requests (rolling window)
Used: 57
Remaining: 3543 (98%)
Terminal Output
All commands use color when connected to a terminal — green for success messages, red for errors, orange for cancellations. Colors are automatically disabled when piping output to files or other programs.
Locale codes are displayed with their language names (e.g. en-US (English (United States)) instead of bare en-US). Enum values and API states are formatted as readable titles (PREPARE_FOR_SUBMISSION becomes “Prepare for Submission”).
Under the Hood
asc-client is built in Swift 6.0 using swift-argument-parser for the CLI framework and Aaron Sky’s asc-swift for type-safe access to the App Store Connect API. Certificate generation uses swift-certificates for X.509 and CSR handling. Binary uploads go through Apple’s xcrun altool.
The result is a single ~64 MB native binary for Apple Silicon. Startup is instant, there’s no runtime to manage.
Developed with Claude Code.
Source & License
asc-client is open source under the MIT license.