> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pushctl.com/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS FAQ

> Understand APNs environments, device tokens, credentials, registration, and common delivery failures.

## Which APNs environment should I select?

The environment in Pushctl must match the `aps-environment` entitlement in the installed app. Xcode sets this entitlement from the provisioning profile.

| Installed build                               | `aps-environment` | Pushctl environment |
| --------------------------------------------- | ----------------- | ------------------- |
| Development-signed build installed from Xcode | `development`     | Sandbox             |
| TestFlight build                              | `production`      | Production          |
| App Store build                               | `production`      | Production          |

Apple also calls the development environment the sandbox environment. See [APS Environment Entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/aps-environment).

<Warning>
  A device token only works in the environment that issued it. Sending a development token to the production endpoint, or a production token to the sandbox endpoint, commonly returns `400 BadDeviceToken`.
</Warning>

If you need development and production installations at the same time, create separate Pushctl applications for those environments. This prevents changing one application-level APNs setting from breaking the other group.

## What does `BadDeviceToken` mean?

APNs returns `BadDeviceToken` when the token is invalid or does not match the selected environment. It does not indicate a notification payload problem.

Check these items in order:

1. Match the Pushctl environment to the installed app's `aps-environment` entitlement.
2. Confirm the iOS bundle ID in Pushctl matches the app target exactly.
3. Relaunch the app and await `waitForRegistration()` so the current APNs token reaches Pushctl.
4. Send another notification after correcting the configuration.
5. If the failure remains, uninstall and reinstall the app to eliminate a cached-token issue.

Apple documents `BadDeviceToken` as non-retryable until its cause is corrected. See [Handling notification responses from APNs](https://developer.apple.com/documentation/usernotifications/handling-notification-responses-from-apns).

## Does `waitForRegistration()` prove APNs can deliver?

No. It confirms that the native token was received and the Pushctl API accepted the installation. APNs validates the combination of token, environment, and topic when Pushctl sends a notification.

Use `waitForRegistration()` as the client-readiness gate. Use the first successful APNs delivery as end-to-end confirmation during a provider migration.

## Is the APNs `.p8` key tied to an environment?

The device token and APNs endpoint are always environment-specific. APNs authentication keys have their own scope:

* Existing team-scoped keys may work in both environments.
* Newer team-scoped and topic-specific keys can be restricted to sandbox or production.

Check the key's scope in the Apple Developer portal. Invalid, expired, or incorrectly scoped provider authentication normally produces an APNs `403` error rather than `BadDeviceToken`. See [Establishing a token-based connection to APNs](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns).

## How can I inspect the built app's entitlement?

Inspect the signed `.app`, not only the source `.entitlements` file:

```bash theme={null}
codesign -d --entitlements - --xml "/path/to/YourApp.app"
```

Find `aps-environment` in the output and compare it with the environment selected in Pushctl.

## Can Pushctl coexist with another notification provider?

Yes. Providers in the same app receive the APNs token issued for that app installation. Keep the existing provider enabled until Pushctl registration, login, and an end-to-end test delivery succeed.

For Capacitor, continue posting both APNs registration callbacks from `AppDelegate.swift`. The notifications are compatible with Capacitor's Push Notifications plugin and can coexist with another provider during migration.
