> ## 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.

# Capacitor quickstart

> Install the Pushctl Capacitor plugin and register an iOS or Android installation.

The plugin supports Capacitor 7 and 8 and exposes the native Pushctl iOS and Android SDKs through one TypeScript API.

<Steps>
  <Step title="Install and synchronize">
    ```bash theme={null}
    npm install @pushctl/capacitor
    npx cap sync
    ```
  </Step>

  <Step title="Complete native setup">
    Configure Firebase for Android. For iOS, enable the Push Notifications capability and forward both APNs registration callbacks from `AppDelegate.swift`. See [native setup](/sdks/capacitor/native-setup).
  </Step>

  <Step title="Initialize">
    ```typescript theme={null}
    import { Pushctl } from '@pushctl/capacitor';

    await Pushctl.initialize({
      applicationKey: 'push_live_your_client_token',
    });
    ```
  </Step>

  <Step title="Request permission">
    ```typescript theme={null}
    const permission = await Pushctl.requestPermission();

    if (!permission.granted) {
      // Explain how to enable notifications in system settings.
    }
    ```
  </Step>

  <Step title="Wait for confirmed registration">
    ```typescript theme={null}
    const subscription = await Pushctl.waitForRegistration();

    if (!subscription.isRegistered) {
      throw new Error('Pushctl is not ready');
    }
    ```

    This resolves only after APNs or Firebase registration is confirmed by the Pushctl API. It rejects if native registration fails, the API rejects registration, or no native token arrives within 15 seconds.
  </Step>

  <Step title="Identify the signed-in user">
    ```typescript theme={null}
    await Pushctl.login({ externalUserId: user.id });
    ```

    `login()` resolves only after the Pushctl API accepts the association. It rejects if the installation is not registered or the request fails.
  </Step>

  <Step title="Handle opens">
    ```typescript theme={null}
    const handle = await Pushctl.addListener('notificationOpened', notification => {
      if (notification.actionUrl) router.push(notification.actionUrl);
    });
    ```

    Call `handle.remove()` when the listener's lifecycle ends.
  </Step>
</Steps>

<Warning>Use a Client token. The application bundle contains values passed from JavaScript to the native plugin.</Warning>

## Migrate from another provider

Keep the existing provider enabled until `waitForRegistration()` resolves and `login()` succeeds. Permission alone does not prove that Pushctl has an active installation.
