A TypeScript library for interacting with Apple's App Store Connect API and StoreKit API.
npm install apple-api-libraryA TypeScript library for interacting with Apple's App Store Connect API and StoreKit API. This package provides utilities for managing beta testers, fetching app information, generating JWT tokens for authentication, and testing API connectivity.

- Installation
- Prerequisites
- Configuration
- Quick Start
- API Reference
- AppleStoreKitToken
- AppStoreLib
- AppStoreBetaTesterLib
- TestNotification
- Usage Examples
- Environment Variables
- Error Handling
- Troubleshooting
- Contributing
- License
Install the package using npm:
``bash`
npm install apple-api-library
Or using yarn:
`bash`
yarn add apple-api-library
This package requires the following peer dependencies:
- axios (^1.9.0) - For making HTTP requestsdotenv
- (^16.5.0) - For environment variable managementjsonwebtoken
- (^9.0.2) - For JWT token generation
Make sure to install these in your project:
`bash`
npm install axios dotenv jsonwebtoken
Before using this library, you need:
1. Apple Developer Account - An active Apple Developer account
2. App Store Connect API Key - Generate an API key in App Store Connect:
- Go to App Store Connect
- Navigate to Users and Access → Keys → App Store Connect API
- Create a new key and download the .p8 private key file
- Note the Key ID and Issuer ID
3. StoreKit API Key (optional) - If you plan to use StoreKit features:
- Generate a separate API key for StoreKit
- Download the .p8 private key file
- Note the Key ID
4. App Information:
- Your app's Bundle ID
- Your app's Apple ID (found in App Store Connect)
Create a .env file (or .env.production, .env.development based on your NODE_ENV) in your project root with the following variables:
`envRequired: App Store Connect API Configuration
APP_STORE_ISSUER_ID=your-issuer-id
APP_STORE_BUNDLE_ID=com.yourcompany.yourapp
APP_APPLE_ID=your-apple-id
$3
| Variable | Required | Description |
| -------------------------- | -------- | --------------------------------------------------------------- |
|
APP_STORE_ISSUER_ID | Yes | Your Issuer ID from App Store Connect |
| APP_STORE_BUNDLE_ID | Yes | Your app's bundle identifier (e.g., com.company.app) |
| APP_APPLE_ID | Yes | Your app's Apple ID (numeric ID from App Store Connect) |
| APP_STORE_CONNECT_KEY_ID | Yes | The Key ID for your App Store Connect API key |
| APP_STORE_CONNECT_KEY | Yes | Path to your .p8 private key file OR the key content itself |
| APP_STORE_KIT_KEY_ID | No | The Key ID for your StoreKit API key (if using StoreKit) |
| APP_STORE_KIT_KEY | No | Path to your StoreKit .p8 private key file OR the key content |
| APP_IS_LOCAL | No | Set to "true" if running locally (reads key from file path) |$3
For Local Development (
APP_IS_LOCAL=true):- Set
APP_STORE_CONNECT_KEY to the file path: /path/to/AuthKey_N262NK6NP4.p8
- The library will read the key file from diskFor Production (
APP_IS_LOCAL not set or false):- Set
APP_STORE_CONNECT_KEY to the actual key content (the entire contents of the .p8 file)
- This is typically done via environment variables in your hosting platformQuick Start
$3
`typescript
import { AppStoreLib } from "apple-api-library"// Initialize the library
const appStore = new AppStoreLib()
// Fetch all apps
const apps = await appStore.getApps()
console.log("Apps:", apps)
`$3
`typescript
import { AppStoreBetaTesterLib } from "apple-api-library"// Initialize the beta tester library
const betaTesterLib = new AppStoreBetaTesterLib()
// Add a tester to a beta group by name
await betaTesterLib.addTesterToGroupByName(
"Public Testing",
"tester@example.com",
"John",
"Doe"
)
`API Reference
$3
Utility class for generating JWT tokens for Apple API authentication.
#### Methods
#####
token(type: TokenType): stringGenerates a JWT token for Apple API authentication.
Parameters:
-
type ("connect" | "store-kit"): The type of token to generate
- "connect": For App Store Connect API
- "store-kit": For App Store StoreKit v2 APIReturns:
string - The generated JWT tokenThrows:
Error - If token generation fails or private key is not setToken Validity: 20 minutes
Example:
`typescript
import { AppleStoreKitToken } from "apple-api-library"// Generate token for App Store Connect API
const connectToken = AppleStoreKitToken.token("connect")
// Generate token for StoreKit API
const storeKitToken = AppleStoreKitToken.token("store-kit")
`---
$3
Main library class for interacting with App Store Connect API.
#### Constructor
`typescript
new AppStoreLib()
`Throws:
Error - If token generation fails#### Methods
#####
getApps(): PromiseFetches all apps associated with your Apple Developer account.
Returns:
Promise - Array of app objectsExample:
`typescript
const appStore = new AppStoreLib()
const apps = await appStore.getApps()
apps.forEach((app) => {
console.log(App: ${app.attributes.name} (ID: ${app.id}))
})
`#####
getAppDetails(appId: string): PromiseFetches detailed information about a specific app.
Parameters:
-
appId (string): The Apple ID of the appReturns:
Promise - App details objectExample:
`typescript
const appStore = new AppStoreLib()
const appDetails = await appStore.getAppDetails("1234567890")
console.log("App Details:", appDetails)
`---
$3
Library class for managing beta testers and beta groups.
#### Constructor
`typescript
new AppStoreBetaTesterLib()
`Throws:
Error - If token generation fails#### Methods
#####
addTesterToGroupByName(groupName: string, email: string, firstName?: string, lastName?: string): PromiseAdds a tester to a beta group by group name. If the tester doesn't exist, they will be created automatically.
Parameters:
-
groupName (string): Name of the beta group
- email (string): Email address of the tester
- firstName (string, optional): First name of the tester
- lastName (string, optional): Last name of the testerThrows:
Error - If the beta group is not foundExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
await betaLib.addTesterToGroupByName(
"Public Testing",
"tester@example.com",
"Jane",
"Smith"
)
`#####
getBetaGroups(): PromiseRetrieves all beta groups for your app.
Returns:
Promise - Array of beta group objectsExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const groups = await betaLib.getBetaGroups()
groups.forEach((group) => {
console.log(Group: ${group.attributes.name} (ID: ${group.id}))
})
`#####
getBetaGroupIdByName(groupName: string): PromiseFinds a beta group ID by its name.
Parameters:
-
groupName (string): Name of the beta groupReturns:
Promise - The group ID if found, null otherwiseExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const groupId = await betaLib.getBetaGroupIdByName("Public Testing")
if (groupId) {
console.log(Group ID: ${groupId})
}
`#####
getBetaTestersInGroup(betaGroupId: string): PromiseLists all testers in a specific beta group.
Parameters:
-
betaGroupId (string): The ID of the beta groupReturns:
Promise - Array of tester objectsExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const groupId = await betaLib.getBetaGroupIdByName("Public Testing")
if (groupId) {
const testers = await betaLib.getBetaTestersInGroup(groupId)
testers.forEach((tester) => {
console.log(Tester: ${tester.attributes.email})
})
}
`#####
getBetaTesters(nextUrl?: string): PromiseLists all beta testers (across all groups) with pagination support.
Parameters:
-
nextUrl (string, optional): URL for the next page of resultsReturns:
Promise - Object containing data array and links object with pagination infoExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const result = await betaLib.getBetaTesters()
console.log(Total testers: ${result.data.length})
if (result.links.next) {
const nextPage = await betaLib.getBetaTesters(result.links.next)
}
`#####
getAllBetaTesters(): PromiseFetches all beta testers across all pages automatically.
Returns:
Promise - Array of all tester objectsExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const allTesters = await betaLib.getAllBetaTesters()
console.log(Total testers: ${allTesters.length})
`#####
addTesterToGroup(betaGroupId: string, email: string, firstName?: string, lastName?: string): PromiseAdds a tester to a beta group by group ID. If the tester doesn't exist, they will be created automatically.
Parameters:
-
betaGroupId (string): The ID of the beta group
- email (string): Email address of the tester
- firstName (string, optional): First name of the tester
- lastName (string, optional): Last name of the testerThrows:
Error - If tester cannot be created or added to groupExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
await betaLib.addTesterToGroup(
"abc123-group-id",
"tester@example.com",
"John",
"Doe"
)
`#####
removeTesterFromGroup(betaGroupId: string, testerId: string): PromiseRemoves a tester from a beta group.
Parameters:
-
betaGroupId (string): The ID of the beta group
- testerId (string): The ID of the testerExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const groupId = await betaLib.getBetaGroupIdByName("Public Testing")
const testers = await betaLib.getBetaTestersInGroup(groupId)
if (testers.length > 0) {
await betaLib.removeTesterFromGroup(groupId, testers[0].id)
}
`#####
updatePublicTestingGroup(emails: string[]): PromiseUpdates the "Public Testing" beta group to match a new list of email addresses. This method will:
- Add any new email addresses that aren't in the group
- Remove any testers whose emails aren't in the provided list
Parameters:
-
emails (string[]): Array of email addresses to sync with the groupThrows:
Error - If "Public Testing" group is not foundExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const emails = [
"tester1@example.com",
"tester2@example.com",
"tester3@example.com",
]
await betaLib.updatePublicTestingGroup(emails)
`#####
getBetaTesterByEmail(email: string): PromiseFetches a beta tester by their email address.
Parameters:
-
email (string): Email address of the testerReturns:
Promise - Tester object if found, null otherwiseExample:
`typescript
const betaLib = new AppStoreBetaTesterLib()
const tester = await betaLib.getBetaTesterByEmail("tester@example.com")
if (tester) {
console.log(Found tester: ${tester.id})
}
`---
$3
Utility class for testing API connectivity and sending test notifications.
#### Methods
#####
test(): PromiseTests the Apple JWT token by making a request to the App Store Connect API.
Throws:
Error - If the test failsExample:
`typescript
import { TestNotification } from "apple-api-library"try {
await TestNotification.test()
console.log("API connection successful!")
} catch (error) {
console.error("API connection failed:", error)
}
`#####
testNotification(): PromiseSends a test notification to the StoreKit API. Note: This requires at least one active subscription product in App Store Connect.
Throws:
Error - If the test notification failsExample:
`typescript
import { TestNotification } from "apple-api-library"try {
await TestNotification.testNotification()
console.log("Test notification sent successfully!")
} catch (error) {
console.error("Failed to send test notification:", error)
}
`---
Usage Examples
$3
`typescript
import { AppStoreBetaTesterLib } from "apple-api-library"async function manageBetaTesters() {
const betaLib = new AppStoreBetaTesterLib()
// Get all beta groups
const groups = await betaLib.getBetaGroups()
console.log(
"Available beta groups:",
groups.map((g) => g.attributes.name)
)
// Add a new tester to a group
await betaLib.addTesterToGroupByName(
"Public Testing",
"newtester@example.com",
"Alice",
"Johnson"
)
// Get all testers in a group
const groupId = await betaLib.getBetaGroupIdByName("Public Testing")
if (groupId) {
const testers = await betaLib.getBetaTestersInGroup(groupId)
console.log(
Testers in Public Testing: ${testers.length})
} // Update the Public Testing group with a new list
const newTesterList = [
"tester1@example.com",
"tester2@example.com",
"tester3@example.com",
]
await betaLib.updatePublicTestingGroup(newTesterList)
}
manageBetaTesters().catch(console.error)
`$3
`typescript
import { AppStoreLib } from "apple-api-library"async function getAppInfo() {
const appStore = new AppStoreLib()
// Get all apps
const apps = await appStore.getApps()
console.log(
You have ${apps.length} app(s)) // Get details for a specific app
if (apps.length > 0) {
const appDetails = await appStore.getAppDetails(apps[0].id)
console.log("App Details:", JSON.stringify(appDetails, null, 2))
}
}
getAppInfo().catch(console.error)
`$3
`typescript
import { TestNotification } from "apple-api-library"async function testConnection() {
try {
console.log("Testing App Store Connect API connection...")
await TestNotification.test()
console.log("✓ Connection successful!")
} catch (error) {
console.error("✗ Connection failed:", error)
}
}
testConnection()
`$3
`typescript
import { AppStoreBetaTesterLib } from "apple-api-library"async function bulkAddTesters() {
const betaLib = new AppStoreBetaTesterLib()
const groupName = "Public Testing"
const testers = [
{ email: "tester1@example.com", firstName: "John", lastName: "Doe" },
{ email: "tester2@example.com", firstName: "Jane", lastName: "Smith" },
{ email: "tester3@example.com", firstName: "Bob", lastName: "Johnson" },
]
for (const tester of testers) {
try {
await betaLib.addTesterToGroupByName(
groupName,
tester.email,
tester.firstName,
tester.lastName
)
console.log(
✓ Added ${tester.email})
} catch (error) {
console.error(✗ Failed to add ${tester.email}:, error)
}
}
}bulkAddTesters().catch(console.error)
`Error Handling
The library throws errors in various scenarios. Always wrap API calls in try-catch blocks:
`typescript
import { AppStoreBetaTesterLib } from "apple-api-library"async function safeAddTester() {
const betaLib = new AppStoreBetaTesterLib()
try {
await betaLib.addTesterToGroupByName("Public Testing", "tester@example.com")
console.log("Tester added successfully")
} catch (error: any) {
if (error.response) {
// API error response
console.error("API Error:", error.response.status, error.response.data)
} else if (error.message) {
// General error
console.error("Error:", error.message)
} else {
// Unknown error
console.error("Unknown error:", error)
}
}
}
`$3
1. Token Generation Failure
- Cause: Missing or invalid private key
- Solution: Verify environment variables are set correctly
2. Beta Group Not Found
- Cause: Group name doesn't exist or is misspelled
- Solution: Use
getBetaGroups() to list available groups3. Tester Already Exists (409 Conflict)
- Cause: Tester email already registered
- Solution: The library handles this automatically, but you can check with
getBetaTesterByEmail() first4. Authentication Failure (401)
- Cause: Invalid or expired token
- Solution: Tokens are auto-generated and valid for 20 minutes. Check your API key configuration
Troubleshooting
$3
Possible Causes:
- Environment variables not set correctly
- Private key file path incorrect (for local development)
- Private key content invalid (for production)
Solutions:
1. Verify all required environment variables are set
2. For local development, ensure
APP_IS_LOCAL=true and the file path is correct
3. For production, ensure the key content is properly escaped in your environment variable
4. Check that the key file has proper permissions (readable)$3
Possible Causes:
- Group name is misspelled or doesn't exist
- Wrong app Apple ID configured
Solutions:
1. List all groups using
getBetaGroups() to see available names
2. Verify APP_APPLE_ID matches your app in App Store Connect
3. Check group names are case-sensitive$3
Possible Causes:
- No active subscription products configured in App Store Connect
- Wrong StoreKit API key configured
Solutions:
1. Ensure you have at least one active subscription product in App Store Connect
2. Verify StoreKit API key configuration
3. Check that
APP_STORE_KIT_KEY_ID and APP_STORE_KIT_KEY` are set correctlyPossible Causes:
- Network connectivity issues
- Apple API rate limiting
Solutions:
1. Check your internet connection
2. Implement retry logic with exponential backoff
3. Reduce request frequency if hitting rate limits
Contributions are welcome! Please feel free to submit a Pull Request.
ISC
---
- Apple App Store Connect API Documentation
- App Store Connect API Keys Guide
- StoreKit 2 API Documentation