@unfoldingcx/appstoreconnect-api
v1.2.5
Published
A powerful TypeScript library for automating Apple App Store Connect submissions with AI-powered release notes generation, intelligent error handling, and automatic retry mechanisms
Maintainers
Readme
"ASCA" stands for "App Store Connect API" and it's a powerful, production-ready TypeScript library for automating Apple App Store Connect submissions to Apple review team. Streamline your iOS, macOS, and tvOS app deployment workflow with intelligent error handling and automatic retry mechanisms.
✨ Features
- ⚡ One shot -
asca submit --build-id latest --version "1.0.0" --ai-release-notes- That's it! - 🎬 GitHub Action - Use as a custom action in your CI/CD workflows
- 🔵 n8n Node - Use as a community node in n8n workflows
- 🎯 Complete Automation - Does review submission workflow from version creation to final submission
- 🤖 AI-Powered Release Notes - Generate release notes from Git commits using OpenAI (25+ languages)
- 🔍 Smart Build Discovery - Use
--build-id latestto automatically use the newest VALID build - 🧠 Intelligent Error Recovery - Automatically handles conflicts and provides actionable feedback
- 🔄 Conflict Resolution - Automatically cancels pending submissions when needed and retries
- 📊 Build Management - Query and list available builds with detailed status information
- 🌍 Multi-Locale Support - Works with any locale configured in your App Store Connect account
- 🖥️ CLI & Library - Use as a library in your code, as a command-line tool, or as a GitHub Action
- 🛡️ Type Safe - Written in TypeScript with full type definitions
- 📝 Detailed Logging - Step-by-step progress tracking with emoji indicators
- 🚨 Clear Error Messages - Human-readable error messages with context and suggestions
📦 Installation
npm install @unfoldingcx/appstoreconnect-apiOr using bun:
bun add @unfoldingcx/appstoreconnect-apiGlobal CLI Installation
Install globally to use the CLI from anywhere:
npm install -g @unfoldingcx/appstoreconnect-apiThen use the asca command:
asca --helpGitHub Action
Use in your GitHub workflows for CI/CD automation:
- uses: unfoldingcx/appstoreconnect-api@v1
with:
issuer-id: ${{ secrets.ASC_ISSUER_ID }}
key-id: ${{ secrets.ASC_KEY_ID }}
private-key: ${{ secrets.ASC_PRIVATE_KEY }}
app-id: ${{ secrets.APP_ID }}
build-id: 'latest'
version: '1.0.0'
ai-release-notes: true
openai-api-key: ${{ secrets.OPENAI_API_KEY }}📚 Full GitHub Action Documentation →
n8n Community Node
Use the App Store Connect node in your N8N workflows:
# Install in n8n via: Settings → Community Nodes → n8n-nodes-appstoreconnect
# Or manually: npm install n8n-nodes-appstoreconnectAvailable operations:
- Submit to Review
- Get Builds
- Cancel Pending Submissions
- Generate AI Release Notes (CLI required for now)
📚 Full n8n Node Documentation →
🖥️ CLI Usage
The package includes a powerful CLI for submitting apps directly from your terminal.
Submit to Review
With manual release notes:
asca submit \
--issuer-id "your-issuer-id" \
--key-id "your-key-id" \
--key-path "./keys/AuthKey.p8" \
--app-id "123456" \
--build-id "abc-def-123" \
--version "1.0.0" \
--platform "IOS" \
--release-notes "Bug fixes and improvements" \
--locale "en-US"With AI-generated release notes: 🤖
# Auto-generate release notes from git commits and submit in one command!
asca submit \
--build-id "abc-def-123" \
--version "1.0.0" \
--ai-release-notes \
--locale "pt-BR"
# With custom date range (last 14 days)
asca submit \
--build-id "abc-def-123" \
--version "1.0.0" \
--ai-release-notes \
--since-days 14 \
--locale "en-US"The --ai-release-notes flag will:
- Fetch your last published App Store build
- Get commits since that build
- Generate localized release notes with OpenAI
- Display preview
- Submit to App Review with generated notes
Use "latest" to auto-select the newest build: ✨
# Don't remember the build ID? Use "latest"!
asca submit \
--build-id latest \
--version "1.0.0" \
--ai-release-notes
# This will:
# 1. Fetch your latest VALID build from TestFlight
# 2. Generate AI release notes
# 3. Submit automaticallyThe latest keyword automatically finds and uses your most recent VALID build, so you don't need to look up build IDs!
⚡ The Ultimate One-Liner
After running asca config once, you can submit to App Review with a single command:
asca submit --build-id latest --version "1.0.0" --ai-release-notesThis one command will:
- ✨ Auto-find your latest VALID build
- 🤖 Generate release notes from git commits with AI
- 🚀 Submit to App Review
From git commits to App Review in ~30 seconds with ZERO manual work!
Using Environment Variables
Set credentials in environment variables for convenience:
export ASC_ISSUER_ID="your-issuer-id"
export ASC_KEY_ID="your-key-id"
export ASC_KEY_PATH="./keys/AuthKey.p8"
export APP_ID="123456"
export OPENAI_API_KEY="sk-..." # For AI release notes
# Now you can use shorter commands
asca submit --build-id "abc" --version "1.0.0" --release-notes "Bug fixes"
# Or with AI-generated notes
asca submit --build-id "abc" --version "1.0.0" --ai-release-notesList Available Builds
asca builds --app-id "123456" --limit 10Cancel Pending Submissions
asca cancel --app-id "123456"Generate AI Release Notes (Preview)
Automatically generate release notes from your git commits using AI:
# Generate notes from commits since last published build
asca release-notes --locale "en-US"
# Short alias
asca rn --locale "pt-BR"
# Generate from last 7 days
asca rn --since-days 7 --locale "es-ES"
# Specify custom git repository path
asca rn --git-path "/path/to/repo" --locale "fr-FR"What it does:
- Fetches your last published build from App Store Connect (READY_FOR_SALE - live in the App Store)
- Gets all git commits since that build was uploaded
- Uses OpenAI to generate localized, user-friendly release notes
- Displays preview with character count (won't submit to App Store)
Important: Uses the last build that's actually live in the App Store, not just the most recent TestFlight build. This ensures release notes only cover changes since the last public release.
Requirements:
- OpenAI API key (configure with
asca configor use--openai-key) - Git repository with commit history
- App Store Connect credentials
Get Help
asca help
asca submit --help
asca builds --help
asca release-notes --help🔑 Setup
1. Create an API Key in App Store Connect
- Go to App Store Connect API Keys
- Click the "+" button to create a new key
- Give it a name and select App Manager or Admin role
- Download the
.p8private key file (you can only download this once!) - Note your Issuer ID (shown at the top of the page)
- Note your Key ID (shown in the key list)
2. Get Your App and Build IDs
- App ID: Found in App Store Connect → Apps → Your App → App Information (numeric ID like
6461211731) - Build ID: You can get this from TestFlight or by using the
getBuilds()function from this library
🎯 Quick Start
import { submitToAppReview } from '@unfoldingcx/appstoreconnect-api'
// Submit your app for review
await submitToAppReview({
issuerId: 'your-issuer-id',
keyId: 'your-key-id',
privateKeyPath: './keys/AuthKey.p8',
appId: 'your-app-id',
buildId: 'build-uuid',
versionString: '1.0.0',
platform: 'IOS',
releaseNotes: 'Bug fixes and performance improvements.',
locale: 'en-US'
})
console.log('✅ Successfully submitted to App Review!')📚 API Reference
submitToAppReview(options)
Submits an app build to Apple App Review with a complete automated workflow.
Parameters:
interface AppStoreConnectOptions {
issuerId: string // Your Issuer ID (UUID format)
keyId: string // Your Key ID (10 characters)
privateKeyPath: string // Path to your .p8 file
appId: string // Your app's unique identifier
buildId: string // Build UUID to submit
versionString?: string // Version string (e.g., "1.0.0")
platform: 'IOS' | 'MACOS' | 'TVOS'
releaseNotes: string // What's New text (max 4000 chars)
locale: string // Locale code (e.g., 'en-US', 'pt-BR')
}What it does:
- ✅ Creates or retrieves the app store version
- 🔗 Associates the build with the version
- 📝 Updates release notes
- 📤 Creates a review submission
- ➕ Adds the version to the submission
- 🎉 Submits to App Review
Returns: Promise<void>
Throws: Error with detailed message indicating which step failed
getBuilds(appId, jwtOptions, limit?)
Retrieves available builds for your app, sorted by upload date (most recent first).
const jwtOptions = {
issuerId: 'your-issuer-id',
keyId: 'your-key-id',
privateKeyPath: './AuthKey.p8'
}
const builds = await getBuilds('your-app-id', jwtOptions, 10)
builds.forEach(build => {
console.log(`${build.attributes.version} - ${build.attributes.processingState}`)
})Returns: Promise<Build[]>
cancelPendingReviewSubmissions(appId, jwtOptions)
Cancels all pending review submissions for an app. Useful when you need to submit a new build but have an existing submission in review.
const canceled = await cancelPendingReviewSubmissions('your-app-id', jwtOptions)
if (canceled) {
console.log('Previous submissions canceled')
}Returns: Promise<boolean> - True if any submissions were canceled
formatBuildInfo(build)
Formats build information into a human-readable string.
const builds = await getBuilds('your-app-id', jwtOptions)
console.log(formatBuildInfo(builds[0]))
// Output:
// • Build ID: 27c6cafd-aeca-4beb-b045-23bfaf72ab2c
// Version: 1.0.0
// Status: VALID
// Uploaded: 11/8/2025, 3:45:00 PMReturns: string
generateAIReleaseNotes(options) 🤖
NEW! Automatically generates release notes from git commits using OpenAI.
import { generateAIReleaseNotes } from '@unfoldingcx/appstoreconnect-api/ai-release-notes'
const result = await generateAIReleaseNotes({
credentials: {
issuerId: 'your-issuer-id',
keyId: 'your-key-id',
privateKeyPath: './AuthKey.p8'
},
appId: 'your-app-id',
locale: 'pt-BR',
openaiApiKey: 'sk-...',
gitRepoPath: './', // optional, defaults to current directory
sinceDays: 7 // optional, or use sinceDate
})
console.log(result.releaseNotes)
console.log(`Based on ${result.commitCount} commits`)What it does:
- Fetches your last published build from App Store Connect (READY_FOR_SALE - live in App Store)
- Gets git commits since that build's upload date
- Uses OpenAI to generate user-friendly, localized release notes
Note: Fetches builds that are actually published to the App Store (not just TestFlight). If no published version exists yet, defaults to last 30 days of commits.
Parameters:
credentials- App Store Connect JWT credentialsappId- Your app's IDlocale- Target language (supports 25+ languages)openaiApiKey- Your OpenAI API keyopenaiOrgId- OpenAI Org ID (optional)gitRepoPath- Path to git repo (default: current directory)sinceDays- Override: use last N days instead of last build datesinceDate- Override: use specific datemaxCommits- Maximum commits to analyze (default: 100)
Returns: Promise<ReleaseNotesResult> with:
releaseNotes- Generated textcommitCount- Number of commits analyzedsinceDate- Date range startlocale- Target localelastBuildVersion- Last build version (if available)
Supported Locales: en-US, pt-BR, es-ES, fr-FR, de-DE, it-IT, ja-JP, ko-KR, zh-CN, zh-TW, and 15+ more
💡 Usage Examples
With Environment Variables
import { submitToAppReview } from '@unfoldingcx/appstoreconnect-api'
await submitToAppReview({
issuerId: process.env.ASC_ISSUER_ID!,
keyId: process.env.ASC_KEY_ID!,
privateKeyPath: process.env.ASC_KEY_PATH!,
appId: process.env.APP_ID!,
buildId: process.env.BUILD_ID!,
versionString: process.env.VERSION!,
platform: 'IOS',
releaseNotes: process.env.RELEASE_NOTES!,
locale: 'en-US'
})Automatic Build Discovery
Find and submit the latest valid build automatically:
import { submitToAppReview, getBuilds } from '@unfoldingcx/appstoreconnect-api'
const jwtOptions = {
issuerId: process.env.ASC_ISSUER_ID!,
keyId: process.env.ASC_KEY_ID!,
privateKeyPath: './keys/AuthKey.p8'
}
// Get the latest valid build
const builds = await getBuilds(process.env.APP_ID!, jwtOptions, 5)
const latestBuild = builds.find(b => b.attributes.processingState === 'VALID')
if (!latestBuild) {
throw new Error('No valid builds found. Please upload a build to TestFlight first.')
}
console.log(`Submitting build ${latestBuild.attributes.version}...`)
await submitToAppReview({
...jwtOptions,
appId: process.env.APP_ID!,
buildId: latestBuild.id,
versionString: latestBuild.attributes.version,
platform: 'IOS',
releaseNotes: 'Bug fixes and performance improvements.',
locale: 'en-US'
})CI/CD Integration (GitHub Actions)
name: Submit to App Review
on:
workflow_dispatch:
inputs:
build_id:
description: 'Build ID from TestFlight'
required: true
version:
description: 'Version string (e.g., 1.0.0)'
required: true
jobs:
submit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install @unfoldingcx/appstoreconnect-api
- name: Submit to App Review
env:
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_PRIVATE_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
APP_ID: ${{ secrets.APP_ID }}
run: |
echo "$ASC_PRIVATE_KEY" > AuthKey.p8
node -e "
const { submitToAppReview } = require('@unfoldingcx/appstoreconnect-api');
submitToAppReview({
issuerId: process.env.ASC_ISSUER_ID,
keyId: process.env.ASC_KEY_ID,
privateKeyPath: './AuthKey.p8',
appId: process.env.APP_ID,
buildId: '${{ github.event.inputs.build_id }}',
versionString: '${{ github.event.inputs.version }}',
platform: 'IOS',
releaseNotes: 'Bug fixes and performance improvements.',
locale: 'en-US'
}).then(() => console.log('Success!')).catch(console.error);
"AI-Powered Release Notes
Automatically generate release notes from git commits:
import { generateAIReleaseNotes } from '@unfoldingcx/appstoreconnect-api/ai-release-notes'
import { submitToAppReview } from '@unfoldingcx/appstoreconnect-api'
// Generate release notes from git commits
const result = await generateAIReleaseNotes({
credentials: {
issuerId: process.env.ASC_ISSUER_ID!,
keyId: process.env.ASC_KEY_ID!,
privateKeyPath: './keys/AuthKey.p8'
},
appId: process.env.APP_ID!,
locale: 'pt-BR',
openaiApiKey: process.env.OPENAI_API_KEY!,
sinceDays: 7 // Last 7 days of commits
})
console.log('Generated notes:', result.releaseNotes)
// Use the generated notes for submission
await submitToAppReview({
issuerId: process.env.ASC_ISSUER_ID!,
keyId: process.env.ASC_KEY_ID!,
privateKeyPath: './keys/AuthKey.p8',
appId: process.env.APP_ID!,
buildId: 'your-build-id',
versionString: '1.0.0',
platform: 'IOS',
releaseNotes: result.releaseNotes, // ← AI-generated notes
locale: result.locale
})Error Handling
import { submitToAppReview } from '@unfoldingcx/appstoreconnect-api'
try {
await submitToAppReview({
// ... your options
})
console.log('✅ Successfully submitted to App Review!')
} catch (error) {
if (error.message.includes('Step 2 failed')) {
console.error('Build association failed. Check the build ID and try again.')
} else if (error.message.includes('Locale')) {
console.error('Invalid locale. Check your App Store Connect localization settings.')
} else {
console.error('Submission failed:', error.message)
}
process.exit(1)
}🛡️ Error Recovery
This library includes intelligent error recovery mechanisms:
Automatic Conflict Resolution
If a build is already waiting for review, the library automatically:
- Detects the conflict (409 INVALID_STATE error)
- Cancels the pending submission
- Retries the new build association
- Continues with the submission
Build Discovery on Failure
If build association fails, the library automatically:
- Fetches your last 5 builds
- Displays them with status and upload date
- Provides guidance on which builds are valid for submission
Locale Validation
If an invalid locale is provided, the error message shows:
- The locale you tried to use
- All available locales for your app
- Clear instructions on how to fix it
📋 Prerequisites
Before using this library, ensure:
- ✅ Your app is set up in App Store Connect
- ✅ At least one locale is configured for your app
- ✅ Your build is uploaded to TestFlight
- ✅ Your build has "VALID" processing state
- ✅ App metadata (description, screenshots, etc.) is complete
- ✅ Your API key has App Manager or Admin role
🔐 Security Best Practices
- Never commit your .p8 file - Add it to
.gitignore - Use environment variables - Store credentials in env vars or secrets managers
- Restrict API key permissions - Use App Manager role instead of Admin when possible
- Rotate keys regularly - Generate new API keys periodically
- Use CI/CD secrets - Store credentials in GitHub Secrets, AWS Secrets Manager, etc.
🐛 Troubleshooting
"No valid builds found"
- Make sure your build is uploaded to TestFlight
- Check that the build processing is complete (not still processing)
- Verify the build hasn't expired (builds expire after 90 days)
"Locale not found"
- Ensure the locale is configured in App Store Connect
- Check the locale code format (e.g., 'en-US', not 'en')
- The error message will show available locales
"API request failed [401]"
- Verify your Issuer ID, Key ID, and private key are correct
- Ensure the .p8 file path is correct
- Check that your API key hasn't been revoked
"API request failed [403]"
- Your API key doesn't have sufficient permissions
- Generate a new key with App Manager or Admin role
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
MIT License - see the LICENSE file for details.
🙏 Acknowledgments
Built with ❤️ for the iOS/macOS developer community.
Special thanks to:
- Apple for providing the App Store Connect API
- The TypeScript and Node.js communities
📞 Support
- 🐛 Bug Reports: GitHub Issues
- 💬 Questions: GitHub Discussions
- 📧 Email: [email protected]
Made with ☕ and TypeScript
