# ITMS-91053: fix “Missing API declaration” in Expo

> ITMS-91053: your build uses a required reason API without declaring it. Add its category and code in ios.privacyManifests, then rebuild.

Original page: [ITMS-91053: fix “Missing API declaration” in Expo](https://devoria.fr/en/blog/itms-91053-missing-api-declaration-expo)
Author: [Mehdi Azizi](https://devoria.fr/en/mehdi-azizi)
Published: 2026-10-25
Content last revised: 2026-10-25


ITMS-91053 is an automated Apple check, sent by email after [you upload your build](https://devoria.fr/en/blog/eas-submit-ios-publish-expo-app-store). It says your app's code uses a "required reason" API (user defaults, file timestamps, system boot time, disk space or keyboards) without explaining why in its privacy manifest. **With Expo, you fix it in `ios.privacyManifests`: the category Apple names, the code that describes your actual use, then a new build.**

On Solys, my mini-game alarm clock, these reasons are declared in the app config. Here's its real declaration, the command that lists what your dependencies use, and the check to run on the final file before you submit.

## What does the ITMS-91053 error mean?

The message is titled "ITMS-91053: Missing API declaration". It says the code in a specific file of your build references one or more APIs that require a reason. Then it lists the categories involved, for example `NSPrivacyAccessedAPICategoryUserDefaults`.

These APIs don't ask the user for any permission. But they can be used to identify a device, which Apple calls fingerprinting. That's forbidden, even when the user agrees to tracking. Apple explains it on its page about [required reason APIs](https://developer.apple.com/documentation/bundleresources/describing-use-of-required-reason-api): every app and every SDK has to declare why it uses them.

The same page sets the rule: **since May 1, 2024, App Store Connect no longer accepts an app that doesn't describe these uses in its manifest.** Some developers have reported getting this email as a simple warning, on an accepted build. Don't count on that: treat it as a blocker and fix it before your next upload.

Like other ITMS errors, this isn't a rejection from a reviewer. Nobody opened your app: Apple analyzed the binary.

## The iOS privacy manifest: the PrivacyInfo.xcprivacy file

The privacy manifest is a file named `PrivacyInfo.xcprivacy`, placed at the root of the app bundle. According to [Apple's documentation on privacy manifests](https://developer.apple.com/documentation/bundleresources/privacy-manifest-files), it describes the data collected, tracking, and required reason APIs. Those go in the `NSPrivacyAccessedAPITypes` array: one entry per category, with its type and its list of reasons.

Here's the trap: **a manifest only covers its own bundle.** Apple says it plainly: every executable or dynamic library that uses these APIs must have a manifest in its bundle. Your app's manifest doesn't cover an embedded framework, and an SDK can't rely on your app's manifest.

Don't confuse it with the Info.plist strings required by [ITMS-90683](https://devoria.fr/en/blog/itms-90683-missing-purpose-string-expo). Those are shown to the user in a permission prompt. The manifest is only read by Apple.

## The 5 API categories and their reason codes

A category's full name starts with `NSPrivacyAccessedAPICategory`. The list of APIs and reasons is on the page for the [NSPrivacyAccessedAPIType](https://developer.apple.com/documentation/bundleresources/app-privacy-configuration/nsprivacyaccessedapitypes/nsprivacyaccessedapitype) key, read on October 15, 2026.

| Category        | Typical API                  | Common reason |
| --------------- | ---------------------------- | ------------- |
| UserDefaults    | `UserDefaults`               | CA92.1        |
| FileTimestamp   | `creationDate`               | C617.1        |
| SystemBootTime  | `systemUptime`               | 35F9.1        |
| DiskSpace       | `volumeAvailableCapacityKey` | E174.1        |
| ActiveKeyboards | `activeInputModes`           | 54BD.1        |

The codes you'll run into most:

- **CA92.1**: the app reads and writes its own preferences, inaccessible to other apps.
- **1C8F.1**: preferences shared with a widget or extension in the same App Group.
- **C617.1**: timestamps or size of files stored in the app's container.
- **3B52.1**: files the user picked, for example in a document picker.
- **35F9.1**: measuring the time elapsed between two app events, or running a timer.
- **E174.1**: checking free space before writing files, with a visible effect for the user.
- **85F4.1**: displaying disk space to the user.

**Declare the reason that matches your actual use, not the one that looks the broadest.** Apple specifies that these APIs and their data are only used for the declared reasons, and never for tracking. Two codes are reserved for third-party SDKs: C56D.1 and 0A2A.1. They don't belong in your app's manifest.

## Declaring the privacy manifest in Expo

With Expo, you don't write the file yourself. You describe it under `expo.ios.privacyManifests`, in app.json or app.config, and prebuild generates `PrivacyInfo.xcprivacy`. That's the method in [Expo's documentation on privacy manifests](https://docs.expo.dev/guides/apple-privacy/).

Here's the real excerpt from Solys's config. The key also contains tracking and collected data, removed here:

```json
{
  "expo": {
    "ios": {
      "privacyManifests": {
        "NSPrivacyAccessedAPITypes": [
          {
            "NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryUserDefaults",
            "NSPrivacyAccessedAPITypeReasons": ["CA92.1", "1C8F.1"]
          },
          {
            "NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategoryFileTimestamp",
            "NSPrivacyAccessedAPITypeReasons": ["C617.1"]
          },
          {
            "NSPrivacyAccessedAPIType": "NSPrivacyAccessedAPICategorySystemBootTime",
            "NSPrivacyAccessedAPITypeReasons": ["35F9.1"]
          }
        ]
      }
    }
  }
}
```

CA92.1, C617.1 and 35F9.1 mirror what React Native and the app's Expo modules declare. 1C8F.1 comes from Solys itself: the app shares settings with its widget through an App Group.

A trap to know about: if your `ios` folder is generated and ignored by git, **a `PrivacyInfo.xcprivacy` written by hand in that folder never makes it into the build.** EAS reruns prebuild and regenerates the folder. I saw it happen on one of my projects on September 9, 2026: the hand-written manifest was gone after a prebuild. The declaration has to live in the config.

One last point: the manifest is part of the binary. An over-the-air update doesn't change it. You have to rebuild and upload again.

## Third-party SDKs that ship their own manifest

Most recent libraries ship their manifest. Expo packages that use these APIs have a `PrivacyInfo.xcprivacy` in their `ios` folder, and so does React Native. The problem comes from how they're linked into your app.

Expo's documentation warns: Apple doesn't correctly read every manifest from statically linked CocoaPods dependencies, which is the case for Expo packages. So you may need to copy their reasons into your app's config.

To cut down that work, the `ios.privacyManifestAggregationEnabled` setting in [expo-build-properties](https://docs.expo.dev/versions/latest/sdk/build-properties/) merges the CocoaPods dependencies' manifests into a single file. In the Podfile Expo generates on my projects (SDK 57), this merging is on unless you turn it off.

The merging has a limit: it only sees dependencies that ship a manifest. An old library without one stays invisible. **So read the file path named in the email**: it tells you where to fix.

| File named            | Where to fix             |
| --------------------- | ------------------------ |
| The app executable    | `ios.privacyManifests`   |
| A `.framework`        | Update the SDK           |
| An `.appex` extension | The extension's manifest |

## Check before you submit

Expo suggests waiting for Apple's email after an upload. That works, but every round trip costs a build. Here are the four checks I run first.

**1. List what your dependencies declare.** From the project root:

```bash
find node_modules -name PrivacyInfo.xcprivacy -not -path '*/example/*' \
  -exec grep -ho "NSPrivacyAccessedAPICategory[A-Za-z]*" {} + | sort | uniq -c
```

On Solys, on October 15, 2026, the command finds 13 manifests and four categories: file timestamps, user defaults, system boot time and disk space. Every listed category must show up in the app's final manifest, or in the manifest of the framework that uses it.

**2. Search your own native code.** Custom modules, plugins, targets like a widget: no SDK declares that code for you.

```bash
grep -rlE "UserDefaults|systemUptime|mach_absolute_time|creationDate|modificationDate|volumeAvailableCapacity|activeInputModes" \
  --include='*.swift' --include='*.m' --include='*.mm' modules plugins targets
```

Adjust the folders to your project. On Solys, this search is what surfaces the preferences shared through the App Group, hence the 1C8F.1 code.

**3. Read the generated manifest.** Run `npx expo prebuild -p ios`, then open `ios/<YourAppName>/PrivacyInfo.xcprivacy`. Careful: this command can delete and recreate the `ios` folder. If you've made manual changes there, work on a copy of the project.

**4. Check the final build.** It's the only proof that counts. Download the IPA from the build page on expo.dev, then run on your Mac:

```bash
unzip -q MonApp.ipa -d ipa
find ipa/Payload -name PrivacyInfo.xcprivacy
plutil -p ipa/Payload/*.app/PrivacyInfo.xcprivacy
```

The second line lists every embedded manifest, the app's and the frameworks'. The third prints the app's manifest, to compare with the list from step 1.

## What the manifest has to say alongside your listing

The manifest doesn't live alone. If it declares tracking or collected data, your App Store listing and your privacy policy have to say the same thing. A mismatch between the three can later cost you a human rejection under [guideline 5.1.1](https://devoria.fr/en/blog/guideline-5-1-1-app-store-rejection). What to align is detailed in [privacy policy for an app](https://devoria.fr/en/blog/app-privacy-policy-ios-apple-requirements).

## Submit your build without nasty surprises

ITMS errors are quick to fix once you know how to read them. The rejections that come after need more perspective: the process is on the [app rejected by Apple](https://devoria.fr/en/expertise/app-rejected-by-apple) page, and the most common reasons are in [why Apple rejects an app](https://devoria.fr/en/blog/apple-app-rejection-real-cases).

To check your submission before you send it, the [Apple Review Kit](https://devoria.fr/en/kits/review-apple) bundles my checklist and Claude Code skills that review your project. If your upload is stuck and you want a human eye on it, [book an hour with me](https://devoria.fr/en/coaching): we go through your project over video call, on your screen. And if you're preparing your first launch, [take the free diagnostic](https://devoria.fr/en/diagnostic): it puts the steps in order and sends you my Apple anti-rejection checklist.

## Frequently asked questions

### Does ITMS-91053 block my build upload?

Apple says that since May 1, 2024, App Store Connect no longer accepts apps that don't describe their use of these APIs. Yet some developers have received the email on an accepted build, as a warning. Either way, fix the manifest before your next build.

### Which reason code should I pick for UserDefaults?

CA92.1 if the app only reads and writes its own preferences. Add 1C8F.1 if it shares them with a widget or extension through an App Group. C56D.1 is reserved for third-party SDKs: don't put it in your app's manifest.

### Do I need to copy every SDK's manifest into app.json?

Not necessarily. If manifest merging is on, the CocoaPods dependencies' manifests are combined when pods are installed. Only copy a category if it's missing from the final manifest, with the reason that describes the actual use.

### Does my widget or extension need its own manifest?

According to Apple, every bundle containing an executable that uses these APIs must have its own manifest. An extension is a separate executable, in its own bundle: the app's manifest doesn't cover it.

### Can I fix ITMS-91053 without a new build?

No. The privacy manifest is part of the binary. Fix the config, rebuild, then test it in [TestFlight](https://devoria.fr/en/blog/testflight-test-iphone-app-before-submitting) before sending it to review.


## Your project

[Apple Review Kit](https://devoria.fr/en/kits/review-apple): The App Store review checklist I use, my Apple rejections and fixes, notes for the reviewer and two Claude Code skills that review your app. 29 €.

[iOS Kit](https://devoria.fr/en/kits/ios): Build your iPhone app with Claude Code: the starter code behind my apps, skills that check its work, and the Apple Review, Screenshots and ASO kits. 129,99 €.
