Skip to main content

App Store Connect

@dougschaefer/app-store-connectv2026.10.14.1· 12d agoBEHAVIORS
01README

Administer an Apple Developer Program team over the App Store Connect API. Team-key auth with an ES256 JWT signed in WebCrypto (no JWT library, no native dependencies), cached in-process and re-signed near its 20-minute limit, with 429 and 5xx retry honouring Retry-After and a re-sign on a mid-flight 401. Roll the whole team up in one call: members by role, pending invitations, devices by class and status, and every certificate or provisioning profile that has expired or expires within a window, which catches the lapsed development profile that makes a tvOS or iOS app silently refuse to launch. Invite people with roles and the Certificates, Identifiers & Profiles permission, cancel invitations, change a member's roles, and register development devices by UDID. List users, invitations, devices, certificates, profiles, and bundle IDs, fully paged and filterable with a CEL selector, with daysToExpiry computed on anything that expires. Reach any unwrapped endpoint through an authenticated passthrough.

02Release Notes

First release: team rollup with expiring certificate and profile detection, users and invitations, device registration and update, certificates, profiles, bundle IDs, and an authenticated passthrough.

03Behaviors1
@dougschaefer/app-store-connectv2026.10.14.1app-store-connect/app-store-connect.ts
fn testConnection()
Sign a token and read one team member, proving the key, the Issuer ID, and the key's role all work
fn sync(expiringWithinDays?: number)
Roll up the whole team in one call: members by role, pending invitations, devices by class and status, and every certificate or profile that has expired or expires within `expiringWithinDays`. The quickest way to catch a signing asset before it lapses — an expired development profile makes a tvOS or iOS app silently refuse to launch.
ArgumentTypeDescription
expiringWithinDays?numberFlag certificates and profiles expiring within this many days
fn listUsers(limit?: number, maxPages?: number, selector?: string)
List team members with their roles, whether they can see all apps, and provisioningAllowed (the 'Access to Certificates, Identifiers & Profiles' permission)
ArgumentTypeDescription
limit?numberRecords per page. App Store Connect caps these collections at 200
maxPages?numberPage ceiling, so a large team can't run unbounded
selector?stringOptional CEL predicate filtered client-side over each flattened record, e.g. certificateType == "DEVELOPMENT"
fn listUserInvitations(limit?: number, maxPages?: number, selector?: string)
List invitations that have been sent but not yet accepted
ArgumentTypeDescription
limit?numberRecords per page. App Store Connect caps these collections at 200
maxPages?numberPage ceiling, so a large team can't run unbounded
selector?stringOptional CEL predicate filtered client-side over each flattened record, e.g. certificateType == "DEVELOPMENT"
fn inviteUser(email: string, firstName: string, lastName: string, roles?: array, provisioningAllowed?: boolean, allAppsVisible?: boolean)
Invite someone to the team by email with the given roles. provisioningAllowed grants 'Access to Certificates, Identifiers & Profiles', which a DEVELOPER needs before Xcode can create their signing certificate. Apple emails the invitation; the person accepts with an existing Apple ID or creates one. Idempotent: when the email already has a pending invitation or is already a team member, nothing is sent and the existing record is returned with noop set.
ArgumentTypeDescription
emailstring
firstNamestring
lastNamestring
roles?array
provisioningAllowed?boolean
allAppsVisible?boolean
fn cancelUserInvitation(id: string)
Cancel a pending invitation by its id (from listUserInvitations), so the emailed link stops working. An invitation that is already gone (accepted, expired, or cancelled) is reported with noop set rather than failing.
ArgumentTypeDescription
idstring
fn updateUser(id: string, roles?: array, provisioningAllowed?: boolean, allAppsVisible?: boolean)
Change an existing member's roles, provisioningAllowed, or allAppsVisible. Only the fields you pass change. Removing a member is deliberately not wrapped; do that in App Store Connect.
ArgumentTypeDescription
idstringUser id from listUsers
roles?array
provisioningAllowed?boolean
allAppsVisible?boolean
fn listDevices(limit?: number, maxPages?: number, selector?: string)
List registered development devices with UDID, deviceClass (APPLE_TV, IPHONE, IPAD, MAC...), model, and ENABLED/DISABLED status. Apple allows 100 per device class per membership year.
ArgumentTypeDescription
limit?numberRecords per page. App Store Connect caps these collections at 200
maxPages?numberPage ceiling, so a large team can't run unbounded
selector?stringOptional CEL predicate filtered client-side over each flattened record, e.g. certificateType == "DEVELOPMENT"
fn registerDevice(name: string, udid: string, platform?: enum)
Register a development device by UDID so new development profiles include it. Apple TV and iPhone/iPad use platform IOS; Macs use MAC_OS. Registration counts against the yearly per-class limit even if the device is later disabled. Idempotent: a UDID that is already registered is returned with noop set, without spending a slot.
ArgumentTypeDescription
namestring
udidstringHardware UDID, e.g. 00008110-000A1B2C3D4E5F60 for an Apple TV 4K (3rd gen)
platform?enum
fn updateDevice(id: string, name?: string, status?: enum)
Rename a registered device or set its status to ENABLED or DISABLED (by device id from listDevices). A disabled device drops out of new profiles but still counts against the yearly per-class limit; Apple does not allow deleting a registration.
ArgumentTypeDescription
idstringDevice id from listDevices
name?string
status?enum
fn listCertificates(limit?: number, maxPages?: number, selector?: string)
List signing certificates with type, owner display name, serial number, expirationDate, and daysToExpiry
ArgumentTypeDescription
limit?numberRecords per page. App Store Connect caps these collections at 200
maxPages?numberPage ceiling, so a large team can't run unbounded
selector?stringOptional CEL predicate filtered client-side over each flattened record, e.g. certificateType == "DEVELOPMENT"
fn listProfiles(limit?: number, maxPages?: number, selector?: string)
List provisioning profiles with type (e.g. TVOS_APP_DEVELOPMENT), state (ACTIVE/INVALID), UUID, expirationDate, and daysToExpiry. Xcode-managed team profiles (automatic signing) are not returned by the API; this lists only profiles created in the portal or through the API.
ArgumentTypeDescription
limit?numberRecords per page. App Store Connect caps these collections at 200
maxPages?numberPage ceiling, so a large team can't run unbounded
selector?stringOptional CEL predicate filtered client-side over each flattened record, e.g. certificateType == "DEVELOPMENT"
fn listBundleIds(limit?: number, maxPages?: number, selector?: string)
List registered bundle identifiers with platform and seed id
ArgumentTypeDescription
limit?numberRecords per page. App Store Connect caps these collections at 200
maxPages?numberPage ceiling, so a large team can't run unbounded
selector?stringOptional CEL predicate filtered client-side over each flattened record, e.g. certificateType == "DEVELOPMENT"
fn request(method?: enum, path: string, query?: string, body?: string)
Authenticated passthrough to any App Store Connect API path, for endpoints this model does not wrap yet. Handles token signing, retries, and error decoding; you supply the method, path, and optional body.
ArgumentTypeDescription
method?enum
pathstringAPI-relative path beginning with /v1, e.g. /v1/bundleIds/ABC/bundleIdCapabilities
query?stringOptional query string, e.g. limit=200&filter[platform]=IOS
body?stringOptional JSON request body as a string

Resources

team(30d)— Team rollup — members and pending invitations, devices by class and status, and the certificates and profiles that are expired or expiring soon
collection(30d)— A list of one kind of team object (users, userInvitations, devices, certificates, profiles, bundleIds). Records with an expirationDate carry daysToExpiry
change(365d)— The result of a write — an invitation sent or cancelled, a user's roles changed, or a device registered or updated. noop is true when nothing was sent because the target was already in the requested state
response(7d)— Raw response from the generic authenticated passthrough
04Stats
C
73 / 100
Downloads
11
Archive size
22.2 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive0/1missing
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License1/2partial

    no SPDX license declared

  • Verified public repository0/2missing
05Platforms
06Labels