Capacitor SDK

Use Case

The Capacitor plugin for Link is a wrapper around the native Android and iOS SDKs for use in Capacitor-based hybrid apps. It is published to npm as @pinwheel/capacitor-sdk and supports Capacitor 6, 7, and 8.

If you are building a pure web app, use the Web SDK or React SDK instead — the Capacitor plugin only runs on iOS and Android.

Installation

Install the plugin and sync it into your native iOS and Android projects:

npm install --save @pinwheel/capacitor-sdk
npx cap sync

You can find the npm package here and the source on GitHub.

Build requirements

Capacitor

  • @capacitor/core 6.0.0 – 8.x.

Android

  • Capacitor 6 / 7: build with JDK 17. Newer JDKs may fail Gradle script analysis.
  • Capacitor 8: build with JDK 21+, since @capacitor/android compiles with Java source release 21.

Configuration

Some platform integrations may require camera access for verification purposes. Ensure the necessary permissions are configured in your native projects:

Android: Add the following permission to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

iOS: Add the following key and description to ios/App/App/Info.plist:

<key>NSCameraUsageDescription</key>
<string>We need access to your camera for verification purposes.</string>

Usage

The Capacitor plugin exposes a single Pinwheel object with open() / close() methods and an event-listener API. Calling Pinwheel.open(...) presents Link as a full-screen native modal.

Pinwheel.open()

Pass the Link token (and any optional flags) to launch Link:

import { Pinwheel } from '@pinwheel/capacitor-sdk';

await Pinwheel.open({
  linkToken: '<LINK_TOKEN>',
  useDarkMode: false,
  useSecureOrigin: true,
});
ParameterTypeDefaultDescription
linkTokenstringrequiredThe Link token created using the /link_tokens endpoint.
useDarkModebooleanfalseRender Link in dark mode. Combine this with whatever mechanism you use to track your app's theme for a consistent UX.
useSecureOriginbooleanfalseLoad Link using the CSP-enabled secure origin.

Pinwheel.close()

Closes the currently presented Pinwheel modal (if any). The modal also dismisses itself automatically on exit.

await Pinwheel.close();

Event listeners

The plugin emits a canonical event stream that includes every Link event with its name and payload. Convenience listeners (success, exit, error, login, loginAttempt) are also provided to mirror the patterns used by the React Native and Flutter SDKs. You can read more about Link events for the full list.

import { Pinwheel } from '@pinwheel/capacitor-sdk';

const eventHandle = await Pinwheel.addListener('event', ({ name, payload }) => {
  console.log('pinwheel event', name, payload);
});

await Pinwheel.addListener('success', (payload) => {
  if (payload.job === 'direct_deposit_switch') {
    console.log('User has successfully updated their direct deposit.');
  }
});

await Pinwheel.addListener('exit', (payload) => {
  // payload is undefined on a clean exit, or a PinwheelError if an error occurred first.
  console.log('exit', payload);
});

await Pinwheel.addListener('error', (error) => console.log('error', error));
await Pinwheel.addListener('login', (payload) => console.log('login', payload));
await Pinwheel.addListener('loginAttempt', (payload) => console.log('loginAttempt', payload));

// Remember to clean up when your view unmounts:
await eventHandle.remove();
// or remove every listener at once:
await Pinwheel.removeAllListeners();

The full list of supported event names and their payload shapes (PinwheelEvent, PinwheelSuccessPayload, PinwheelError, PinwheelLoginPayload, etc.) is exported from the package and visible in src/definitions.ts.

Example Project

The plugin repository ships with three example apps — one for each supported Capacitor major (6, 7, and 8) — that exercise the full SDK against a running backend. See the example app README on GitHub.

Maintenance

Pinwheel regularly releases improvements to this SDK. You should update to the latest version of the Capacitor SDK at least quarterly to ensure the best product performance.

You can check out the full changelog here on GitHub.