> For the complete documentation index, see [llms.txt](https://nvsbl.gitbook.io/apphafen/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nvsbl.gitbook.io/apphafen/apple.md).

# Apple

This app needs three values for an Apple App Store Connect connection:

* `Issuer ID`
* `Key ID`
* `Private key (.p8 contents)`

## What each value is

### Issuer ID, Key ID, and private key

These come from an App Store Connect API key and are used to sign short-lived JWTs for the App Store Connect API.

{% stepper %}
{% step %}

### Sign in to App Store Connect

Sign in to [App Store Connect](https://appstoreconnect.apple.com/).
{% endstep %}

{% step %}

### Open Users and Access

Open `Users and Access`.
{% endstep %}

{% step %}

### Open Integrations

Open `Integrations`.
{% endstep %}

{% step %}

### Open App Store Connect API

Open the `App Store Connect API` section if needed.
{% endstep %}

{% step %}

### Request API access if needed

If API access is not enabled for the account yet, request access first.
{% endstep %}

{% step %}

### Open Team Keys

Open `Team Keys`.
{% endstep %}

{% step %}

### Generate an API key

Click `Generate API Key`.
{% endstep %}

{% step %}

### Enter key details

Enter a name for the key.
{% endstep %}

{% step %}

### Choose a role

Choose a role with access to the apps you want to track.
{% endstep %}

{% step %}

### Generate the key

Generate the key.
{% endstep %}

{% step %}

### Copy the credentials

Copy the displayed `Issuer ID` and `Key ID`.
{% endstep %}

{% step %}

### Download the private key

Download the private key file and keep the `.p8` contents.
{% endstep %}
{% endstepper %}

Paste the full `.p8` file contents into this app, including:

```
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
```

{% hint style="warning" %}

* The private key can only be downloaded once. Store it securely when Apple shows it.
* The `Issuer ID`, `Key ID`, and private key must all come from the same App Store Connect API key setup.
* The key’s role must have access to the apps you want to import and sync.
* Team keys work across the account according to their assigned role. Individual keys follow the permissions of the user who created them.
  {% endhint %}

## How app discovery works

This app automatically discovers the apps available to the authenticated App Store Connect account.

You do not need to paste app IDs manually.

{% stepper %}
{% step %}

### Load the app

This app loads the app from the App Store Connect API.
{% endstep %}

{% step %}

### Load App Store versions

This app loads available App Store versions.
{% endstep %}

{% step %}

### Import product rows

This app imports one product row per supported platform.
{% endstep %}
{% endstepper %}

Right now, the Apple connector imports:

* iOS apps
* macOS apps

If an app ships on both supported platforms, this app creates one row per platform.

## Required Apple-side prerequisites

* App Store Connect API access must be enabled for the account.
* The API key must still be active and not revoked.
* The selected key role must be able to access the apps you want to track.
* The private key pasted into this app must be the original `.p8` key for that `Key ID`.
* Your server must have a correct system clock because Apple JWT authentication is time-sensitive.

## How Apple authentication works

App Store Connect API requests use a JWT in the `Authorization` header.

This app creates that JWT for you automatically from the stored `Issuer ID`, `Key ID`, and private key.

The JWT:

* uses the `ES256` signing algorithm
* sets `kid` in the header to your `Key ID`
* sets `iss` to your `Issuer ID`
* sets `aud` to `appstoreconnect-v1`
* uses a short expiration window

## Quick validation checklist

If the app shows `Apple authentication failed.` or cannot import your apps, check these in order:

1. Confirm App Store Connect API access is enabled for the account.
2. Confirm the `Issuer ID` is copied exactly from App Store Connect.
3. Confirm the `Key ID` is copied exactly from the same API key.
4. Confirm the pasted private key is the full `.p8` contents, including the `BEGIN` and `END` lines.
5. Confirm the API key has not been revoked.
6. Confirm the key role has access to the apps you expect to import.
7. Confirm your system clock is correct.

## Direct authentication test

You can verify the credentials outside the app by generating a JWT and calling the apps endpoint.

Create a token with this PHP one-liner:

```bash
php -r '
$issuerId = "YOUR_ISSUER_ID";
$keyId = "YOUR_KEY_ID";
$privateKey = <<<KEY
-----BEGIN PRIVATE KEY-----
YOUR_P8_KEY_CONTENTS
-----END PRIVATE KEY-----
KEY;
$header = rtrim(strtr(base64_encode(json_encode([
  "alg" => "ES256",
  "kid" => $keyId,
  "typ" => "JWT",
])), "+/", "-_"), "=");
$now = time();
$payload = rtrim(strtr(base64_encode(json_encode([
  "iss" => $issuerId,
  "aud" => "appstoreconnect-v1",
  "exp" => $now + 1200,
])), "+/", "-_"), "=");
$unsigned = $header . "." . $payload;
openssl_sign($unsigned, $signatureDer, $privateKey, OPENSSL_ALGO_SHA256);
function derToJose(string $der): string {
  $offset = 3;
  $rLength = ord($der[$offset]);
  $offset++;
  $r = substr($der, $offset, $rLength);
  $offset += $rLength + 1;
  $sLength = ord($der[$offset]);
  $offset++;
  $s = substr($der, $offset, $sLength);
  return str_pad(ltrim($r, "\0"), 32, "\0", STR_PAD_LEFT) . str_pad(ltrim($s, "\0"), 32, "\0", STR_PAD_LEFT);
}
$signature = rtrim(strtr(base64_encode(derToJose($signatureDer)), "+/", "-_"), "=");
echo $unsigned . "." . $signature;
'
```

Use the resulting token like this:

```bash
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  "https://api.appstoreconnect.apple.com/v1/apps?limit=5"
```

Interpretation:

* `200`: credentials are valid
* `401`: wrong issuer ID, wrong key ID, wrong private key, expired token, or system clock issue
* `403`: authenticated, but the key does not have access to the requested resources

## Direct app test

If authentication works, verify a specific app directly:

```bash
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  "https://api.appstoreconnect.apple.com/v1/apps/YOUR_APP_ID"
```

You can also inspect version state for a platform:

```bash
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  "https://api.appstoreconnect.apple.com/v1/apps/YOUR_APP_ID/appStoreVersions?filter[platform]=IOS&include=build"
```

Interpretation:

* `200`: the app exists and the key can access it
* `401`: authentication problem
* `403`: the key is authenticated but does not have access to that app or endpoint
* `404`: wrong app ID

## What the app imports from Apple

For each discovered Apple app, the app:

{% stepper %}
{% step %}

### Load the app record

Loads the app record from the App Store Connect API.
{% endstep %}

{% step %}

### Resolve supported platforms

Resolves supported platforms from App Store version data.
{% endstep %}

{% step %}

### Import product rows

Imports one product row per supported platform.
{% endstep %}

{% step %}

### Sync App Store state and version information

Syncs the current App Store state and version information for each imported row.
{% endstep %}
{% endstepper %}

The current Apple status mapping includes states such as:

* `PREPARE_FOR_SUBMISSION`
* `WAITING_FOR_REVIEW`
* `IN_REVIEW`
* `PENDING_DEVELOPER_RELEASE`
* `READY_FOR_SALE`
* `REJECTED`
* `METADATA_REJECTED`
* `INVALID_BINARY`
* `DEVELOPER_REMOVED_FROM_SALE`
* `REMOVED_FROM_SALE`

## Sources

* [App Store Connect API overview](https://developer.apple.com/documentation/appstoreconnectapi)
* [Creating API Keys for App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api)
* [Generating Tokens for API Requests](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests)
* [App Store Connect API get started guide](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://nvsbl.gitbook.io/apphafen/apple.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
