
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
apple-api-library
Advanced tools
A TypeScript library for interacting with Apple's App Store Connect API and StoreKit API.
A 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.
Install the package using npm:
npm install apple-api-library
Or using yarn:
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 generationMake sure to install these in your project:
npm install axios dotenv jsonwebtoken
Before using this library, you need:
Apple Developer Account - An active Apple Developer account
App Store Connect API Key - Generate an API key in App Store Connect:
.p8 private key fileStoreKit API Key (optional) - If you plan to use StoreKit features:
.p8 private key fileApp Information:
Create a .env file (or .env.production, .env.development based on your NODE_ENV) in your project root with the following variables:
# Required: 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
# Required: App Store Connect API Key
APP_STORE_CONNECT_KEY_ID=your-connect-key-id
APP_STORE_CONNECT_KEY=/path/to/AuthKey_N262NK6NP4.p8
# Optional: StoreKit API Key (if using StoreKit features)
APP_STORE_KIT_KEY_ID=your-storekit-key-id
APP_STORE_KIT_KEY=/path/to/SubscriptionKey_D5X8SVY366.p8
# Optional: Local Development Flag
APP_IS_LOCAL=true # Set to "true" if running locally (reads key from file path)
| 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) |
For Local Development (APP_IS_LOCAL=true):
APP_STORE_CONNECT_KEY to the file path: /path/to/AuthKey_N262NK6NP4.p8For Production (APP_IS_LOCAL not set or false):
APP_STORE_CONNECT_KEY to the actual key content (the entire contents of the .p8 file)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)
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"
)
Utility class for generating JWT tokens for Apple API authentication.
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 token
Throws: Error - If token generation fails or private key is not set
Token Validity: 20 minutes
Example:
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")
Main library class for interacting with App Store Connect API.
new AppStoreLib()
Throws: Error - If token generation fails
getApps(): Promise<any[]>Fetches all apps associated with your Apple Developer account.
Returns: Promise<any[]> - Array of app objects
Example:
const appStore = new AppStoreLib()
const apps = await appStore.getApps()
apps.forEach((app) => {
console.log(`App: ${app.attributes.name} (ID: ${app.id})`)
})
getAppDetails(appId: string): Promise<any>Fetches detailed information about a specific app.
Parameters:
appId (string): The Apple ID of the appReturns: Promise<any> - App details object
Example:
const appStore = new AppStoreLib()
const appDetails = await appStore.getAppDetails("1234567890")
console.log("App Details:", appDetails)
Library class for managing beta testers and beta groups.
new AppStoreBetaTesterLib()
Throws: Error - If token generation fails
addTesterToGroupByName(groupName: string, email: string, firstName?: string, lastName?: string): Promise<void>Adds 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 groupemail (string): Email address of the testerfirstName (string, optional): First name of the testerlastName (string, optional): Last name of the testerThrows: Error - If the beta group is not found
Example:
const betaLib = new AppStoreBetaTesterLib()
await betaLib.addTesterToGroupByName(
"Public Testing",
"tester@example.com",
"Jane",
"Smith"
)
getBetaGroups(): Promise<any[]>Retrieves all beta groups for your app.
Returns: Promise<any[]> - Array of beta group objects
Example:
const betaLib = new AppStoreBetaTesterLib()
const groups = await betaLib.getBetaGroups()
groups.forEach((group) => {
console.log(`Group: ${group.attributes.name} (ID: ${group.id})`)
})
getBetaGroupIdByName(groupName: string): Promise<string | null>Finds a beta group ID by its name.
Parameters:
groupName (string): Name of the beta groupReturns: Promise<string | null> - The group ID if found, null otherwise
Example:
const betaLib = new AppStoreBetaTesterLib()
const groupId = await betaLib.getBetaGroupIdByName("Public Testing")
if (groupId) {
console.log(`Group ID: ${groupId}`)
}
getBetaTestersInGroup(betaGroupId: string): Promise<any[]>Lists all testers in a specific beta group.
Parameters:
betaGroupId (string): The ID of the beta groupReturns: Promise<any[]> - Array of tester objects
Example:
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): Promise<any>Lists all beta testers (across all groups) with pagination support.
Parameters:
nextUrl (string, optional): URL for the next page of resultsReturns: Promise<any> - Object containing data array and links object with pagination info
Example:
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(): Promise<any[]>Fetches all beta testers across all pages automatically.
Returns: Promise<any[]> - Array of all tester objects
Example:
const betaLib = new AppStoreBetaTesterLib()
const allTesters = await betaLib.getAllBetaTesters()
console.log(`Total testers: ${allTesters.length}`)
addTesterToGroup(betaGroupId: string, email: string, firstName?: string, lastName?: string): Promise<void>Adds 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 groupemail (string): Email address of the testerfirstName (string, optional): First name of the testerlastName (string, optional): Last name of the testerThrows: Error - If tester cannot be created or added to group
Example:
const betaLib = new AppStoreBetaTesterLib()
await betaLib.addTesterToGroup(
"abc123-group-id",
"tester@example.com",
"John",
"Doe"
)
removeTesterFromGroup(betaGroupId: string, testerId: string): Promise<void>Removes a tester from a beta group.
Parameters:
betaGroupId (string): The ID of the beta grouptesterId (string): The ID of the testerExample:
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[]): Promise<void>Updates the "Public Testing" beta group to match a new list of email addresses. This method will:
Parameters:
emails (string[]): Array of email addresses to sync with the groupThrows: Error - If "Public Testing" group is not found
Example:
const betaLib = new AppStoreBetaTesterLib()
const emails = [
"tester1@example.com",
"tester2@example.com",
"tester3@example.com",
]
await betaLib.updatePublicTestingGroup(emails)
getBetaTesterByEmail(email: string): Promise<any | null>Fetches a beta tester by their email address.
Parameters:
email (string): Email address of the testerReturns: Promise<any | null> - Tester object if found, null otherwise
Example:
const betaLib = new AppStoreBetaTesterLib()
const tester = await betaLib.getBetaTesterByEmail("tester@example.com")
if (tester) {
console.log(`Found tester: ${tester.id}`)
}
Utility class for testing API connectivity and sending test notifications.
test(): Promise<void>Tests the Apple JWT token by making a request to the App Store Connect API.
Throws: Error - If the test fails
Example:
import { TestNotification } from "apple-api-library"
try {
await TestNotification.test()
console.log("API connection successful!")
} catch (error) {
console.error("API connection failed:", error)
}
testNotification(): Promise<void>Sends 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 fails
Example:
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)
}
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)
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)
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()
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)
The library throws errors in various scenarios. Always wrap API calls in try-catch blocks:
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)
}
}
}
Token Generation Failure
Beta Group Not Found
getBetaGroups() to list available groupsTester Already Exists (409 Conflict)
getBetaTesterByEmail() firstAuthentication Failure (401)
Possible Causes:
Solutions:
APP_IS_LOCAL=true and the file path is correctPossible Causes:
Solutions:
getBetaGroups() to see available namesAPP_APPLE_ID matches your app in App Store ConnectPossible Causes:
Solutions:
APP_STORE_KIT_KEY_ID and APP_STORE_KIT_KEY are set correctlyPossible Causes:
Solutions:
Contributions are welcome! Please feel free to submit a Pull Request.
ISC
FAQs
A TypeScript library for interacting with Apple's App Store Connect API and StoreKit API.
We found that apple-api-library demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.