
app-store-connect-cli
Automate iOS/macOS App Store Connect workflows - TestFlight, builds, submissions, screenshots, and metadata with the asc CLI
- Installs
- 243
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/reason-machines/devtools-skills --skill app-store-connect-cli- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads app-store-connect-cli automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
App Store Connect CLI (asc)
Skill by ara.so — Devtools Skills collection.
Fast, scriptable CLI for the App Store Connect API. Automate TestFlight, builds, submissions, signing, analytics, screenshots, subscriptions, and more. JSON-first, no interactive prompts - perfect for CI/CD and automation.
Installation
# Homebrew (macOS/Linux)
brew install asc
# Install script
curl -fsSL https://asccli.sh/install | bash
# Verify installation
asc version
asc --help
For Windows, download signed binaries from the releases page.
Authentication
Generate API Keys
- Visit https://appstoreconnect.apple.com/access/integrations/api
- Create a new API key with appropriate role (Admin for full access)
- Download the
.p8private key file - Note the Key ID and Issuer ID
Configure Authentication
Interactive environments (with keychain):
asc auth login \
--name "MyApp" \
--key-id "$ASC_KEY_ID" \
--issuer-id "$ASC_ISSUER_ID" \
--private-key /path/to/AuthKey.p8 \
--network
CI/CD or headless environments:
asc auth login \
--bypass-keychain \
--name "MyCIKey" \
--key-id "$ASC_KEY_ID" \
--issuer-id "$ASC_ISSUER_ID" \
--private-key /path/to/AuthKey.p8
Repo-local credentials:
asc auth login \
--local \
--bypass-keychain \
--name "LocalKey" \
--key-id "$ASC_KEY_ID" \
--issuer-id "$ASC_ISSUER_ID" \
--private-key /path/to/AuthKey.p8
Validate Authentication
# Check auth status
asc auth status --validate
# Run diagnostics
asc auth doctor
# List active profile
asc auth list
Core Commands
Apps Management
# List all apps
asc apps list --output table
asc apps list --output json --pretty
# Get specific app info
asc apps info view --app "1234567890" --output json
# Search apps
asc apps list --output json | jq '.data[] | select(.attributes.name | contains("MyApp"))'
Builds
# Upload IPA to App Store Connect
asc builds upload --app "1234567890" --ipa /path/to/MyApp.ipa
# List builds
asc builds list --app "1234567890" --output table
asc builds list --app "1234567890" --limit 10 --sort -uploadedDate
# Get next build number
asc builds next-build-number --app "1234567890" --version "1.2.3"
# Get build details
asc builds get --id "BUILD_ID" --output json
TestFlight
# Publish to TestFlight
asc publish testflight \
--app "1234567890" \
--ipa /path/to/MyApp.ipa \
--group "Internal Testers" \
--wait
# List TestFlight groups
asc testflight groups list --app "1234567890" --output table
# Add testers to group
asc testflight testers add \
--group "GROUP_ID" \
--email user@example.com
# List feedback
asc testflight feedback list \
--app "1234567890" \
--paginate \
--output json
# List crashes
asc testflight crashes list \
--app "1234567890" \
--sort -createdDate \
--limit 10
# Get crash logs
asc testflight crashes log --submission-id "SUBMISSION_ID"
App Store Submissions
# Full publish workflow (upload + attach + submit)
asc publish appstore \
--app "1234567890" \
--ipa /path/to/MyApp.ipa \
--version "1.2.3" \
--submit \
--confirm
# Stage release (without submitting)
asc release stage \
--app "1234567890" \
--version "1.2.3" \
--build "BUILD_ID" \
--copy-metadata-from "1.2.2" \
--dry-run
# Validate readiness
asc validate --app "1234567890" --version "1.2.3"
# Check submission status
asc submit status --version-id "VERSION_ID"
# Monitor status with auto-refresh
asc status --app "1234567890" --watch
# Cancel submission
asc submit cancel --version-id "VERSION_ID" --confirm
Versions
# List versions
asc versions list --app "1234567890" --output table
# Create new version
asc versions create \
--app "1234567890" \
--version "1.3.0" \
--platform "IOS"
# Get version details
asc versions get --id "VERSION_ID" --output json
Metadata & Localization
# List localizations
asc localizations list \
--app "1234567890" \
--type app-info \
--output table
# Initialize metadata directory structure
asc metadata init \
--dir ./metadata \
--version "1.2.3" \
--locale "en-US"
# Apply metadata from directory
asc metadata apply \
--app "1234567890" \
--version "1.2.3" \
--dir ./metadata \
--dry-run
# Audit keywords (ASO checks)
asc metadata keywords audit \
--app "1234567890" \
--version "1.2.3" \
--blocked-terms-file ./blocked-terms.txt
# Sync metadata between versions
asc metadata sync \
--app "1234567890" \
--from-version "1.2.2" \
--to-version "1.2.3" \
--locale "en-US"
Screenshots
# Plan screenshot layout
asc screenshots plan \
--app "1234567890" \
--version "1.2.3" \
--review-output-dir ./screenshots/review
# Apply screenshots
asc screenshots apply \
--app "1234567890" \
--version "1.2.3" \
--review-output-dir ./screenshots/review \
--confirm
# Upload screenshots for specific locale
# First, get version localization ID
asc localizations list \
--version "VERSION_ID" \
--output json \
--locale "en-US" | jq '.data[0].id'
# Then upload
asc screenshots upload \
--version-localization "VERSION_LOCALIZATION_ID" \
--path ./screenshots/en-US \
--device-type "IPHONE_65" \
--replace
# List existing screenshots
asc screenshots list \
--version-localization "VERSION_LOCALIZATION_ID" \
--output table
Review Status
# Check review status
asc review status --app "1234567890"
# Run review diagnostics
asc review doctor --app "1234567890"
Signing & Certificates
# List certificates
asc certificates list --output table
# List provisioning profiles
asc profiles list --output table
# List bundle IDs
asc bundle-ids list --output table
# Register new bundle ID
asc bundle-ids create \
--identifier "com.example.myapp" \
--name "My App" \
--platform "IOS"
Xcode Integration
# Archive project
asc xcode archive \
--project MyApp.xcodeproj \
--scheme "MyApp" \
--archive-path ./build/MyApp.xcarchive
# Export IPA
asc xcode export \
--archive-path ./build/MyApp.xcarchive \
--export-options-plist ./ExportOptions.plist \
--export-path ./build/output
Xcode Cloud
# List workflows
asc xcode-cloud workflows list --output table
# Trigger workflow from PR
asc xcode-cloud run \
--workflow-id "WORKFLOW_ID" \
--pull-request-id "123"
# Rerun existing build
asc xcode-cloud run \
--source-run-id "BUILD_RUN_ID" \
--clean
# Get build run details
asc xcode-cloud build-runs get --id "BUILD_RUN_ID" --output json
Workflows
Workflow Configuration
Create .asc/workflow.json for reusable workflows:
{
"workflows": {
"testflight_beta": {
"description": "Build and deploy to TestFlight",
"steps": [
{
"name": "get_next_build",
"command": "builds next-build-number",
"args": {
"app": "1234567890",
"version": "{{VERSION}}"
},
"capture": "BUILD_NUMBER"
},
{
"name": "archive",
"command": "xcode archive",
"args": {
"project": "MyApp.xcodeproj",
"scheme": "MyApp",
"archive-path": "./build/MyApp.xcarchive"
}
},
{
"name": "export",
"command": "xcode export",
"args": {
"archive-path": "./build/MyApp.xcarchive",
"export-options-plist": "./ExportOptions.plist",
"export-path": "./build/output"
}
},
{
"name": "publish",
"command": "publish testflight",
"args": {
"app": "1234567890",
"ipa": "./build/output/MyApp.ipa",
"group": "Internal Testers",
"wait": true
}
}
]
},
"appstore_release": {
"description": "Submit to App Store",
"steps": [
{
"name": "validate",
"command": "validate",
"args": {
"app": "1234567890",
"version": "{{VERSION}}"
}
},
{
"name": "publish",
"command": "publish appstore",
"args": {
"app": "1234567890",
"ipa": "./build/output/MyApp.ipa",
"version": "{{VERSION}}",
"submit": true,
"confirm": true
}
}
]
}
}
}
Run Workflows
# Validate workflow
asc workflow validate
# Dry run
asc workflow run --dry-run testflight_beta VERSION:1.2.3
# Execute workflow
asc workflow run testflight_beta VERSION:1.2.3
# Execute App Store release
asc workflow run appstore_release VERSION:1.2.3
CI/CD Integration
GitHub Actions
name: TestFlight Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Install asc
run: brew install asc
- name: Configure Auth
run: |
echo "${{ secrets.ASC_PRIVATE_KEY }}" > AuthKey.p8
asc auth login \
--bypass-keychain \
--name "CI" \
--key-id "${{ secrets.ASC_KEY_ID }}" \
--issuer-id "${{ secrets.ASC_ISSUER_ID }}" \
--private-key ./AuthKey.p8
- name: Build and Deploy
run: |
asc workflow run testflight_beta VERSION:1.2.3
env:
ASC_BYPASS_KEYCHAIN: "1"
GitLab CI
deploy_testflight:
stage: deploy
tags:
- macos
script:
- brew install asc
- echo "$ASC_PRIVATE_KEY" > AuthKey.p8
- |
asc auth login \
--bypass-keychain \
--name "GitLab CI" \
--key-id "$ASC_KEY_ID" \
--issuer-id "$ASC_ISSUER_ID" \
--private-key ./AuthKey.p8
- asc publish testflight --app "$APP_ID" --ipa ./MyApp.ipa --group "Internal" --wait
variables:
ASC_BYPASS_KEYCHAIN: "1"
only:
- main
CircleCI
version: 2.1
jobs:
deploy:
macos:
xcode: 15.0.0
steps:
- checkout
- run:
name: Install asc
command: brew install asc
- run:
name: Configure Auth
command: |
echo "$ASC_PRIVATE_KEY" > AuthKey.p8
asc auth login \
--bypass-keychain \
--name "CircleCI" \
--key-id "$ASC_KEY_ID" \
--issuer-id "$ASC_ISSUER_ID" \
--private-key ./AuthKey.p8
- run:
name: Deploy
command: asc workflow run testflight_beta VERSION:${CIRCLE_TAG}
environment:
ASC_BYPASS_KEYCHAIN: "1"
Output Formats
The CLI auto-detects output format based on context:
- TTY (interactive):
tableformat - Non-TTY (pipes/CI):
jsonformat
Explicit Format Control
# Table output
asc apps list --output table
# JSON output
asc apps list --output json
# Pretty JSON (human-readable)
asc apps list --output json --pretty
# Markdown output
asc apps list --output markdown
# Set default via environment
export ASC_DEFAULT_OUTPUT=json
Parsing JSON Output
# Extract app IDs
asc apps list --output json | jq -r '.data[].id'
# Filter by name
asc apps list --output json | jq '.data[] | select(.attributes.name == "MyApp")'
# Get build count
asc builds list --app "1234567890" --output json | jq '.data | length'
# Extract version strings
asc versions list --app "1234567890" --output json | jq -r '.data[].attributes.versionString'
Common Patterns
Complete Release Workflow
#!/bin/bash
set -e
APP_ID="1234567890"
VERSION="1.2.3"
IPA_PATH="./build/output/MyApp.ipa"
# 1. Get next build number
BUILD_NUMBER=$(asc builds next-build-number --app "$APP_ID" --version "$VERSION" --output json | jq -r '.buildNumber')
echo "Next build number: $BUILD_NUMBER"
# 2. Build (external Xcode process)
# xcodebuild archive ...
# xcodebuild -exportArchive ...
# 3. Upload and publish to TestFlight
asc publish testflight \
--app "$APP_ID" \
--ipa "$IPA_PATH" \
--group "Internal Testers" \
--wait
# 4. Wait for processing
sleep 60
# 5. Validate for App Store
asc validate --app "$APP_ID" --version "$VERSION"
# 6. Submit to App Store
asc publish appstore \
--app "$APP_ID" \
--version "$VERSION" \
--build "BUILD_ID" \
--submit \
--confirm
# 7. Monitor status
asc status --app "$APP_ID" --watch
Metadata Sync Between Versions
#!/bin/bash
APP_ID="1234567890"
FROM_VERSION="1.2.2"
TO_VERSION="1.2.3"
LOCALES=("en-US" "es-ES" "fr-FR" "de-DE")
for locale in "${LOCALES[@]}"; do
echo "Syncing $locale..."
asc metadata sync \
--app "$APP_ID" \
--from-version "$FROM_VERSION" \
--to-version "$TO_VERSION" \
--locale "$locale"
done
Screenshot Upload Pipeline
#!/bin/bash
APP_ID="1234567890"
VERSION_ID="VERSION_ID"
SCREENSHOTS_DIR="./screenshots"
# Get version localizations
LOCALIZATIONS=$(asc localizations list \
--version "$VERSION_ID" \
--output json | jq -r '.data[] | "\(.attributes.locale)|\(.id)"')
while IFS='|' read -r locale loc_id; do
echo "Uploading screenshots for $locale..."
# Upload for each device type
for device in "IPHONE_65" "IPHONE_67" "IPAD_PRO_129"; do
if [ -d "$SCREENSHOTS_DIR/$locale/$device" ]; then
asc screenshots upload \
--version-localization "$loc_id" \
--path "$SCREENSHOTS_DIR/$locale/$device" \
--device-type "$device" \
--replace
fi
done
done <<< "$LOCALIZATIONS"
Crash Log Collection
#!/bin/bash
APP_ID="1234567890"
OUTPUT_DIR="./crash_logs"
mkdir -p "$OUTPUT_DIR"
# Get recent crashes
CRASHES=$(asc testflight crashes list \
--app "$APP_ID" \
--sort -createdDate \
--limit 20 \
--output json)
# Download logs for each crash
echo "$CRASHES" | jq -r '.data[].id' | while read -r submission_id; do
echo "Downloading crash log: $submission_id"
asc testflight crashes log \
--submission-id "$submission_id" > "$OUTPUT_DIR/$submission_id.log"
done
Automated TestFlight Feedback Reports
#!/bin/bash
APP_ID="1234567890"
REPORT_FILE="testflight_feedback_$(date +%Y%m%d).json"
# Collect all feedback
asc testflight feedback list \
--app "$APP_ID" \
--paginate \
--output json > "$REPORT_FILE"
# Generate summary
echo "Feedback Summary:"
jq '.data | group_by(.attributes.rating) | map({rating: .[0].attributes.rating, count: length})' "$REPORT_FILE"
Environment Variables
# Authentication
export ASC_KEY_ID="ABC123"
export ASC_ISSUER_ID="DEF456"
export ASC_PRIVATE_KEY_PATH="/path/to/AuthKey.p8"
# Bypass keychain (CI/CD)
export ASC_BYPASS_KEYCHAIN="1"
# Default output format
export ASC_DEFAULT_OUTPUT="json"
# API debugging
export ASC_DEBUG="api"
# Config file location
export ASC_CONFIG_PATH="/custom/path/config.json"
Troubleshooting
Authentication Issues
# Check keychain access
asc auth doctor
# Retry with bypass (if keychain blocked)
ASC_BYPASS_KEYCHAIN=1 asc auth status --validate
# Validate active profile
asc auth status --validate
# Re-login with bypass
asc auth login \
--bypass-keychain \
--name "Retry" \
--key-id "$ASC_KEY_ID" \
--issuer-id "$ASC_ISSUER_ID" \
--private-key /path/to/AuthKey.p8
API Debugging
# Enable API debugging
ASC_DEBUG=api asc apps list
# Or use flag
asc --api-debug apps list
# Capture detailed logs
asc --api-debug apps list 2>&1 | tee debug.log
Build Upload Failures
# Verify IPA integrity
unzip -t MyApp.ipa
# Check app ID matches
asc apps list --output json | jq '.data[] | select(.id == "1234567890")'
# Retry with explicit app ID
asc builds upload --app "1234567890" --ipa ./MyApp.ipa --verbose
Version Conflicts
# List existing versions
asc versions list --app "1234567890" --output table
# Check version state
asc versions get --id "VERSION_ID" --output json | jq '.data.attributes.appStoreState'
# Delete draft version (if needed)
asc versions delete --id "VERSION_ID" --confirm
Screenshot Upload Issues
# Verify device type compatibility
asc screenshots plan --app "1234567890" --version "1.2.3" --review-output-dir ./review
# Check localization ID
asc localizations list --version "VERSION_ID" --output json
# Use correct device type codes:
# IPHONE_65, IPHONE_67, IPHONE_61, IPAD_PRO_129, IPAD_PRO_3GEN_129
Best Practices
-
Always validate before submission:
asc validate --app "APP_ID" --version "VERSION" -
Use
--dry-runfor destructive operations:asc metadata apply --dry-run --app "APP_ID" --version "VERSION" --dir ./metadata -
Monitor submissions with
--watch:asc status --app "APP_ID" --watch -
Store credentials securely in CI:
- Use GitHub Secrets, GitLab Variables, etc.
- Never commit
.p8files - Use
--bypass-keychainin CI
-
Parse JSON programmatically:
asc apps list --output json | jq -r '.data[].id' -
Use workflows for complex pipelines:
- Define in
.asc/workflow.json - Version control your workflows
- Test with
--dry-run
- Define in
-
Leverage
--waitfor synchronous operations:asc publish testflight --wait --app "APP_ID" --ipa ./app.ipa
Additional Resources
- Official docs: https://asccli.sh
- GitHub repo: https://github.com/rorkai/App-Store-Connect-CLI
- CI/CD guide: https://github.com/rorkai/App-Store-Connect-CLI/blob/main/docs/CI_CD.md
- Workflows guide: https://github.com/rorkai/App-Store-Connect-CLI/blob/main/docs/WORKFLOWS.md
- Apple API docs: https://developer.apple.com/documentation/appstoreconnectapi
FAQ
What does the app-store-connect-cli skill do?
Automate iOS/macOS App Store Connect workflows - TestFlight, builds, submissions, screenshots, and metadata with the asc CLI
How do I install app-store-connect-cli?
Run npx skills add https://github.com/reason-machines/devtools-skills --skill app-store-connect-cli in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/app-store-connect-cli/ (personal) or .claude/skills/app-store-connect-cli/ (project).
Which AI agents can use app-store-connect-cli?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is app-store-connect-cli safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains app-store-connect-cli?
It is published by reason-machines in reason-machines/devtools-skills. It has 243 recorded installs.
More from reason-machines/devtools-skills
cc-switch-cli
reason-machines/devtools-skills
Cross-platform CLI tool for managing Claude Code, Codex, Gemini, OpenCode & OpenClaw providers, MCP servers, prompts, skills, and proxies.
wx-cli-wechat-local-data
reason-machines/devtools-skills
Open the skill to read its SKILL.md instructions.
chrome-devtools-mcp-automation
reason-machines/devtools-skills
Open the skill to read its SKILL.md instructions.
officecli-office-automation
reason-machines/devtools-skills
Open the skill to read its SKILL.md instructions.
cli-anything-agent-native-software
reason-machines/devtools-skills
Generate and use command-line interfaces that make any software controllable by AI agents with structured JSON output
autocli-web-scraping
reason-machines/devtools-skills
Open the skill to read its SKILL.md instructions.
Popular mobile skills
design-mobile-apps
designed-by-ai/skills
Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.
ios-design
uizze.sh
Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.
vercel-react-native-skills
vercel-labs/agent-skills
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
animate-expo
emilkowalski/skills
Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.
xcode-project-setup
firebase/agent-skills
Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).
firebase-remote-config-basics
firebase/agent-skills
Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.