;
}
```
### `PlayOnceResult`
[Section titled âPlayOnceResultâ](#playonceresult)
```typescript
export interface PlayOnceResult {
/**
* The internally generated asset ID for this playback
* Can be used to control playback (pause, stop, etc.) before completion
*/
assetId: string;
}
```
### `AssetPlayOptions`
[Section titled âAssetPlayOptionsâ](#assetplayoptions)
```typescript
export interface AssetPlayOptions {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Time to start playing the audio, in seconds
*/
time?: number;
/**
* Delay to start playing the audio, in seconds
*/
delay?: number;
/**
* Volume of the audio, between 0.1 and 1.0
*/
volume?: number;
/**
* Whether to fade in the audio
*/
fadeIn?: boolean;
/**
* Whether to fade out the audio
*/
fadeOut?: boolean;
/**
* Fade in duration in seconds.
* Only used if fadeIn is true.
* Default is 1s.
*/
fadeInDuration?: number;
/**
* Fade out duration in seconds.
* Only used if fadeOut is true.
* Default is 1s.
*/
fadeOutDuration?: number;
/**
* Time in seconds from the start of the audio to start fading out.
* Only used if fadeOut is true.
* Default is fadeOutDuration before end of audio.
*/
fadeOutStartTime?: number;
}
```
### `AssetPauseOptions`
[Section titled âAssetPauseOptionsâ](#assetpauseoptions)
```typescript
export interface AssetPauseOptions {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Whether to fade out the audio before pausing
*/
fadeOut?: boolean;
/**
* Fade out duration in seconds.
* Default is 1s.
*/
fadeOutDuration?: number;
}
```
### `AssetResumeOptions`
[Section titled âAssetResumeOptionsâ](#assetresumeoptions)
```typescript
export interface AssetResumeOptions {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Whether to fade in the audio during resume
*/
fadeIn?: boolean;
/**
* Fade in duration in seconds.
* Default is 1s.
*/
fadeInDuration?: number;
}
```
### `Assets`
[Section titled âAssetsâ](#assets)
```typescript
export interface Assets {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
}
```
### `AssetStopOptions`
[Section titled âAssetStopOptionsâ](#assetstopoptions)
```typescript
export interface AssetStopOptions {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Whether to fade out the audio before stopping
*/
fadeOut?: boolean;
/**
* Fade out duration in seconds.
* Default is 1s.
*/
fadeOutDuration?: number;
}
```
### `AssetVolume`
[Section titled âAssetVolumeâ](#assetvolume)
```typescript
export interface AssetVolume {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Volume of the audio, between 0.1 and 1.0
*/
volume: number;
/**
* Time over which to fade to the target volume, in seconds. Default is 0s (immediate).
*/
duration?: number;
}
```
### `AssetRate`
[Section titled âAssetRateâ](#assetrate)
```typescript
export interface AssetRate {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Rate of the audio, between 0.1 and 1.0
*/
rate: number;
}
```
### `AssetSetTime`
[Section titled âAssetSetTimeâ](#assetsettime)
```typescript
export interface AssetSetTime {
/**
* Asset Id, unique identifier of the file
*/
assetId: string;
/**
* Time to set the audio, in seconds
*/
time: number;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-native-audio](/plugins/capacitor-native-audio/) for the native capability in Using @capgo/capacitor-native-audio, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# @capgo/capacitor-native-biometric
> Secure credential storage and biometric-gated access for Android and iOS.
## Overview
[Section titled âOverviewâ](#overview)
Native Biometric is a secure credential vault for iOS and Android. It stores credentials in the native Keychain or Keystore and can require biometric authentication before sensitive values are returned. It is a direct replacement for Ionic Identity Vault credential-vault flows; your app keeps control of timeout, lifecycle, privacy-screen, and custom-passcode policies.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `setCredentials` and `getCredentials` - Securely store and retrieve credentials for a given server.
* `getSecureCredentials` - Read credentials only after a native biometric check.
* `isCredentialsSaved` and `deleteCredentials` - Build onboarding, locked-state, and logout flows.
* `isAvailable` and `verifyIdentity` - Check authentication availability and prompt the user when needed.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isAvailable` | Checks if biometric authentication hardware is available. |
| `addListener` | Adds a listener that is called when the app resumes from background. This is useful to detect if biometry availability has changed while the app was in the background (e.g., user enrolled/unenrolled biometrics). |
| `verifyIdentity` | Prompts the user to authenticate with biometrics. |
| `getCredentials` | Gets the stored credentials for a given server. |
| `setCredentials` | Stores the given credentials for a given server. |
| `deleteCredentials` | Deletes the stored credentials for a given server. |
| `getSecureCredentials` | Gets the stored credentials for a given server, requiring biometric authentication. Credentials must have been stored with accessControl set to BIOMETRY\_CURRENT\_SET or BIOMETRY\_ANY. |
| `isCredentialsSaved` | Checks if credentials are already saved for a given server. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Reference Values
[Section titled âReference Valuesâ](#reference-values)
Use these values when handling `biometricError` results from `authenticate`.
### Biometric Auth Errors
[Section titled âBiometric Auth Errorsâ](#biometric-auth-errors)
| Value | Code | Platform | Meaning |
| ------------------------- | ---- | ------------ | ---------------------------------------------------------------------- |
| `UNKNOWN_ERROR` | `0` | Android, iOS | Unknown error occurred. |
| `BIOMETRICS_UNAVAILABLE` | `1` | Android, iOS | Biometrics are unavailable because hardware is missing or unavailable. |
| `USER_LOCKOUT` | `2` | Android, iOS | User is locked out after too many failed attempts. |
| `BIOMETRICS_NOT_ENROLLED` | `3` | Android, iOS | No biometrics are enrolled on the device. |
| `USER_TEMPORARY_LOCKOUT` | `4` | Android | User is temporarily locked out, typically for 30 seconds. |
| `AUTHENTICATION_FAILED` | `10` | Android, iOS | Authentication failed. |
| `APP_CANCEL` | `11` | iOS | App canceled the authentication flow. |
| `INVALID_CONTEXT` | `12` | iOS | Authentication context is invalid. |
| `NOT_INTERACTIVE` | `13` | iOS | Authentication was not interactive. |
| `PASSCODE_NOT_SET` | `14` | Android, iOS | Device passcode, PIN, or fallback credential is not set. |
| `SYSTEM_CANCEL` | `15` | Android, iOS | System canceled authentication, for example after screen lock. |
| `USER_CANCEL` | `16` | Android, iOS | User canceled authentication. |
| `USER_FALLBACK` | `17` | Android, iOS | User chose fallback authentication. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-native-biometric](https://github.com/Cap-go/capacitor-native-biometric/).
## Keep going from @capgo/capacitor-native-biometric
[Section titled âKeep going from @capgo/capacitor-native-biometricâ](#keep-going-from-capgocapacitor-native-biometric)
If you are using **@capgo/capacitor-native-biometric** to plan authentication and account flows, connect it with [Using @capgo/capacitor-native-biometric](/plugins/capacitor-native-biometric/) for the native capability in Using @capgo/capacitor-native-biometric, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication, and [SSO (Enterprise)](/docs/webapp/enterprise-sso/) for the implementation detail in SSO (Enterprise).
# Getting Started
> Install @capgo/capacitor-native-biometric and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-native-biometric` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-native-biometric
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `isAvailable`
[Section titled âisAvailableâ](#isavailable)
Checks if biometric authentication hardware is available.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.isAvailable();
```
### `verifyIdentity`
[Section titled âverifyIdentityâ](#verifyidentity)
Prompts the user to authenticate with biometrics.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.verifyIdentity();
```
### `getCredentials`
[Section titled âgetCredentialsâ](#getcredentials)
Gets the stored credentials for a given server.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.getCredentials({} as GetCredentialOptions);
```
### `setCredentials`
[Section titled âsetCredentialsâ](#setcredentials)
Stores the given credentials for a given server.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.setCredentials({} as SetCredentialOptions);
```
### `deleteCredentials`
[Section titled âdeleteCredentialsâ](#deletecredentials)
Deletes the stored credentials for a given server.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.deleteCredentials({} as DeleteCredentialOptions);
```
### `getSecureCredentials`
[Section titled âgetSecureCredentialsâ](#getsecurecredentials)
Gets the stored credentials for a given server, requiring biometric authentication. Credentials must have been stored with accessControl set to BIOMETRY\_CURRENT\_SET or BIOMETRY\_ANY.
On iOS, the system automatically shows the biometric prompt when accessing the protected Keychain item. On Android, BiometricPrompt is shown with a CryptoObject bound to the credential decryption key.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.getSecureCredentials({} as GetSecureCredentialsOptions);
```
### `isCredentialsSaved`
[Section titled âisCredentialsSavedâ](#iscredentialssaved)
Checks if credentials are already saved for a given server.
```typescript
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.isCredentialsSaved({} as IsCredentialsSavedOptions);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `IsAvailableOptions`
[Section titled âIsAvailableOptionsâ](#isavailableoptions)
```typescript
export interface IsAvailableOptions {
/**
* Only for iOS.
* Specifies if should fallback to passcode authentication if biometric authentication is not available.
* On Android, this parameter is ignored due to BiometricPrompt API constraints:
* DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.
*/
useFallback: boolean;
}
```
### `AvailableResult`
[Section titled âAvailableResultâ](#availableresult)
Result from isAvailable() method indicating biometric authentication availability.
```typescript
export interface AvailableResult {
/**
* Whether authentication is available (biometric or fallback if useFallback is true)
*/
isAvailable: boolean;
/**
* The strength of available authentication method (STRONG, WEAK, or NONE)
*/
authenticationStrength: AuthenticationStrength;
/**
* The primary biometry type available on the device.
* On Android devices with multiple biometry types, this returns MULTIPLE.
* Use this for display purposes only - always use isAvailable for logic decisions.
*/
biometryType: BiometryType;
/**
* Whether the device has a secure lock screen (PIN, pattern, or password).
* This is independent of biometric enrollment.
*/
deviceIsSecure: boolean;
/**
* Whether strong biometry (Face ID, Touch ID, or fingerprint on devices that consider it strong)
* is specifically available, separate from weak biometry or device credentials.
*/
strongBiometryIsAvailable: boolean;
/**
* Error code from BiometricAuthError enum. Only present when isAvailable is false.
* Indicates why biometric authentication is not available.
* @see BiometricAuthError
*/
errorCode?: BiometricAuthError;
}
```
### `BiometryChangeListener`
[Section titled âBiometryChangeListenerâ](#biometrychangelistener)
Callback type for biometry change listener.
```typescript
export type BiometryChangeListener = (result: AvailableResult) => void;
```
### `BiometricOptions`
[Section titled âBiometricOptionsâ](#biometricoptions)
```typescript
export interface BiometricOptions {
reason?: string;
title?: string;
subtitle?: string;
description?: string;
negativeButtonText?: string;
/**
* Only for iOS.
* Specifies if should fallback to passcode authentication if biometric authentication fails.
* On Android, this parameter is ignored due to BiometricPrompt API constraints:
* DEVICE_CREDENTIAL authenticator and negative button (cancel) are mutually exclusive.
*/
useFallback?: boolean;
/**
* Only for iOS.
* Set the text for the fallback button in the authentication dialog.
* If this property is not specified, the default text is set by the system.
*/
fallbackTitle?: string;
/**
* Only for Android.
* Set a maximum number of attempts for biometric authentication. The maximum allowed by android is 5.
* @default 1
*/
maxAttempts?: number;
/**
* Only for Android.
* Specify which biometry types are allowed for authentication.
* If not specified, all available types will be allowed.
* @example [BiometryType.FINGERPRINT, BiometryType.FACE_AUTHENTICATION]
*/
allowedBiometryTypes?: BiometryType[];
}
```
### `GetCredentialOptions`
[Section titled âGetCredentialOptionsâ](#getcredentialoptions)
```typescript
export interface GetCredentialOptions {
server: string;
}
```
### `Credentials`
[Section titled âCredentialsâ](#credentials)
```typescript
export interface Credentials {
username: string;
password: string;
}
```
### `SetCredentialOptions`
[Section titled âSetCredentialOptionsâ](#setcredentialoptions)
```typescript
export interface SetCredentialOptions {
username: string;
password: string;
server: string;
/**
* Access control level for the stored credentials.
* When set to BIOMETRY_CURRENT_SET or BIOMETRY_ANY, the credentials are
* hardware-protected and require biometric authentication to access.
*
* On iOS, this adds SecAccessControl to the Keychain item.
* On Android, this creates a biometric-protected Keystore key and requires
* BiometricPrompt authentication for both storing and retrieving credentials.
*
* @default AccessControl.NONE
* @since 8.4.0
*/
accessControl?: AccessControl;
}
```
### `DeleteCredentialOptions`
[Section titled âDeleteCredentialOptionsâ](#deletecredentialoptions)
```typescript
export interface DeleteCredentialOptions {
server: string;
}
```
### `GetSecureCredentialsOptions`
[Section titled âGetSecureCredentialsOptionsâ](#getsecurecredentialsoptions)
```typescript
export interface GetSecureCredentialsOptions {
server: string;
/**
* Reason for requesting biometric authentication.
* Displayed in the biometric prompt on both iOS and Android.
*/
reason?: string;
/**
* Title for the biometric prompt.
* Only for Android.
*/
title?: string;
/**
* Subtitle for the biometric prompt.
* Only for Android.
*/
subtitle?: string;
/**
* Description for the biometric prompt.
* Only for Android.
*/
description?: string;
/**
* Text for the negative/cancel button.
* Only for Android.
*/
negativeButtonText?: string;
}
```
### `IsCredentialsSavedOptions`
[Section titled âIsCredentialsSavedOptionsâ](#iscredentialssavedoptions)
```typescript
export interface IsCredentialsSavedOptions {
server: string;
}
```
### `IsCredentialsSavedResult`
[Section titled âIsCredentialsSavedResultâ](#iscredentialssavedresult)
```typescript
export interface IsCredentialsSavedResult {
isSaved: boolean;
}
```
### `AuthenticationStrength`
[Section titled âAuthenticationStrengthâ](#authenticationstrength)
```typescript
export enum AuthenticationStrength {
/**
* No authentication available, even if PIN is available but useFallback = false
*/
NONE = 0,
/**
* Strong authentication: Face ID on iOS, fingerprints on devices that consider fingerprints strong (Android).
* Note: PIN/pattern/password is NEVER considered STRONG, even when useFallback = true.
*/
STRONG = 1,
/**
* Weak authentication: Face authentication on Android devices that consider face weak,
* or PIN/pattern/password if useFallback = true (PIN is always WEAK, never STRONG).
*/
WEAK = 2,
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan authentication and account flows, connect it with [Using @capgo/capacitor-native-biometric](/plugins/capacitor-native-biometric/) for the native capability in Using @capgo/capacitor-native-biometric, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# @capgo/capacitor-native-loader
> Render polished loaders above, below, around, or instead of the WebView using native iOS and Android views.
Native render path
Show loaders as UIKit and Android overlay views instead of asking the WebView to animate expensive translucent effects.
Flexible placement
Place loaders fullscreen, centered, pinned to an edge, Chrome-style at the top, or around the WebView with safe-area-aware insets.
Built-in and asset loaders
Use native Siri-style, Siri v2 edge, Chrome top progress, orbit, ring, pulse, dots, bars, wave, halo, image, or Lottie-based loaders.
Callable everywhere
Trigger loaders from JavaScript, Swift, Kotlin, or other native plugins through the public native API.
## When To Use It
[Section titled âWhen To Use Itâ](#when-to-use-it)
`@capgo/capacitor-native-loader` is for loading states that should stay smooth, translucent, and native while the WebView is busy, resizing, navigating, or hidden behind native surfaces.
Use it when you need:
* transparent full-screen loading layers above web content
* Chrome-style top progress bars that can resize the WebView instead of covering content
* edge loaders at the top, bottom, left, or right of the app
* loaders around a resized WebView while native content owns part of the screen
* native fallback loaders that survive heavy web rendering, route changes, or startup work
* shared loaders triggered by another native plugin before JavaScript is ready
* Lottie, bundled image, or remote asset-backed loading animations
Note
For web-only route animations, use [@capgo/capacitor-transitions](/docs/plugins/transitions/). Use Native Loader when the loading state itself should be native or when the WebView needs to be resized while loading.
## Demo Styles
[Section titled âDemo Stylesâ](#demo-styles)
| Style | Preview |
| ---------- | ------------------------------------------------------------------------------- |
| Siri |  |
| Siri v2 |  |
| Chrome top |  |
| Ring |  |
| Dots |  |
| Bars |  |
| Wave |  |
| Orbit |  |
| Pulse |  |
| Halo |  |
| Around |  |
| Lottie |  |
| Image |  |
## Core API
[Section titled âCore APIâ](#core-api)
* `show(options)` displays a loader and returns its `id`.
* `update(options)` changes an existing loader without tearing down the overlay.
* `setProgress(options)` updates determinate loader progress.
* `hide(options)` removes one loader.
* `hideAll(options)` removes all loaders.
* `setWebViewLayout(options)` resizes or insets the WebView/body so native loaders can sit beside it.
* `resetWebViewLayout(options?)` restores the original WebView/body layout.
* `getState()` returns currently visible loader ids.
* `configure(options)` sets default style, placement, colors, motion, and behavior.
## Placement Model
[Section titled âPlacement Modelâ](#placement-model)
`placement` controls where the native surface appears:
* `fullscreen` covers the whole app, optionally translucent.
* `center` floats a compact loader over the WebView.
* `top`, `bottom`, `left`, and `right` pin loaders to a safe-area-aware edge.
* `chrome` style uses a full-width native top bar and pairs well with `webView.mode: 'resize'`.
* `around` renders loader motion around the screen frame.
* `custom` uses an explicit frame for native-plugin or split-view workflows.
Use `interactionMode: 'passThrough'` when users can keep interacting with the WebView, `block` when loading should prevent taps, or `loaderOnly` when only the loader surface should receive touches.
## Keep going from @capgo/capacitor-native-loader
[Section titled âKeep going from @capgo/capacitor-native-loaderâ](#keep-going-from-capgocapacitor-native-loader)
If you are using **@capgo/capacitor-native-loader** to plan native media and interface behavior, connect it with [Getting Started](/docs/plugins/native-loader/getting-started/) for implementation details, [@capgo/capacitor-native-navigation](/docs/plugins/native-navigation/) for native chrome and WebView layout, [@capgo/capacitor-transitions](/docs/plugins/transitions/) for web route motion, and [Using @capgo/capacitor-native-loader](/plugins/capacitor-native-loader/) for the tutorial.
# Getting Started
> Install @capgo/capacitor-native-loader and show native loaders from JavaScript, Swift, Kotlin, or another plugin.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-native-loader` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```bash
npm install @capgo/capacitor-native-loader
npx cap sync
```
2. **Show a native loader from JavaScript**
```ts
import { NativeLoader } from '@capgo/capacitor-native-loader';
const { id } = await NativeLoader.show({
style: 'siri',
placement: 'fullscreen',
message: 'Preparing workspace',
colors: ['#71f6ff', '#8b5cf6', '#ff4ecd', '#fff7ad'],
scrimColor: 'rgba(3, 7, 18, 0.42)',
interactionMode: 'block',
});
await doExpensiveWork();
await NativeLoader.hide({ id });
```
## Configure Defaults
[Section titled âConfigure Defaultsâ](#configure-defaults)
```ts
await NativeLoader.configure({
defaults: {
style: 'orbit',
placement: 'center',
size: 96,
colors: ['#38bdf8', '#a78bfa'],
reducedMotion: 'system',
interactionMode: 'passThrough',
},
});
```
## Chrome-Style Top Loader
[Section titled âChrome-Style Top Loaderâ](#chrome-style-top-loader)
Use `style: 'chrome'` when you want the familiar browser top progress bar and the WebView should stay usable below it.
```ts
const { id } = await NativeLoader.show({
style: 'chrome',
placement: 'top',
colors: ['#4285f4', '#34a853', '#fbbc05', '#ea4335'],
thickness: 4,
interactionMode: 'passThrough',
webView: {
mode: 'resize',
insets: { top: 12 },
restoreOnHide: true,
},
});
await NativeLoader.hide({ id, restoreWebView: true });
```
## Siri V2 Edge Loader
[Section titled âSiri V2 Edge Loaderâ](#siri-v2-edge-loader)
Use `style: 'siri-v2'` when the loading state should be a native full-screen color motion around the app edge instead of a centered card.
```ts
const { id } = await NativeLoader.show({
style: 'siri-v2',
placement: 'fullscreen',
colors: ['#71f6ff', '#8b5cf6', '#ff4ecd', '#fff7ad'],
thickness: 10,
scrimColor: 'rgba(3, 7, 18, 0.10)',
interactionMode: 'passThrough',
});
await NativeLoader.hide({ id });
```
## Update Progress
[Section titled âUpdate Progressâ](#update-progress)
```ts
const { id } = await NativeLoader.show({
style: 'ring',
message: 'Uploading',
progress: 0,
});
for await (const progress of uploadFile(file)) {
await NativeLoader.setProgress({ id, progress });
}
await NativeLoader.hide({ id });
```
## Resize The WebView
[Section titled âResize The WebViewâ](#resize-the-webview)
Use `setWebViewLayout` when a native loader should own part of the screen while web content remains visible and usable.
```ts
await NativeLoader.setWebViewLayout({
mode: 'inset',
insets: { top: 96, bottom: 24 },
animated: true,
});
await NativeLoader.show({
style: 'wave',
placement: 'top',
size: 72,
message: 'Syncing',
interactionMode: 'passThrough',
});
```
Restore the original layout when the native surface is gone:
```ts
await NativeLoader.hideAll({ restoreWebView: true });
```
## Lottie And Image Loaders
[Section titled âLottie And Image Loadersâ](#lottie-and-image-loaders)
Bundle Lottie JSON or image assets in the native app and reference them from JavaScript.
```ts
await NativeLoader.show({
style: 'lottie',
placement: 'center',
asset: {
type: 'lottie',
source: 'rocket-loader.json',
loop: true,
},
});
```
```ts
await NativeLoader.show({
style: 'image',
placement: 'bottom',
asset: {
type: 'image',
source: 'loader-frame',
},
});
```
Note
Remote assets are supported, but bundled native assets are better for startup loaders because they are available before networking and JavaScript initialization finish.
## Native Swift Calls
[Section titled âNative Swift Callsâ](#native-swift-calls)
Other iOS plugins can call the shared loader directly.
```swift
import CapgoCapacitorNativeLoader
let id = NativeLoader.shared.show(options: [
"style": "siri",
"placement": "fullscreen",
"message": "Opening secure session",
"interactionMode": "block"
])
NativeLoader.shared.setProgress(id: id, progress: 0.6)
NativeLoader.shared.hide(id: id)
```
## Native Kotlin Calls
[Section titled âNative Kotlin Callsâ](#native-kotlin-calls)
Other Android plugins can call the public object from Kotlin or Java.
```kotlin
import app.capgo.nativeloader.NativeLoader
val id = NativeLoader.show(
activity = activity,
options = mapOf(
"style" to "orbit",
"placement" to "fullscreen",
"message" to "Loading profile",
"interactionMode" to "block",
),
webView = bridge.webView,
)
NativeLoader.setProgress(id, 0.6)
NativeLoader.hide(id)
```
## Common Options
[Section titled âCommon Optionsâ](#common-options)
| Option | Type | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| `style` | `'siri' \| 'siri-v2' \| 'chrome' \| 'orbit' \| 'ring' \| 'pulse' \| 'dots' \| 'bars' \| 'wave' \| 'halo' \| 'lottie' \| 'image'` | Loader renderer |
| `placement` | `'center' \| 'top' \| 'bottom' \| 'left' \| 'right' \| 'fullscreen' \| 'around' \| 'custom'` | Native surface position |
| `interactionMode` | `'passThrough' \| 'block' \| 'loaderOnly'` | Touch handling |
| `backgroundColor` | `string` | Overlay color, including alpha |
| `scrimColor` | `string` | Fullscreen or around-screen scrim color |
| `colors` | `string[]` | Loader gradient colors |
| `progress` | `number` | Determinate value from `0` to `1` |
| `autoHide` | `number` | Milliseconds before hiding automatically |
| `asset` | `object` | Lottie or image asset source |
Use `reducedMotion: 'system'` to respect the userâs platform motion settings.
# @capgo/capacitor-native-market
> Capacitor Native Market Plugin for opening app store listings and pages.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Native Market Plugin for opening app store listings and pages.
Package name changed.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `openStoreListing` - Launch app listing page in Play Store (Android) or App Store (iOS).
* `openDevPage` - Deep-link directly to a developerâs page in the Play Store. Android only.
* `openCollection` - Link users to a collection or top charts in the Play Store. Android only.
* `openEditorChoicePage` - Link users to Editorâs choice page in the Play Store. Android only.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ---------------------- | ------------------------------------------------------------------------- |
| `openStoreListing` | Launch app listing page in Play Store (Android) or App Store (iOS). |
| `openDevPage` | Deep-link directly to a developerâs page in the Play Store. Android only. |
| `openCollection` | Link users to a collection or top charts in the Play Store. Android only. |
| `openEditorChoicePage` | Link users to Editorâs choice page in the Play Store. Android only. |
| `search` | Search the Play Store with custom search terms. Android only. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-native-market](https://github.com/Cap-go/capacitor-native-market/).
## Keep going from @capgo/capacitor-native-market
[Section titled âKeep going from @capgo/capacitor-native-marketâ](#keep-going-from-capgocapacitor-native-market)
If you are using **@capgo/capacitor-native-market** to plan store approval and distribution, connect it with [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [Capacitor OTA Updates: App Store Approval Guide](/blog/capacitor-ota-updates-app-store-approval-guide/) for the practical context in Capacitor OTA Updates: App Store Approval Guide, and [Google Play Staged Rollouts: How It Works](/blog/google-play-staged-rollouts-how-it-works/) for the practical context in Google Play Staged Rollouts: How It Works.
# Getting Started
> Install @capgo/capacitor-native-market and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-native-market` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-native-market
npx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { NativeMarket } from '@capgo/capacitor-native-market';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `openStoreListing`
[Section titled âopenStoreListingâ](#openstorelisting)
Launch app listing page in Play Store (Android) or App Store (iOS).
```typescript
import { NativeMarket } from '@capgo/capacitor-native-market';
// Open app in store
await NativeMarket.openStoreListing({
appId: 'com.example.app'
});
// Open app in specific country store (iOS only)
await NativeMarket.openStoreListing({
appId: 'com.example.app',
country: 'IT'
});
```
### `openDevPage`
[Section titled âopenDevPageâ](#opendevpage)
Deep-link directly to a developerâs page in the Play Store. Android only.
```typescript
import { NativeMarket } from '@capgo/capacitor-native-market';
await NativeMarket.openDevPage({
devId: 'Google+LLC'
});
```
### `openCollection`
[Section titled âopenCollectionâ](#opencollection)
Link users to a collection or top charts in the Play Store. Android only.
```typescript
import { NativeMarket } from '@capgo/capacitor-native-market';
await NativeMarket.openCollection({
name: 'featured'
});
```
### `openEditorChoicePage`
[Section titled âopenEditorChoicePageâ](#openeditorchoicepage)
Link users to Editorâs choice page in the Play Store. Android only.
```typescript
import { NativeMarket } from '@capgo/capacitor-native-market';
await NativeMarket.openEditorChoicePage({
editorChoice: 'editorial_fitness_apps_us'
});
```
### `search`
[Section titled âsearchâ](#search)
Search the Play Store with custom search terms. Android only.
```typescript
import { NativeMarket } from '@capgo/capacitor-native-market';
await NativeMarket.search({
terms: 'fitness apps'
});
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-native-navigation
> Render iOS and Android navigation chrome natively while JavaScript keeps owning routes, content, icons, labels, and bar state.
Native chrome
Draw the top navigation bar and bottom tab bar with platform UI instead of web components.
Web-owned routes
Native emits user intent events, then your existing router changes the WebView content.
Serialized icons
Configure tabs and buttons with SVG, SF Symbol, bundled image, or Android drawable descriptors.
Native transition shell
Capture the current WebView, update content in JavaScript, then finish with a native snapshot-to-WebView animation.
Zoom routes
Open card, grid, and media-detail routes with shared-element-style native zoom geometry.
## Demo
[Section titled âDemoâ](#demo)
| Native shell | Tap flow |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  |  |
## Core API
[Section titled âCore APIâ](#core-api)
* `configure(options?)` enables the native chrome host and controls content insets.
* `setNavbar(options)` updates native title, subtitle, back button, buttons, colors, transparency, and visibility.
* `setTabbar(options)` updates tabs, selected tab, badges, labels, icons, colors, and visibility.
* `beginTransition(options?)` captures the outgoing WebView before the JavaScript route change.
* `finishTransition(options?)` animates from the captured snapshot to the live WebView after route content is ready.
* `beginZoomTransition(target, options?)` and `finishZoomTransition(target?, options?)` are JavaScript helpers for zoom transitions from elements or rectangles.
* `getPluginVersion()` returns the native implementation version marker.
## Events
[Section titled âEventsâ](#events)
* `navbarBack` fires when the native back affordance is tapped.
* `navbarItemTap` fires when a native navbar action button is tapped.
* `tabSelect` fires when a native tab is selected.
* `safeAreaChanged` reports native bar and safe-area inset changes.
* `transitionStart` and `transitionEnd` report native transition boundaries.
## Platform Model
[Section titled âPlatform Modelâ](#platform-model)
iOS uses `UINavigationBar` and `UITabBar`. On iOS 26 and newer, the plugin lets the system render Liquid Glass behavior; older versions use native translucent/material fallbacks.
Android uses an AppCompat toolbar and Material bottom navigation with edge-to-edge placement.
The plugin does not create one native WebView per route. Version 1 keeps a single Capacitor WebView for bridge stability and lets native own only the frame, bar visuals, tab selection chrome, safe-area reporting, and transition shell.
## Keep going from @capgo/capacitor-native-navigation
[Section titled âKeep going from @capgo/capacitor-native-navigationâ](#keep-going-from-capgocapacitor-native-navigation)
If you are using **@capgo/capacitor-native-navigation** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-native-navigation](/plugins/capacitor-native-navigation/) for the native capability in Using @capgo/capacitor-native-navigation, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-native-navigation and render native navigation chrome over a Capacitor WebView.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-native-navigation` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```sh
npm i @capgo/capacitor-native-navigation
```
2. **Sync native projects**
```sh
npx cap sync
```
3. **Configure native chrome**
```ts
import { NativeNavigation } from '@capgo/capacitor-native-navigation';
await NativeNavigation.configure({
contentInsetMode: 'css',
animationDuration: 360,
colors: {
tint: '#0f172a',
inactiveTint: '#64748b',
},
});
```
4. **Render the native navbar**
```ts
await NativeNavigation.setNavbar({
title: 'Home',
subtitle: 'Native chrome',
transparent: true,
backButton: { visible: false },
rightItems: [
{
id: 'compose',
title: 'Compose',
icon: {
svg: ' ',
},
},
],
});
```
5. **Render the native tabbar**
```ts
await NativeNavigation.setTabbar({
selectedId: 'home',
labelVisibilityMode: 'selected',
icons: true,
colors: {
dynamic: true,
tint: '#0f172a',
inactiveTint: '#64748b',
},
tabs: [
{
id: 'home',
title: 'Home',
icon: {
svg: ' ',
},
},
{
id: 'settings',
title: 'Settings',
badge: '2',
icon: {
svg: ' ',
},
},
],
});
```
6. **Handle native intent events**
```ts
await NativeNavigation.addListener('navbarBack', () => {
router.back();
});
await NativeNavigation.addListener('navbarItemTap', ({ id }) => {
if (id === 'compose') router.push('/compose');
});
await NativeNavigation.addListener('tabSelect', ({ id }) => {
router.push(`/${id}`);
});
```
## Transition flow
[Section titled âTransition flowâ](#transition-flow)
Native transitions are a transaction around your normal JavaScript route change:
```ts
const transition = await NativeNavigation.beginTransition({
direction: 'forward',
});
router.push('/detail');
await router.ready?.();
await NativeNavigation.setNavbar({
title: 'Detail',
backButton: { visible: true, title: 'Back' },
});
await NativeNavigation.finishTransition({
id: transition.id,
direction: 'forward',
});
```
## Zoom transition
[Section titled âZoom transitionâ](#zoom-transition)
Use the zoom helpers for card-to-detail or media-preview flows. Pass the tapped element before your router changes content, then finish after the detail page is ready.
```ts
import { beginZoomTransition, finishZoomTransition } from '@capgo/capacitor-native-navigation';
const card = document.querySelector('[data-message-card]');
if (card) {
const transition = await beginZoomTransition(card, { cornerRadius: 18 });
router.push('/message/42');
await router.ready?.();
await NativeNavigation.setNavbar({
title: 'Message',
backButton: { visible: true, title: 'Inbox' },
});
await finishZoomTransition(undefined, {
id: transition.id,
cornerRadius: 18,
});
}
```
## Use with @capgo/capacitor-transitions
[Section titled âUse with @capgo/capacitor-transitionsâ](#use-with-capgocapacitor-transitions)
Use Native Navigation for the native navbar, tabbar, safe-area insets, and native intent events. Use `@capgo/capacitor-transitions` for the WebView page stack underneath the native chrome.
```bash
npm install @capgo/capacitor-native-navigation @capgo/capacitor-transitions
npx cap sync
```
Initialize both packages once:
```ts
import { NativeNavigation } from '@capgo/capacitor-native-navigation';
import '@capgo/capacitor-transitions';
import { initTransitions, setupRouterOutlet, setDirection } from '@capgo/capacitor-transitions/react';
initTransitions({ platform: 'auto' });
const outlet = document.querySelector('cap-router-outlet');
if (outlet) {
setupRouterOutlet(outlet, { platform: 'auto', swipeGesture: 'auto' });
}
await NativeNavigation.configure({
contentInsetMode: 'css',
});
```
Keep `cap-router-outlet` focused on pages, not duplicate web bars:
```html
Inbox content
```
Drive both packages from the same router actions:
```ts
async function openMessage(id: string) {
setDirection('forward');
await router.push(`/messages/${id}`);
await NativeNavigation.setNavbar({
title: 'Message',
backButton: { visible: true, title: 'Inbox' },
});
}
await NativeNavigation.addListener('navbarBack', () => {
setDirection('back');
router.back();
});
await NativeNavigation.addListener('tabSelect', ({ id }) => {
setDirection('root');
router.push(`/${id}`);
});
```
Pick one animation layer per route change. Let `@capgo/capacitor-transitions` animate normal page pushes, and use Native Navigationâs zoom helpers only for shared-element or zoom routes.
## CSS insets
[Section titled âCSS insetsâ](#css-insets)
With `contentInsetMode: 'css'`, the plugin writes native bar dimensions to `document.documentElement`.
```css
.page {
padding-top: var(--cap-native-navigation-top);
padding-bottom: var(--cap-native-navigation-bottom);
}
```
Available variables:
* `--cap-native-navigation-top`
* `--cap-native-navigation-right`
* `--cap-native-navigation-bottom`
* `--cap-native-navigation-left`
* `--cap-native-navbar-height`
* `--cap-native-tabbar-height`
## Icon descriptors
[Section titled âIcon descriptorsâ](#icon-descriptors)
Icons must be serializable because native UI renders them. You can use cross-platform SVG, platform-specific SVG, SF Symbols, bundled iOS images, Android drawable resources, or bundled Android images.
```ts
const icon = {
svg: ' ',
width: 24,
height: 24,
template: true,
src: 'fallback_asset_name',
ios: {
svg: ' ',
sfSymbol: 'house.fill',
image: 'BundledAssetName',
},
android: {
svg: ' ',
resource: 'ic_menu_view',
image: 'bundled_drawable_name',
},
};
```
Inline SVG supports the icon-focused subset used by common icon sets such as Lucide and Feather: `path`, `line`, `polyline`, `polygon`, `circle`, and `rect`. SVG icons are rendered as template images by default, so native tint colors can recolor them.
## Optional web components
[Section titled âOptional web componentsâ](#optional-web-components)
The package can register custom elements for framework-agnostic declarative setup:
```ts
import { defineNativeNavigationElements } from '@capgo/capacitor-native-navigation';
defineNativeNavigationElements();
```
```html
```
Note
The web components are optional. React, Vue, Angular, Svelte, Solid, and vanilla apps can all use the imperative API directly.
## Platform notes
[Section titled âPlatform notesâ](#platform-notes)
* iOS renders `UINavigationBar` and `UITabBar`; iOS 26+ uses the system Liquid Glass bar behavior.
* Android renders an AppCompat toolbar and Material bottom navigation.
* Web fallback does not draw native bars. It mirrors events and inset variables for browser development.
* The plugin keeps one full-screen Capacitor WebView. Native owns the frame, bars, safe-area reporting, and transition shell.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-native-navigation](/plugins/capacitor-native-navigation/) for the native capability in Using @capgo/capacitor-native-navigation, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# @capgo/native-purchases
> In-app Subscriptions Made Easy.
## Overview
[Section titled âOverviewâ](#overview)
In-app Subscriptions Made Easy.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* Native in-app purchases and subscriptions with StoreKit 2 and Google Play Billing.
* Product metadata loaded directly from the stores, including localized titles and prices.
* iOS StoreKit pricing terms for monthly subscriptions with 12-month commitments.
* Purchase, restore, entitlement refresh, and native subscription management flows.
* App Transaction helpers for migration from older business models.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `restorePurchases` | Restores a userâs previous and links their appUserIDs to any userâs also using those . |
| `getAppTransaction` | Gets the App Transaction information, which provides details about when the user originally downloaded or purchased the app. |
| `isEntitledToOldBusinessModel` | Compares the original app version from the App Transaction against a target version to determine if the user is entitled to features from an earlier business model. |
| `purchaseProduct` | Starts the native purchase flow. On supported iOS versions, pass `billingPlanType: 'monthly'` to purchase a monthly billing plan with a 12-month commitment. |
| `getProducts` | Gets product info associated with product identifiers. On supported iOS versions, subscription products can include `pricingTerms`. |
| `getProduct` | Gets the product info for a single product identifier. |
| `isBillingSupported` | Check if billing is supported for the current device. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
| `getPurchases` | Gets all the userâs purchases (both in-app purchases and subscriptions). This method queries the platformâs purchase history for the current user. |
| `manageSubscriptions` | Opens the platformâs native subscription management page. This allows users to view, modify, or cancel their subscriptions. |
| `acknowledgePurchase` | Manually acknowledge/finish a purchase transaction. |
| `consumePurchase` | Consume an in-app purchase on Android. |
| `addListener` | Listen for StoreKit transaction updates delivered by Appleâs Transaction.updates. Fires on app launch if there are unfinished transactions, and for any updates afterward. iOS only. |
| `addListener` | Listen for StoreKit transaction verification failures delivered by Appleâs Transaction.updates. Fires when the verification result is unverified. iOS only. |
| `removeAllListeners` | Remove all registered listeners. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-native-purchases](https://github.com/Cap-go/capacitor-native-purchases/).
## Keep going from @capgo/native-purchases
[Section titled âKeep going from @capgo/native-purchasesâ](#keep-going-from-capgonative-purchases)
If you are using **@capgo/native-purchases** to plan payments and purchases, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [Capgo Pricing](/pricing/) for the product workflow in Capgo Pricing, [Payment system](/docs/webapp/payment/) for the implementation detail in Payment system, [Getting Started](/docs/plugins/native-purchases/getting-started/) for the implementation detail in Getting Started, and [Revenue Playbook](/docs/plugins/native-purchases/revenue-playbook/) for the implementation detail in Revenue Playbook.
# Create Android Auto-Renewable Subscription
> Step-by-step guide to creating auto-renewable subscriptions in Google Play Console for the native-purchases plugin.
Auto-renewable subscriptions provide access to content, services, or premium features in your app on an ongoing basis. This guide will help you create and configure subscriptions in Google Play Console.
## Overview
[Section titled âOverviewâ](#overview)
Subscriptions automatically renew at the end of each billing period until the user cancels. Theyâre ideal for:
* Premium content access
* Ad-free experiences
* Cloud storage
* Ongoing services
## Creating a Subscription
[Section titled âCreating a Subscriptionâ](#creating-a-subscription)
1. **Navigate to Subscriptions**
In Google Play Console, select your app and choose **Monetize > Subscriptions** from the left menu.
Click the **Create subscription** button to begin.

2. **Enter Basic Information**
Provide a subscription name and product ID. The product ID is required for configuration in your app and cannot be changed later.

3. **Create Base Plan**
Google Play requires exactly one base plan per subscription. The native-purchases plugin supports only one base plan to maintain compatibility with iOS.
Click **Add base plan** to continue.

4. **Configure Base Plan Details**
Enter:
* **Base plan ID**: Unique identifier for this plan
* **Billing period**: How often users are charged (weekly, monthly, yearly, etc.)
* **Grace period**: Time window during which Google maintains the subscription while retrying payment before cancellation

5. **Set Up Pricing**
Access the pricing section and select all countries/regions where you want to offer the subscription.

6. **Configure Price**
Set your base price in your primary currency. Google Play automatically converts this to local currencies.

7. **Review Regional Pricing**
Review the automatically converted prices for each country. You can adjust individual prices if needed.

8. **Save Configuration**
Save your pricing configuration.

9. **Activate Subscription**
Click the **Activate** button to make your subscription product live and available for purchase.

## Important Considerations
[Section titled âImportant Considerationsâ](#important-considerations)
### Base Plan Limitation
[Section titled âBase Plan Limitationâ](#base-plan-limitation)
The native-purchases plugin requires exactly one base plan per subscription to ensure consistency with iOS subscription handling. Multiple base plans are not supported.
### Grace Period
[Section titled âGrace Periodâ](#grace-period)
The grace period allows Google Play to retry failed payments while maintaining the userâs subscription access. Common grace periods are:
* 3 days for monthly subscriptions
* 7 days for longer subscriptions
### Subscription Status
[Section titled âSubscription Statusâ](#subscription-status)
After creation, your subscription will be in âDraftâ status until activated. You can test draft subscriptions in sandbox mode.
## Using in Your App
[Section titled âUsing in Your Appâ](#using-in-your-app)
Once created, reference the subscription in your app using the product ID:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Load subscription info
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['com.example.premium.monthly'],
productType: PURCHASE_TYPE.SUBS,
});
const product = products[0];
console.log(`${product.title} â ${product.priceString}`);
// Purchase (planIdentifier = Base Plan ID from Google Play Console)
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.example.premium.monthly',
planIdentifier: 'monthly-plan', // REQUIRED on Android, ignored on iOS
productType: PURCHASE_TYPE.SUBS,
});
console.log('Transaction ID', transaction.transactionId);
// Later, check purchase state
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const subscription = purchases.find(
(purchase) => purchase.productIdentifier === 'com.example.premium.monthly',
);
if (subscription && subscription.purchaseState === 'PURCHASED' && subscription.isAcknowledged) {
console.log('Subscription active locally');
// For expiration/cancellation, validate purchaseToken through your backend
}
```
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
* [Create an introductory offer](/docs/plugins/native-purchases/android-introductory-offer/) to attract new subscribers
* [Configure sandbox testing](/docs/plugins/native-purchases/android-sandbox-testing/) to test your subscriptions
* Set up backend receipt validation for security
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Subscription not appearing in app:**
* Verify the product ID matches exactly
* Ensure the subscription is activated
* Check that your app has the correct package name
* Wait 2-3 hours after activation for changes to propagate
**Base plan errors:**
* Ensure you have exactly one base plan
* Verify all required fields are filled
* Check that billing period is valid
**Pricing issues:**
* Confirm at least one country is selected
* Verify base price is greater than minimum allowed
* Check currency conversion rates are acceptable
## Keep going from Create Android Auto-Renewable Subscription
[Section titled âKeep going from Create Android Auto-Renewable Subscriptionâ](#keep-going-from-create-android-auto-renewable-subscription)
If you are using **Create Android Auto-Renewable Subscription** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# Create Android Subscription Introductory Offer
> Learn how to create introductory offers for auto-renewable subscriptions on Android to attract new subscribers.
Introductory offers allow you to provide eligible users with either a free trial or a discounted introductory price. After the introductory period concludes, subscriptions automatically renew at standard pricing unless cancelled.
## Overview
[Section titled âOverviewâ](#overview)
Introductory offers are a powerful tool to:
* Reduce barriers to entry for new subscribers
* Increase conversion rates
* Allow users to try your premium features risk-free
* Build long-term subscriber relationships
## Eligibility
[Section titled âEligibilityâ](#eligibility)
Users can receive an introductory offer if they havenât previously purchased or received an introductory offer for the subscription. Google Play handles eligibility automatically.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
You must first [create an auto-renewable subscription](/docs/plugins/native-purchases/android-create-subscription/) before adding an introductory offer.
## Creating an Introductory Offer
[Section titled âCreating an Introductory Offerâ](#creating-an-introductory-offer)
1. **Access Offer Configuration**
Navigate to your subscription in Google Play Console and select the **Add offer** button.

2. **Select Base Plan**
A modal will appear requiring you to choose your base plan. Typically, youâll only have one base plan. Click **Add offer** to continue.

3. **Configure Offer Details**
Enter the following information:
**Offer ID**: A unique identifier for this offer
**Eligibility**: Choose who can receive this offer
* **New customers**: Only users who have never subscribed
* **Existing customers**: Users who previously subscribed
* **Developer determined**: Custom eligibility logic (not supported by native-purchases)
Caution
The native-purchases plugin does not support the âDeveloper determinedâ eligibility option. Use âNew customersâ or âExisting customersâ instead.

4. **Add Phases**
Click **Add phase** at the bottom of the page to define your offer structure.
You can add up to two phases, allowing combinations like:
* Free trial only
* Discounted price only
* Free trial followed by discounted recurring payment
5. **Select Phase Type**
Choose from three phase types:
**Free Trial**
* Complimentary access for a set duration
* Example: 7 days free, then $9.99/month
**Single Payment**
* One-time discounted price for a specific period
* Example: $1.99 for 2 months, then $9.99/month
**Discounted Recurring Payment**
* Reduced per-billing-cycle rate for multiple cycles
* Example: $4.99/month for 3 months, then $9.99/month
6. **Configure Phase Duration**
Set how long the introductory phase lasts:
* Days, weeks, or months
* Number of billing cycles
7. **Finalize and Activate**
Click **Apply**, then **Save** to activate the offer. The **Activate** button will become available once saved.
## Offer Phase Examples
[Section titled âOffer Phase Examplesâ](#offer-phase-examples)
### Example 1: Simple Free Trial
[Section titled âExample 1: Simple Free Trialâ](#example-1-simple-free-trial)
* Phase 1: 7 days free
* Then: $9.99/month standard pricing
### Example 2: Discounted Introduction
[Section titled âExample 2: Discounted Introductionâ](#example-2-discounted-introduction)
* Phase 1: $1.99 for the first month
* Then: $9.99/month standard pricing
### Example 3: Extended Trial + Discount
[Section titled âExample 3: Extended Trial + Discountâ](#example-3-extended-trial--discount)
* Phase 1: 14 days free
* Phase 2: $4.99/month for 2 months
* Then: $9.99/month standard pricing
## Using in Your App
[Section titled âUsing in Your Appâ](#using-in-your-app)
The native-purchases plugin automatically handles introductory offer eligibility and presentation:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Fetch products (includes intro offer metadata)
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['com.example.premium.monthly'],
productType: PURCHASE_TYPE.SUBS,
});
const product = products[0];
if (product.introductoryPrice) {
console.log(`Intro price: ${product.introductoryPriceString}`);
console.log(`Regular price: ${product.priceString}`);
console.log(
`Offer duration: ${product.introductoryPrice.subscriptionPeriod?.numberOfUnits} ${product.introductoryPrice.subscriptionPeriod?.unit}`,
);
} else {
console.log('No intro offer configured for this product');
}
// Purchase (Google Play applies intro pricing automatically if the user is eligible)
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.example.premium.monthly',
planIdentifier: 'monthly-plan', // Base Plan ID from Google Play Console
productType: PURCHASE_TYPE.SUBS,
});
console.log('Introductory purchase transaction', transaction.transactionId);
```
## Best Practices
[Section titled âBest Practicesâ](#best-practices)
### Offer Duration
[Section titled âOffer Durationâ](#offer-duration)
* **Free trials**: 3-14 days is optimal for most apps
* **Discounted periods**: 1-3 months works well for building habit
* **Price discount**: 50-70% off regular price drives conversions
### Marketing
[Section titled âMarketingâ](#marketing)
* Clearly display the intro offer and regular price
* Show what happens after the intro period
* Make cancellation easy and transparent
* Remind users before the intro period ends
### A/B Testing
[Section titled âA/B Testingâ](#ab-testing)
Test different offer structures:
* Free trial length
* Discount percentage
* Discount duration
* Single phase vs. multi-phase
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
* Only one introductory offer can be active per subscription at a time
* Users can only claim an intro offer once per subscription
* Intro offers donât apply to subscription upgrades/downgrades
* Changes to intro offers donât affect existing subscribers
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Intro offer not showing:**
* Verify the offer is activated in Play Console
* Check user eligibility (may have used offer before)
* Ensure app is using latest product information
**Wrong users receiving offer:**
* Review eligibility settings (new vs. existing customers)
* Check if user previously subscribed on different device
* Verify Play Store account history
**Offer not applying at purchase:**
* Confirm product ID matches exactly
* Check that offer is still active and not expired
* Verify date range settings for the offer
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
* [Configure sandbox testing](/docs/plugins/native-purchases/android-sandbox-testing/) to test your offers
* Monitor conversion rates in Play Console analytics
* Consider creating multiple subscription tiers with different offers
## Keep going from Create Android Subscription Introductory Offer
[Section titled âKeep going from Create Android Subscription Introductory Offerâ](#keep-going-from-create-android-subscription-introductory-offer)
If you are using **Create Android Subscription Introductory Offer** to plan payments and purchases, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [Capgo Pricing](/pricing/) for the product workflow in Capgo Pricing, [Payment system](/docs/webapp/payment/) for the implementation detail in Payment system, [@capgo/native-purchases](/docs/plugins/native-purchases/) for the implementation detail in @capgo/native-purchases, and [Getting Started](/docs/plugins/native-purchases/getting-started/) for the implementation detail in Getting Started.
# Android Play Store Review Guidelines for IAP
> Complete guide to passing Google Play review with in-app purchases and subscriptions, including compliance requirements and best practices.
Getting your Android app approved on Google Play requires compliance with Googleâs policies, especially for apps with in-app purchases and subscriptions. This guide covers everything you need to pass review successfully.
## Release Path That Works
[Section titled âRelease Path That Worksâ](#release-path-that-works)
1. **Build a Signed Android App Bundle**
New Google Play apps should be uploaded as an Android App Bundle (`.aab`), not a sideloaded debug APK.
Keep your `versionCode` increasing on every upload and store your upload key safely if you use Play App Signing.

2. **Create the App Record in Play Console**
If you do not have a developer account yet, start with [Play Console signup](https://play.google.com/console/signup). Then, in **Home > Create app**, choose the language, app/game type, free/paid status, support email, and accept the required declarations.
Choose the free/paid setting carefully. Google lets you change a paid app to free later, but once an app has been offered for free, it cannot be switched to paid.

3. **Complete App Content and Store Listing**
Before production review, finish the required Play Console declarations:
* Privacy policy
* Ads
* App access
* Target audience and content
* Content rating
* Data Safety
* Sensitive permissions declarations, if applicable
4. **Run a Play-Installed Test Track**
Start with **internal testing** for fast QA. If your developer account is a personal account created after November 13, 2023, you must also complete a **closed test** with at least 12 opted-in testers for 14 consecutive days before production access.

5. **Verify Billing End-to-End**
Install the app from Google Play, not from a locally exported APK. Then confirm that:
* Products load from Play correctly
* The purchase sheet shows a **test purchase** banner for license testers
* Entitlements unlock after purchase
* Restore and subscription management flows work
## Google Play Billing Requirements
[Section titled âGoogle Play Billing Requirementsâ](#google-play-billing-requirements)
### Mandatory Billing System
[Section titled âMandatory Billing Systemâ](#mandatory-billing-system)
For digital goods and services, you **must** use Google Playâs billing system:
**Digital Goods (Must Use Play Billing):**
* Subscriptions to premium features
* In-app currency or credits
* Digital content (ebooks, music, videos)
* Game upgrades and power-ups
* App unlocks and premium tiers
**Physical Goods (Cannot Use Play Billing):**
* Physical merchandise
* Real-world services
* One-time donations to nonprofits
:::note Subscription Setup In Play Console, configure Android subscriptions using the current **subscription -> base plan -> offer** model. In `native-purchases`, pass the Base Plan ID with `planIdentifier`. :::
### Implementation with Native Purchases
[Section titled âImplementation with Native Purchasesâ](#implementation-with-native-purchases)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Ensure billing is available on the device
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) throw new Error('Google Play Billing not available');
// Fetch subscription products (Store data is requiredânever hardcode pricing)
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['premium_monthly', 'premium_yearly'],
productType: PURCHASE_TYPE.SUBS,
});
// Plan identifiers are the Base Plan IDs you create in Google Play Console
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'premium_monthly',
planIdentifier: 'monthly-plan', // REQUIRED on Android, ignored on iOS
productType: PURCHASE_TYPE.SUBS,
});
console.log('Purchase token for server validation:', transaction.purchaseToken);
```
## Transparency and Disclosure Requirements
[Section titled âTransparency and Disclosure Requirementsâ](#transparency-and-disclosure-requirements)
### Upfront Pricing Disclosure
[Section titled âUpfront Pricing Disclosureâ](#upfront-pricing-disclosure)
Google Play mandates clear disclosure of all costs before purchase:
**Required Elements:**
* Exact price in userâs local currency
* Billing frequency (monthly, yearly, etc.)
* Whatâs included in the subscription
* Total cost for introductory offers
* When charges will occur

**Example of Compliant UI:**
```typescript
function SubscriptionCard({ product }) {
return (
{product.title}
{/* Show intro offer if available */}
{product.introductoryPrice && (
{product.introductoryPriceString}
for {product.introductoryPricePeriod}
)}
{/* Regular price */}
{product.priceString}
per {product.subscriptionPeriod}
{/* Clear description */}
{product.description}
{/* Renewal terms */}
Renews automatically. Cancel anytime in Google Play.
handlePurchase(product)}>
Subscribe Now
);
}
```
### Auto-Renewal Disclosure
[Section titled âAuto-Renewal Disclosureâ](#auto-renewal-disclosure)
Before a subscription auto-renews, Google requires:
* Clear notification that renewal will occur
* Reminder of the price
* Easy access to cancellation
Tip
The native-purchases plugin works with Google Play to handle auto-renewal notifications automatically. Ensure your subscription products are properly configured in Google Play Console.
### Cross-Platform Pricing Clarity
[Section titled âCross-Platform Pricing Clarityâ](#cross-platform-pricing-clarity)
If you sell the same entitlement on multiple platforms, keep the product naming, billing period, included benefits, and renewal language aligned so users are not surprised.
Prices can legitimately differ because of taxes, local currency, or store economics, but the purchase UI must never hide those differences or imply a different renewal cost than the one Google Play will charge.
## Privacy Policy Requirements
[Section titled âPrivacy Policy Requirementsâ](#privacy-policy-requirements)
### Mandatory Privacy Policy
[Section titled âMandatory Privacy Policyâ](#mandatory-privacy-policy)
If your app includes in-app purchases, you must:
1. **Link in Play Store Listing**
* Add privacy policy URL in Play Console
* Must be publicly accessible
* Must be in the same language as your app
2. **Link Within App**
* Display privacy policy in app settings
* Show before collecting any user data
* Make easily discoverable
**Example Implementation:**
```typescript
function SettingsScreen() {
const openPrivacyPolicy = () => {
window.open('https://yourapp.com/privacy', '_blank');
};
const openTerms = () => {
window.open('https://yourapp.com/terms', '_blank');
};
return (
Settings
Privacy Policy
Terms of Service
NativePurchases.manageSubscriptions()}>
Manage Subscriptions
);
}
```
### Data Safety Section
[Section titled âData Safety Sectionâ](#data-safety-section)
Google Play requires detailed disclosure in the Data Safety section:
**For IAP Apps, Declare:**
* Purchase history collection
* Email addresses (for receipts)
* Device IDs (for fraud prevention)
* Payment information handling
* Analytics data collection
The Data Safety section is legally binding. Inaccurate declarations can result in app removal.
## App Content Declarations
[Section titled âApp Content Declarationsâ](#app-content-declarations)
Google Play review is not only about the binary. Before a production release, complete the declarations on **Policy and programs > App content**.
**The minimum set to review carefully:**
* **Privacy policy**: Public URL in Play Console, plus an in-app entry point when required
* **Ads**: Declare whether the app contains ads
* **App access**: Give reviewers working credentials or a clear test path if any screen is gated
* **Target audience and content**: Match the real audience of the app
* **Content ratings**: Complete the IARC questionnaire so the app is not marked unrated
* **Data Safety**: Declare collection, sharing, and security practices accurately
Tip
If a reviewer needs login credentials, 2FA steps, a region toggle, or a specific test account to access billing, put that in **App access** and in your release notes. Missing reviewer access is a common preventable rejection.
## Common Rejection Reasons
[Section titled âCommon Rejection Reasonsâ](#common-rejection-reasons)
### 1. Missing or Incorrect Billing Implementation
[Section titled â1. Missing or Incorrect Billing Implementationâ](#1-missing-or-incorrect-billing-implementation)
**Why It Fails:**
* Not using Google Play Billing for digital goods
* Using deprecated billing APIs
* Implementing custom payment solutions for subscriptions
**Prevention:**
```typescript
// â
Correct: Use native-purchases (uses Google Play Billing)
await NativePurchases.purchaseProduct({
productIdentifier: 'premium_monthly',
planIdentifier: 'monthly-plan',
productType: PURCHASE_TYPE.SUBS,
});
// â Wrong: Custom payment processor for subscriptions
// await CustomPayment.charge(user, 9.99);
```
### 2. Unclear Pricing or Hidden Costs
[Section titled â2. Unclear Pricing or Hidden Costsâ](#2-unclear-pricing-or-hidden-costs)
**Why It Fails:**
* Price only shown after clicking purchase
* Additional fees not disclosed upfront
* Vague subscription terms
**Prevention:**
```typescript
function PurchaseScreen({ product }) {
return (
{/* Show ALL costs upfront */}
Premium Subscription
{product.priceString}/month
Taxes may apply based on location
Includes:
Ad-free experience
Unlimited cloud storage
Priority support
Subscription renews automatically unless cancelled at least
24 hours before the end of the current period.
Manage or cancel in Google Play Subscriptions.
Start Subscription
);
}
```
### 3. Deceptive Subscription Patterns
[Section titled â3. Deceptive Subscription Patternsâ](#3-deceptive-subscription-patterns)
**Why It Fails:**
* Pre-selecting premium options
* Hiding cheaper alternatives
* Making cancellation difficult
* Fake urgency (âOnly 3 spots left!â)


**Prevention:**
* Display all subscription tiers equally
* Make cancellation clear and accessible
* Avoid countdown timers or fake scarcity
* Donât use dark patterns to push expensive options
### 4. Incomplete Testing
[Section titled â4. Incomplete Testingâ](#4-incomplete-testing)
**Why It Fails:**
* App crashes when purchasing
* Products donât load
* Purchase confirmation doesnât show
* Premium features donât unlock after purchase
* Testing only happened on sideloaded builds instead of a Play-installed testing track
**Prevention:**
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Comprehensive testing before submission
async function testPurchaseFlow() {
try {
// 1. Test product loading
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['premium_monthly', 'premium_yearly'],
productType: PURCHASE_TYPE.SUBS,
});
console.log('â Products loaded:', products.length);
// 2. Test purchase flow
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'premium_monthly',
planIdentifier: 'monthly-plan',
productType: PURCHASE_TYPE.SUBS,
});
console.log('â Purchase completed', transaction.transactionId);
// 3. Verify entitlements
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
if (
purchases.some(
(purchase) =>
purchase.productIdentifier === 'premium_monthly' &&
['PURCHASED', '1'].includes(purchase.purchaseState ?? '') &&
purchase.isAcknowledged,
)
) {
console.log('â Premium features unlocked');
}
// 4. Test restore
await NativePurchases.restorePurchases();
console.log('â Restore works');
} catch (error) {
console.error('â Test failed:', error);
}
}
```
### 5. Privacy Policy Violations
[Section titled â5. Privacy Policy Violationsâ](#5-privacy-policy-violations)
**Why It Fails:**
* No privacy policy link in app
* Privacy policy not accessible
* Data collection not disclosed
* Data Safety section inaccurate
**Prevention:**
* Add privacy policy to Play Store listing
* Include link in app settings
* Accurately fill out Data Safety section
* Update policy when adding new data collection
## Alternative Billing Programs
[Section titled âAlternative Billing Programsâ](#alternative-billing-programs)
Googleâs alternative billing programs are region-specific and can change. If you want anything other than standard Google Play Billing, confirm the exact market eligibility, required APIs, and disclosure language in Play Console immediately before implementation.
Note
For most apps, sticking to standard Google Play Billing is the simplest and lowest-risk path for review.
## Subscription Management
[Section titled âSubscription Managementâ](#subscription-management)
### Easy Cancellation
[Section titled âEasy Cancellationâ](#easy-cancellation)
Users must be able to:
* View active subscriptions easily
* Cancel without contacting support
* Understand when cancellation takes effect
**Implementation:**
```typescript
import { NativePurchases } from '@capgo/native-purchases';
function ManageSubscriptionButton() {
const openManagement = async () => {
try {
// Opens Google Play subscription management
await NativePurchases.manageSubscriptions();
} catch (error) {
// Fallback to direct URL
const playStoreUrl = 'https://play.google.com/store/account/subscriptions';
window.open(playStoreUrl, '_blank');
}
};
return (
Manage Subscription in Google Play
);
}
```
### Cancellation Grace Period
[Section titled âCancellation Grace Periodâ](#cancellation-grace-period)
**Required Disclosure:**
* When does cancellation take effect?
* Do users keep access until period ends?
* Are partial refunds available?
```typescript
function CancellationInfo() {
return (
Cancellation Policy
Cancel anytime in Google Play
Access continues until end of billing period
No refunds for partial periods
Resubscribe anytime to regain access
NativePurchases.manageSubscriptions()}>
Manage in Google Play
);
}
```
## Pre-Submission Checklist
[Section titled âPre-Submission Checklistâ](#pre-submission-checklist)

1. **Verify Billing Implementation**
* Using Google Play Billing (via native-purchases)
* All subscription products created in Play Console
* Base plans and offers configured correctly
* Products are activated and published
* Pricing set for all target countries
2. **Test Purchase Flows**
* Create license test account
* Install the build from a Play testing track
* Test each subscription tier
* Verify products load correctly
* Test purchase completion
* Confirm the **test purchase** banner appears
* Verify premium features unlock
* Test subscription restoration
* Test on multiple devices
3. **Review All Copy**
* Pricing displayed clearly before purchase
* All fees disclosed upfront
* Subscription terms are clear
* Cancellation process explained
* No misleading claims
4. **App Content and Privacy**
* Privacy policy linked in Play Console
* Privacy policy accessible in app
* Ads declaration completed
* App access instructions added if the app is gated
* Data Safety section completed accurately
* Permissions justified and documented
5. **Content Rating and Audience**
* Complete content rating questionnaire
* Complete target audience and content section
* Ensure rating matches actual content
* Declare in-app purchases in questionnaire
6. **Prepare Store Listing**
* App description accurate
* Short description is within 80 characters
* Full description is within 4000 characters
* At least 2 phone screenshots uploaded
* 1024x500 feature graphic uploaded
* Screenshots show current version
* All required assets uploaded
## Review Timeline
[Section titled âReview Timelineâ](#review-timeline)
**Production Access for New Personal Accounts:** Usually 7 days or less after you apply **First Production Review:** Often several days, sometimes longer if billing or policy questions are raised **Updates:** Often faster than a first release, but still reviewed **Appeals:** Plan for several days and provide exact fixes and reviewer instructions
:::tip Rolling Reviews Unlike Apple, Google reviews apps continuously. Your app may go live at any time during the review period, not at a fixed time. :::
## Testing Before Submission
[Section titled âTesting Before Submissionâ](#testing-before-submission)
### License Testing
[Section titled âLicense Testingâ](#license-testing)
1. **Add Test Account:**
* Go to Play Console
* Settings > License testing
* Add Gmail account for testing
2. **Test in Sandbox:**
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Test purchases with license test account
async function testInSandbox() {
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) {
console.error('Billing not supported in this environment');
return;
}
// Fetch products (returns test pricing when using a license tester)
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['premium_monthly'],
productType: PURCHASE_TYPE.SUBS,
});
console.log('Test products:', products);
// Make test purchase (no charge)
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'premium_monthly',
planIdentifier: 'monthly-plan',
productType: PURCHASE_TYPE.SUBS,
});
console.log('Test purchase complete:', transaction.transactionId);
}
```
3. **Verify Test Banner:**
* When purchasing with test account
* Should see âTest purchaseâ notification
* No real charges occur
### Internal and Closed Testing Tracks
[Section titled âInternal and Closed Testing Tracksâ](#internal-and-closed-testing-tracks)
Before production release:
1. Create an **internal testing** track for fast QA or a **closed testing** track for broader testing
2. Upload a signed `.aab` and publish the testing release
3. Add tester email addresses and share the opt-in link
4. Have testers install the build from Google Play
5. Verify purchase flows work end-to-end on the Play-installed build
6. If your personal developer account was created after November 13, 2023, keep at least 12 testers opted in to a closed test for 14 consecutive days before applying for production
A sideloaded debug build is not a substitute for a Play-installed testing build when validating Google Play Billing.
## Best Practices for Native Purchases
[Section titled âBest Practices for Native Purchasesâ](#best-practices-for-native-purchases)
### Handle All Purchase States
[Section titled âHandle All Purchase Statesâ](#handle-all-purchase-states)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
async function handlePurchase(productId: string, planIdentifier?: string) {
try {
setLoading(true);
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: productId,
planIdentifier,
productType: planIdentifier ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP,
});
console.log('Purchase token:', transaction.purchaseToken ?? transaction.receipt);
// Success - check entitlements from the store
const { purchases } = await NativePurchases.getPurchases({
productType: planIdentifier ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP,
});
const isOwned = purchases.some(
(purchase) =>
purchase.productIdentifier === productId &&
(purchase.purchaseState === 'PURCHASED' || purchase.purchaseState === '1') &&
purchase.isAcknowledged,
);
if (isOwned) {
unlockPremiumFeatures();
showSuccess('Premium activated!');
}
} catch (error: any) {
// Handle specific error cases
switch (error.code) {
case 'USER_CANCELLED':
// User backed out - no error needed
console.log('Purchase cancelled');
break;
case 'ITEM_ALREADY_OWNED':
// They already own it - restore instead
showInfo('You already own this! Restoring...');
await NativePurchases.restorePurchases();
break;
case 'ITEM_UNAVAILABLE':
showError('This subscription is currently unavailable. Please try again later.');
break;
case 'NETWORK_ERROR':
showError('Network error. Please check your connection and try again.');
break;
default:
showError('Purchase failed. Please try again.');
console.error('Purchase error:', error);
}
} finally {
setLoading(false);
}
}
```
### Implement Restore Purchases
[Section titled âImplement Restore Purchasesâ](#implement-restore-purchases)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
function RestorePurchasesButton() {
const [loading, setLoading] = useState(false);
const handleRestore = async () => {
setLoading(true);
try {
await NativePurchases.restorePurchases();
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const hasSubscription = purchases.some(
(purchase) => purchase.productType === 'subs' && purchase.isAcknowledged,
);
if (hasSubscription) {
unlockPremiumFeatures();
showSuccess('Subscriptions restored!');
return;
}
// Check one-time unlocks if needed
const { purchases: iaps } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.INAPP,
});
const hasInApp = iaps.some((purchase) => purchase.productIdentifier === 'premium_unlock');
if (hasInApp) {
unlockPremiumFeatures();
showSuccess('Previous purchases restored!');
return;
}
showInfo('No previous purchases found.');
} catch (error) {
showError('Failed to restore purchases. Please try again.');
} finally {
setLoading(false);
}
};
return (
{loading ? 'Restoring...' : 'Restore Purchases'}
);
}
```
### Check Subscription Status
[Section titled âCheck Subscription Statusâ](#check-subscription-status)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
async function checkSubscriptionStatus() {
try {
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const subscription = purchases.find(
(purchase) =>
purchase.productIdentifier === 'premium_monthly' &&
(purchase.purchaseState === 'PURCHASED' || purchase.purchaseState === '1') &&
purchase.isAcknowledged,
);
if (!subscription) {
showPaywall();
return;
}
console.log('Subscription active:', {
productId: subscription.productIdentifier,
expiresAt: subscription.expirationDate,
willRenew: subscription.willCancel === false,
purchaseToken: subscription.purchaseToken,
});
unlockPremiumFeatures();
} catch (error) {
console.error('Failed to check subscription:', error);
}
}
```
## If Your App Gets Rejected
[Section titled âIf Your App Gets Rejectedâ](#if-your-app-gets-rejected)
### Common Policy Violations
[Section titled âCommon Policy Violationsâ](#common-policy-violations)
**Payments Policy:**
* Not using Google Play Billing
* Misleading subscription terms
* Hidden costs
**User Data Policy:**
* Missing privacy policy
* Inaccurate Data Safety declarations
* Excessive permissions
### Resolution Steps
[Section titled âResolution Stepsâ](#resolution-steps)
1. **Review the Violation Notice**
* Read the specific policy cited
* Understand what Google flagged
* Check examples they provided
2. **Fix the Issue**
* Address root cause, not just symptoms
* Test thoroughly after fix
* Document all changes made
3. **Submit Appeal (if applicable)**

```plaintext
Subject: Policy Violation Appeal - [App Name]
Dear Google Play Review Team,
I have received notification that my app violates [Policy X.Y].
I have made the following changes to comply:
1. [Specific change made]
2. [Specific change made]
3. [Specific change made]
The updated version [version number] addresses all concerns raised.
Test account for verification:
Email: test@example.com
Password: TestPass123
Thank you for your consideration.
```

4. **Resubmit or Update**
* Upload fixed version
* Resubmit for review
* Monitor status in Play Console
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
* [Google Play Developer Policy Center](https://play.google.com/about/developer-content-policy/)
* [Google Play Billing Documentation](https://developer.android.com/google/play/billing)
* [Subscriptions Best Practices](https://developer.android.com/google/play/billing/subscriptions)
* [Prepare Your App for Review](https://support.google.com/googleplay/android-developer/answer/9859455)
* [Testing Requirements for New Personal Accounts](https://support.google.com/googleplay/android-developer/answer/14151465)
* [Play Console Help](https://support.google.com/googleplay/android-developer/)
## Need Expert Help?
[Section titled âNeed Expert Help?â](#need-expert-help)
Navigating Play Store review can be complex, especially when you need to combine billing compliance, App content declarations, and testing-track setup. If you need personalized assistance:
**[Book a consultation call with our team](https://book.capgo.app/consulting-services/)** for help with:
* Complete Play Store review preparation
* Testing track setup and tester recruitment
* IAP implementation review
* Data Safety and privacy compliance
* Rejection troubleshooting and appeals
* Complete app submission process
Our experts have guided hundreds of apps through successful Play Store submissions and can help you navigate the current requirements.
## Support
[Section titled âSupportâ](#support)
Need help with implementation?
* Review the [Native Purchases documentation](/docs/plugins/native-purchases/getting-started/)
* Check [Android sandbox testing guide](/docs/plugins/native-purchases/android-sandbox-testing/)
* Visit [Google Play Developer Support](https://support.google.com/googleplay/android-developer/)
## Keep going from Android Play Store Review Guidelines for IAP
[Section titled âKeep going from Android Play Store Review Guidelines for IAPâ](#keep-going-from-android-play-store-review-guidelines-for-iap)
If you are using **Android Play Store Review Guidelines for IAP** to plan security and compliance, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# Configure Android Sandbox Testing
> Learn how to set up sandbox testing for in-app purchases on Android using Google Play Console.
Testing in-app purchases requires proper configuration in Google Play Console. This guide will walk you through setting up sandbox testing for your Android app.
## Use the right build for the job
[Section titled âUse the right build for the jobâ](#use-the-right-build-for-the-job)
Before you start, separate these three Android build types:
* **Local debug/dev build**: Good for checking UI and native integrations on your device.
* **Signed release AAB uploaded to Play Console**: Required for realistic Google Play billing tests.
* **Play-installed testing build**: The build your testers install from an internal or closed track. Use this for purchase QA.
If you only sideload an APK from Android Studio or `adb`, Google Play Billing may not behave the same way it does in production. For subscription and in-app purchase validation, always test with a build installed from Google Play.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
* A Google Play Console developer account. If you have not created one yet, start at [Play Console signup](https://play.google.com/console/signup).
* An app record created in Play Console with your final Android package name
* Your in-app products or subscriptions created in Play Console
* A test Gmail account that you can add to license testing
* A signed release build ready for upload
Before you spend time on billing setup, create the app in Play Console and decide whether it will be **free** or **paid**. Google lets you move a paid app to free later, but once an app has been offered for free, it cannot be switched to paid.
Use an Android App Bundle (`.aab`) for new Play Store apps:
```bash
bunx cap sync android
cd android
./gradlew bundleRelease
```
Make sure your Android release signing is already configured before you run `bundleRelease`. If your keystore, signing config, or release passwords are not set yet, create the signed bundle from Android Studio with **Build > Generate Signed App Bundle / APK**, which prompts you for those values.
## Setup Process
[Section titled âSetup Processâ](#setup-process)
1. **Add Testing Account**
In Play Console, open **Settings > License testing** and add the primary Google account used on your Android test device.
This ensures purchases show the Play sandbox flow instead of attempting a real charge.

2. **Choose a Testing Track**
Go to **Test and release > Testing** and choose one of these tracks:
* **Internal testing**: Fastest path for QA and billing smoke tests. New bundles are normally available within minutes.
* **Closed testing**: Better for broader testing, and required before production for personal developer accounts created after November 13, 2023.
For a first release, Play may show a temporary app name and listing information to internal testers for up to 48 hours.

3. **Create Tester List**
After opening your track, create a tester list and add the Google accounts that should receive the build.
If you are working toward production access on a newly created personal account, make sure you use **closed testing** and keep at least 12 testers opted in for 14 consecutive days.

4. **Upload a Signed Release Build**
Create a new release in the selected testing track and upload your signed `.aab`.
New Google Play apps should use an Android App Bundle rather than an APK. After upload, save the release, fix any policy or store listing blockers Play flags, then publish the release to the testing track.

5. **Join the Testing Program**
Open the opt-in URL from your test device and click the **âBecome a testerâ** button to enroll.
Install the app from the Play Store listing created by that opt-in flow, not from a locally exported APK.

6. **Build and Test**
Launch the Play-installed build on the test device and attempt a purchase. You should see a message like:
> âThis is a test order; you will not be charged.â

## Important Notes
[Section titled âImportant Notesâ](#important-notes)
* For billing QA, uninstall any sideloaded copy of the app before installing the Play testing build.
* Internal testing is great for fast smoke tests, but closed testing is the track that matters for new personal-account production access.
* Test accounts will not be charged for purchases
* Test purchases use the same flow as production purchases
* You can test all subscription features including trials and introductory offers
* Test subscriptions have accelerated renewal periods for faster testing
* Use the same Google account for all three places: the Play Store on the device, the tester opt-in flow, and License testing
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Products not showing in test mode:**
* Ensure your app is uploaded to a testing track
* Verify the test account is added to License testing
* Check that products are active in Google Play Console
* Confirm the build was installed from Google Play, not sideloaded locally
**âItem not availableâ error:**
* Wait 2-3 hours after creating products for them to become available
* Ensure your appâs package name matches the one in Play Console
* Verify youâre signed in with a test account
* Confirm you uploaded the signed release build that points at the same package name and product catalog
**Test purchases showing as real charges:**
* Double-check the account is added to License testing
* Ensure youâre using the build from the testing track
* Verify the testing banner appears during purchase
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
* [Test purchases with Google Play Billing](https://developer.android.com/google/play/billing/test)
* [Set up an open, closed, or internal test](https://support.google.com/googleplay/android-developer/answer/9845334)
* [Testing requirements for new personal developer accounts](https://support.google.com/googleplay/android-developer/answer/14151465)
## Keep going from Configure Android Sandbox Testing
[Section titled âKeep going from Configure Android Sandbox Testingâ](#keep-going-from-configure-android-sandbox-testing)
If you are using **Configure Android Sandbox Testing** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# Getting Started
> Learn how to install and use the @capgo/native-purchases plugin to implement one-time purchases and subscriptions with StoreKit 2 and Google Play Billing 7.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/native-purchases` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```sh
bun add @capgo/native-purchases
```
2. **Sync with native projects**
```sh
bunx cap sync
```
3. **Check billing support**
```typescript
import { NativePurchases } from '@capgo/native-purchases';
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) {
throw new Error('Billing is not available on this device');
}
```
4. **Load products directly from the stores**
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { products } = await NativePurchases.getProducts({
productIdentifiers: [
'com.example.premium.monthly',
'com.example.premium.yearly',
'com.example.one_time_unlock'
],
productType: PURCHASE_TYPE.SUBS, // Use PURCHASE_TYPE.INAPP for oneâtime products
});
products.forEach((product) => {
console.log(product.title, product.priceString);
});
```
5. **Implement purchase & restore flows**
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const monthlyPlanId = 'monthly-plan'; // Base Plan ID from Google Play Console
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.example.premium.monthly',
planIdentifier: monthlyPlanId, // REQUIRED for Android subscriptions, ignored on iOS
productType: PURCHASE_TYPE.SUBS,
quantity: 1,
});
console.log('Transaction ID', transaction.transactionId);
await NativePurchases.restorePurchases();
```
iOS monthly commitment plans
If a supported iOS subscription returns `pricingTerms`, you can show a monthly billing option with a 12-month commitment and pass `billingPlanType: 'monthly'` to `purchaseProduct()`. See [iOS monthly commitment billing plans](/docs/plugins/native-purchases/ios-monthly-commitment/) for the full flow.
* iOS
* Create in-app products and subscriptions in App Store Connect.
* Use StoreKit Local Testing or Sandbox testers for QA.
* No manifest edits required. Make sure your products are approved.
* Android
* Create in-app products and subscriptions in Google Play Console.
* Upload at least an internal test build and add license testers.
* Add the billing permission to `AndroidManifest.xml`:
```xml
```
## Purchase service example
[Section titled âPurchase service exampleâ](#purchase-service-example)
```typescript
import { NativePurchases, PURCHASE_TYPE, Transaction } from '@capgo/native-purchases';
import { Capacitor } from '@capacitor/core';
class PurchaseService {
private premiumProduct = 'com.example.premium.unlock';
private monthlySubId = 'com.example.premium.monthly';
private monthlyPlanId = 'monthly-plan'; // Base Plan ID (Android only)
async initialize() {
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) throw new Error('Billing unavailable');
const { products } = await NativePurchases.getProducts({
productIdentifiers: [this.premiumProduct, this.monthlySubId],
productType: PURCHASE_TYPE.SUBS,
});
console.log('Loaded products', products);
if (Capacitor.getPlatform() === 'ios') {
NativePurchases.addListener('transactionUpdated', (transaction) => {
this.handleTransaction(transaction);
});
}
}
async buyPremium(appAccountToken?: string) {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: this.premiumProduct,
productType: PURCHASE_TYPE.INAPP,
appAccountToken,
});
await this.processTransaction(transaction);
}
async buyMonthly(appAccountToken?: string) {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: this.monthlySubId,
planIdentifier: this.monthlyPlanId, // REQUIRED for Android subscriptions
productType: PURCHASE_TYPE.SUBS,
appAccountToken,
});
await this.processTransaction(transaction);
}
async restore() {
await NativePurchases.restorePurchases();
await this.refreshEntitlements();
}
async openManageSubscriptions() {
await NativePurchases.manageSubscriptions();
}
private async processTransaction(transaction: Transaction) {
this.unlockContent(transaction.productIdentifier);
this.validateOnServer(transaction).catch(console.error);
}
private unlockContent(productIdentifier: string) {
// persist entitlement locally
console.log('Unlocked', productIdentifier);
}
private async refreshEntitlements() {
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
console.log('Current purchases', purchases);
}
private async handleTransaction(transaction: Transaction) {
console.log('StoreKit transaction update:', transaction);
await this.processTransaction(transaction);
}
private async validateOnServer(transaction: Transaction) {
await fetch('/api/validate-purchase', {
method: 'POST',
body: JSON.stringify({
transactionId: transaction.transactionId,
receipt: transaction.receipt,
purchaseToken: transaction.purchaseToken,
}),
});
}
}
```
## Required purchase options
[Section titled âRequired purchase optionsâ](#required-purchase-options)
| Option | Platform | Description |
| ------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `productIdentifier` | iOS + Android | SKU/Product ID configured in App Store Connect / Google Play Console. |
| `productType` | Android only | `PURCHASE_TYPE.INAPP` or `PURCHASE_TYPE.SUBS`. Defaults to `INAPP`. Always set to `SUBS` for subscriptions. |
| `planIdentifier` | Android subscriptions | Base Plan ID from Google Play Console. Required for subscriptions, ignored on iOS and in-app purchases. |
| `billingPlanType` | iOS subscriptions | StoreKit billing plan to purchase. Use `'monthly'` for monthly billing with a 12-month commitment when `product.pricingTerms` exposes that option. |
| `quantity` | iOS | Only for in-app purchases, defaults to `1`. Android always purchases one item. |
| `appAccountToken` | iOS + Android | UUID/string linking the purchase to your user. Required to be UUID on iOS; Android accepts any obfuscated string up to 64 chars. |
| `isConsumable` | Android | Set to `true` to auto-consume tokens after granting entitlement for consumables. Defaults to `false`. |
## Checking entitlement status
[Section titled âChecking entitlement statusâ](#checking-entitlement-status)
Use `getPurchases()` for a cross-platform view of every transaction the stores report:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
purchases.forEach((purchase) => {
if (purchase.isActive && purchase.expirationDate) {
console.log('iOS sub active until', purchase.expirationDate);
}
const isAndroidIapValid =
['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged;
if (isAndroidIapValid) {
console.log('Grant in-app entitlement for', purchase.productIdentifier);
}
});
```
### Platform behavior
[Section titled âPlatform behaviorâ](#platform-behavior)
* **iOS**: Subscriptions include `isActive`, `expirationDate`, `willCancel`, and StoreKit 2 listener support. In-app purchases require server receipt validation.
* **Android**: `isActive`/`expirationDate` are not populated; call the Google Play Developer API with the `purchaseToken` for authoritative status. `purchaseState` must be `PURCHASED` and `isAcknowledged` must be `true`.
## API quick reference
[Section titled âAPI quick referenceâ](#api-quick-reference)
* `isBillingSupported()` â check for StoreKit / Google Play availability.
* `getProduct()` / `getProducts()` â fetch price, localized title, description, intro offers, and supported iOS pricing terms.
* `purchaseProduct()` â initiate StoreKit 2 or Billing client purchase flow, including iOS monthly commitment billing plans.
* `restorePurchases()` â replay historical purchases and sync to current device.
* `getPurchases()` â list all iOS transactions or Play Billing purchases.
* `manageSubscriptions()` â open the native subscription management UI.
* `addListener('transactionUpdated')` â handle pending StoreKit 2 transactions when your app starts (iOS only).
## Best practices
[Section titled âBest practicesâ](#best-practices)
1. **Show store pricing** â Apple requires displaying `product.title` and `product.priceString`; never hardcode.
2. **Use `appAccountToken`** â deterministically generate a UUID (v5) from your user ID to link purchases to accounts.
3. **Validate server-side** â send `receipt` (iOS) / `purchaseToken` (Android) to your backend for verification.
4. **Handle errors gracefully** â check for user cancellations, network failures, and unsupported billing environments.
5. **Test thoroughly** â follow the [iOS sandbox guide](/docs/plugins/native-purchases/ios-sandbox-testing/) and [Android sandbox guide](/docs/plugins/native-purchases/android-sandbox-testing/).
6. **Offer restore & management** â add UI buttons wired to `restorePurchases()` and `manageSubscriptions()`.
## Revenue next steps
[Section titled âRevenue next stepsâ](#revenue-next-steps)
After the purchase flow works, use the [Revenue Playbook](/docs/plugins/native-purchases/revenue-playbook/) to plan your first paid funnel: product scope, ASO, pricing, paywall placement, analytics, and churn feedback.
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Products not loading**
* Make sure the bundle ID / application ID matches store configuration.
* Confirm the product IDs are active and approved (App Store) or activated (Google Play).
* Wait several hours after creating products; store propagation is not instant.
**Purchase cancelled or stuck**
* Users can cancel mid-flow; wrap calls in `try/catch` and surface friendly error messages.
* For Android, ensure test accounts install the app from Play Store (internal track) so Billing works.
* Check logcat/Xcode for billing errors when running on device.
**Subscription state incorrect**
* Use `getPurchases()` to compare store data with your local entitlement cache.
* On Android, always query the Google Play Developer API with the `purchaseToken` to obtain expiration dates or refund status.
* On iOS, check `isActive`/`expirationDate` and validate receipts to detect refunds or revocations.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# iOS App Store Review Guidelines for IAP
> Complete guide to passing App Store review with in-app purchases and subscriptions, including common rejection reasons and best practices.
Getting your app approved on the App Store requires careful attention to Appleâs guidelines, especially when implementing in-app purchases and subscriptions. This guide covers everything you need to know to pass review on your first submission.

## Before You Submit
[Section titled âBefore You Submitâ](#before-you-submit)
### Finish App Store Connect Setup
[Section titled âFinish App Store Connect Setupâ](#finish-app-store-connect-setup)
Before Apple reviews your purchase flow, make sure the app record itself is complete:
* Add a **Privacy Policy URL** in App Store Connect
* Add a **Support URL** that leads to real contact information for users
* Complete the **age rating** questionnaire so the app is publishable
* Add **App Review contact details** and clear reviewer notes
* If login is required, provide a **demo account that does not expire during review**
Note
Apple treats the support site as more than a placeholder link. It should contain real contact details so users can reach you about app issues, feedback, and feature requests.

### Prepare Real Screenshots
[Section titled âPrepare Real Screenshotsâ](#prepare-real-screenshots)
* Use current screenshots from the actual build under review
* For iPhone, `1290 x 2796` (6.7-inch) is the easiest default size
* If your app runs on iPad, upload iPad screenshots too
* Current accepted large iPad sizes include `2064 x 2752` (13-inch) and `2048 x 2732` (12.9-inch)
* Never stretch iPhone screenshots to fake iPad support
### Dry-Run the Reviewer Journey in TestFlight
[Section titled âDry-Run the Reviewer Journey in TestFlightâ](#dry-run-the-reviewer-journey-in-testflight)
Run the exact path Apple will follow on a real device:
* Install the latest build from TestFlight
* Sign in with the review account you plan to provide
* Reach the paywall without hidden gestures or debug menus
* Complete purchase, restore, and manage-subscription flows
* Verify the app still behaves correctly if permissions are denied
## In-App Purchase Requirements
[Section titled âIn-App Purchase Requirementsâ](#in-app-purchase-requirements)
### Pricing Transparency (Critical)
[Section titled âPricing Transparency (Critical)â](#pricing-transparency-critical)
Apple requires crystal-clear pricing disclosure before any purchase:
**Must-Have Elements:**
* Display exact price before purchase button
* Show billing frequency (e.g., â$9.99/monthâ)
* Clearly state what users get for their money
* Indicate when charges will occur
**Common Rejection:**
> âSubscription pricing must be clear and upfront.â
:::caution Price Consistency All prices must match across:
* App Store metadata listing
* In-app purchase screens
* Subscription management screens
Even a $1 discrepancy between store listing ($4.99) and app ($5.99) will trigger automatic rejection. :::
### Subscription Plan Presentation
[Section titled âSubscription Plan Presentationâ](#subscription-plan-presentation)
**Required Disclosures:**
* All available subscription tiers displayed together
* Clear comparison of features per tier
* No auto-defaulting to premium tiers through UI tricks
* Easy-to-locate cancellation instructions


**Example of Compliant UI:**
```typescript
import { NativePurchases } from '@capgo/native-purchases';
function SubscriptionScreen() {
return (
Choose Your Plan
{/* Show all tiers equally */}
{/* Clear cancellation info */}
Cancel anytime in Settings > Subscriptions.
No refunds for partial periods.
);
}
```
### Restore Purchases
[Section titled âRestore Purchasesâ](#restore-purchases)
**Required Implementation:**
Every app with IAP must provide a way for users to restore previous purchases without contacting support.
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
async function restorePurchases() {
try {
await NativePurchases.restorePurchases();
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const activeSub = purchases.find(
(purchase) => purchase.isActive && purchase.expirationDate,
);
if (activeSub) {
unlockPremiumFeatures();
showMessage('Purchases restored successfully!');
return;
}
const { purchases: iaps } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.INAPP,
});
const hasIap = iaps.some((purchase) => purchase.productIdentifier === 'premium_unlock');
showMessage(
hasIap ? 'Premium purchase restored!' : 'No previous purchases found.',
);
} catch (error) {
showError('Failed to restore purchases. Please try again.');
}
}
// Add a visible "Restore Purchases" button
Restore Purchases
```
## Common Rejection Reasons
[Section titled âCommon Rejection Reasonsâ](#common-rejection-reasons)
### 1. App Crashes or Broken Functionality
[Section titled â1. App Crashes or Broken Functionalityâ](#1-app-crashes-or-broken-functionality)
**Why It Fails:**
* App crashes on launch
* Purchase flow fails to complete
* Features shown in screenshots donât work
**Prevention:**
* Test on real devices (not just simulators)
* Test all subscription flows end-to-end
* Verify receipt validation works
* Check network error handling
### 2. Metadata Mismatches
[Section titled â2. Metadata Mismatchesâ](#2-metadata-mismatches)
**Why It Fails:**
* Screenshots show features not in current build
* Description mentions functionality that doesnât exist
* Pricing in metadata differs from in-app pricing

**Prevention:**
```typescript
// Document exactly what's in each tier
const SUBSCRIPTION_FEATURES = {
basic: ['Ad-free', 'Cloud sync', 'Basic themes'],
premium: ['Ad-free', 'Cloud sync', 'All themes', 'Priority support']
};
// Use these in both your app AND App Store description
```
### 3. Missing Permission Explanations
[Section titled â3. Missing Permission Explanationsâ](#3-missing-permission-explanations)
**Why It Fails:**
* Requesting camera/location/health without explanation
* Permission requests buried multiple screens deep
* Vague or generic permission descriptions
**Prevention:**
Update your `Info.plist` with clear explanations:
 
```xml
NSCameraUsageDescription
Camera access is needed to scan product barcodes for quick subscription upgrades.
NSLocationWhenInUseUsageDescription
Location helps us show relevant local content in your Premium subscription.
```
### 4. Misleading Marketing
[Section titled â4. Misleading Marketingâ](#4-misleading-marketing)
**Why It Fails:**
* Claims like â#1 app in worldâ without proof
* âUnlimitedâ features that have hidden limits
* Fake urgency tactics (âOnly 2 spots left!â)


**Prevention:**
* Be specific and factual in descriptions
* Avoid superlatives without evidence
* Donât pressure users with fake scarcity
### 5. Hidden Cancellation Process
[Section titled â5. Hidden Cancellation Processâ](#5-hidden-cancellation-process)
**Why It Fails:**
* No mention of how to cancel
* Cancellation button hidden or obscured
* Multi-step cancellation process without Appleâs native flow
**Prevention:**
```typescript
// Always inform users about cancellation
function SubscriptionInfo() {
return (
How to Cancel
Open iPhone Settings
Tap your name at the top
Tap Subscriptions
Select this app and tap Cancel
Or manage directly in the App Store app.
Manage Subscription in Settings
);
}
async function openSubscriptionManagement() {
// Direct link to iOS subscription management
await NativePurchases.showManageSubscriptions();
}
```
## Privacy & Data Usage (Section 5.1.1)
[Section titled âPrivacy & Data Usage (Section 5.1.1)â](#privacy--data-usage-section-511)
Apple has significantly tightened privacy requirements in 2025.
### Required Disclosures
[Section titled âRequired Disclosuresâ](#required-disclosures)
**For Every Permission:**
1. Why you need it (specific use case)
2. When it will be used
3. How data is stored/shared
4. Whether itâs optional or required
### Example: Proper Permission Flow
[Section titled âExample: Proper Permission Flowâ](#example-proper-permission-flow)
```typescript
async function requestCameraPermission() {
// Show explanation BEFORE requesting
await showDialog({
title: 'Camera Access',
message: 'We need camera access to let you scan barcodes for quick product lookup. Your photos are never uploaded or stored.',
buttons: ['Not Now', 'Allow']
});
// Then request permission
const result = await Camera.requestPermissions();
return result.camera === 'granted';
}
```
### Privacy Nutrition Labels
[Section titled âPrivacy Nutrition Labelsâ](#privacy-nutrition-labels)
Ensure your App Store privacy labels accurately reflect:
* Purchase history collection
* Email addresses (for receipts)
* Device IDs (for fraud prevention)
* Usage data (for analytics)
Inaccurate privacy labels are a common rejection reason in 2025. Audit your data collection carefully.
## Pre-Submission Checklist
[Section titled âPre-Submission Checklistâ](#pre-submission-checklist)

1. **Test All Purchase Flows**
* Buy each subscription tier
* Test free trials
* Verify introductory offers apply correctly
* Test restore purchases
* Verify Family Sharing (if enabled)
* Test on multiple devices
2. **Verify Pricing Consistency**
* Check App Store metadata matches in-app prices
* Verify all currencies are correct
* Confirm free trial durations match descriptions
* Check introductory offer terms are accurate
3. **Review All Copy**
* Remove placeholder text
* Verify claims are testable
* Check grammar and spelling
* Ensure descriptions match current build
* Remove competitor mentions
4. **Test Permissions**
* Request only necessary permissions
* Show clear explanations before requesting
* Test âDenyâ flows (app should still work)
* Verify Info.plist descriptions are clear
5. **Prepare Test Account**
* Create a review account that remains valid during review
* Document login credentials in App Review information
* Verify the reviewer can reach the paywall and complete the purchase flow
* Include extra accounts or app-specific switches in the Notes field if needed
6. **Check Metadata**
* Screenshots match current UI
* Support URL includes real contact information
* Privacy policy URL is filled in
* Age rating matches the content in the build
* App preview video (if any) shows current version
* Description accurately describes features
* Privacy policy is accessible in-app and from the store listing
7. **Write Detailed Review Notes**
```plaintext
Contact:
Name: Jane Developer
Email: review@yourapp.com
Phone: +1 555-0100
Test Account:
Email: reviewer@test.com
Password: TestPass123!
This account does not expire during review.
Testing Instructions:
1. Log in with test account above
2. Tap "Upgrade to Premium" button
3. Select "Monthly Premium" subscription
4. Complete purchase (no charge in sandbox)
5. Verify premium features unlock
Note: Subscription pricing is clearly shown before purchase.
Cancellation instructions are in Settings > Account.
```
## Review Timeline
[Section titled âReview Timelineâ](#review-timeline)

**Standard Review:** 24-48 hours **Peak Periods:** 3-5 days (App Store holiday releases) **Weekends:** No reviews processed **Expedited Review:** Available for critical bug fixes (request via App Store Connect)
Common statuses you will see in App Store Connect:
* `Waiting for Review`
* `In Review`
* `Pending Developer Release`
* `Rejected`
Tip
Submit early in the week to avoid weekend delays. Monday submissions typically get reviewed by Wednesday.
## 2026 Submission Focus
[Section titled â2026 Submission Focusâ](#2026-submission-focus)
### Current Focus Areas
[Section titled âCurrent Focus Areasâ](#current-focus-areas)
**1. Subscription Clarity**
* Side-by-side plan comparisons required
* No âdark patternsâ that hide cheaper options
* Clear downgrade/upgrade paths
**2. Metadata Accuracy**
* Screenshots must match the build being reviewed
* iPad screenshots are required if iPad support is enabled
* Support URL and privacy policy should already be live before submission
**3. Privacy and Review Detail Quality**
* Privacy disclosures must match what your SDKs actually collect
* App Review contact info and notes should be complete on the first submission
* Demo credentials must stay valid for the full review window
**4. Submission Readiness**
* Apple updates minimum SDK requirements regularly, so confirm the current deadline before uploading a release build
* TestFlight is the safest place to verify the exact reviewer path before you submit
## Best Practices for Native Purchases Plugin
[Section titled âBest Practices for Native Purchases Pluginâ](#best-practices-for-native-purchases-plugin)
### Implement Proper Error Handling
[Section titled âImplement Proper Error Handlingâ](#implement-proper-error-handling)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
async function handlePurchase(productId: string) {
try {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: productId,
productType: PURCHASE_TYPE.SUBS,
});
// Success
await validateReceiptOnServer(transaction.receipt);
showSuccess('Subscription activated!');
unlockFeatures();
} catch (error: any) {
// Handle specific error cases
if (error.code === 'USER_CANCELLED') {
// User cancelled - don't show error
console.log('Purchase cancelled by user');
} else if (error.code === 'PAYMENT_PENDING') {
showInfo('Payment is pending. Please check back later.');
} else if (error.code === 'PRODUCT_ALREADY_PURCHASED') {
// Restore instead
await NativePurchases.restorePurchases();
} else {
// Show user-friendly error
showError('Unable to complete purchase. Please try again.');
}
}
}
```
### Display Loading States
[Section titled âDisplay Loading Statesâ](#display-loading-states)
```typescript
function PurchaseButton({ productId }: { productId: string }) {
const [loading, setLoading] = useState(false);
const handlePurchase = async () => {
setLoading(true);
try {
await NativePurchases.purchaseProduct({ productIdentifier: productId });
} finally {
setLoading(false);
}
};
return (
{loading ? 'Processing...' : 'Subscribe Now'}
);
}
```
### Show Terms Clearly
[Section titled âShow Terms Clearlyâ](#show-terms-clearly)
```typescript
function SubscriptionTerms() {
return (
Subscription automatically renews unless cancelled at least 24 hours
before the end of the current period.
Your account will be charged for renewal within 24 hours prior to
the end of the current period.
Subscriptions may be managed by the user and auto-renewal may be
turned off in Account Settings after purchase.
Terms of Service |
Privacy Policy
);
}
```
## If Your App Gets Rejected
[Section titled âIf Your App Gets Rejectedâ](#if-your-app-gets-rejected)
### Steps to Resolve
[Section titled âSteps to Resolveâ](#steps-to-resolve)
1. **Read the rejection carefully**
* Note the specific guideline cited (e.g., 3.1.1, 5.1.1)
* Understand exactly what Apple flagged
2. **Fix the issue thoroughly**
* Donât just patch - fix root cause
* Test the fix extensively
* Document what you changed
3. **Respond in Resolution Center**
```plaintext
Thank you for your feedback. I have addressed the issue:
Issue: Subscription pricing not clear upfront
Fix: Added explicit pricing display on subscription selection
screen showing "$9.99/month" before purchase button. Also added
cancellation instructions on the same screen.
The changes are in this submission and can be tested using the
provided test account.
```
4. **Resubmit promptly**
* Resubmissions are typically reviewed faster
* Usually within 24 hours
### Appeal Process
[Section titled âAppeal Processâ](#appeal-process)
If you believe the rejection is incorrect:

1. Click âAppealâ in App Store Connect
2. Provide clear evidence:
* Screenshots showing compliance
* References to specific guidelines
* Explanation of how you meet requirements
3. Be professional and factual
4. Include test account if functionality is hard to find

## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
* [Apple App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/)
* [In-App Purchase Guidelines](https://developer.apple.com/app-store/review/guidelines/#in-app-purchase)
* [Subscriptions Best Practices](https://developer.apple.com/app-store/subscriptions/)
* [App Store Connect Help](https://developer.apple.com/help/app-store-connect/)
* [Screenshot Specifications](https://developer.apple.com/help/app-store-connect/reference/app-information/screenshot-specifications)
* [Platform Version Information](https://developer.apple.com/help/app-store-connect/reference/app-information/platform-version-information)
## Support
[Section titled âSupportâ](#support)
If youâre still having issues:
* Review the [Native Purchases documentation](/docs/plugins/native-purchases/getting-started/)
* Check [common troubleshooting issues](/docs/plugins/native-purchases/getting-started/#troubleshooting)
* Contact Apple Developer Support for guideline clarifications
### Need Expert Help?
[Section titled âNeed Expert Help?â](#need-expert-help)
Struggling with app review or need personalized assistance? **[Book a consultation call with our team](https://book.capgo.app/consulting-services/)** for dedicated support with:
* IAP implementation review and optimization
* App Store review preparation and strategy
* Submission checklist review
* Rejection resolution and appeals
* Complete testing and validation
Our experts have successfully helped hundreds of apps pass review!
## Keep going from iOS App Store Review Guidelines for IAP
[Section titled âKeep going from iOS App Store Review Guidelines for IAPâ](#keep-going-from-ios-app-store-review-guidelines-for-iap)
If you are using **iOS App Store Review Guidelines for IAP** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# Create iOS Auto-Renewable Subscription
> Step-by-step guide to creating auto-renewable subscriptions in App Store Connect for the native-purchases plugin.
Auto-renewable subscriptions provide recurring access to content, services, or premium features in your iOS app. This guide walks you through creating subscriptions in App Store Connect.
## Overview
[Section titled âOverviewâ](#overview)
Auto-renewable subscriptions automatically renew at the end of each billing period until users cancel. Theyâre perfect for:
* Premium content and features
* Ad-free experiences
* Cloud storage and sync
* Streaming services
* Professional tools and utilities
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
Before creating subscriptions, you must:
1. [Create a subscription group](/docs/plugins/native-purchases/ios-subscription-group/) to organize your subscriptions
2. Have an active Apple Developer Program membership
3. Complete banking and tax information in App Store Connect
## Creating a Subscription
[Section titled âCreating a Subscriptionâ](#creating-a-subscription)
1. **Navigate to Subscriptions**
In App Store Connect, select your app and go to **Monetize > Subscriptions**.
Select your subscription group or create a new one if needed.

2. **Create New Subscription**
Click the **+** icon next to your subscription group to add a new subscription.
3. **Enter Basic Information**
**Reference Name**: Descriptive name for your internal use (not shown to customers)
* Examples: âPremium Monthlyâ, âUltimate Annualâ, âBasic Planâ
**Product ID**: Unique identifier for this subscription (cannot be changed later)
* Format: `com.yourcompany.yourapp.premium_monthly`
* Use descriptive, lowercase names with underscores
* Required for configuring the native-purchases plugin

4. **Configure Duration**
Select the subscription duration from available options:
* 1 week
* 1 month
* 2 months
* 3 months
* 6 months
* 1 year
The duration determines how often users are billed.
5. **Set Up Pricing**
Click **Add Subscription Price** to configure pricing:
**Base Territory**: Select your primary market (usually your country)
**Price**: Set the subscription price
* Apple automatically converts to other currencies
* Choose from Appleâs price tiers
* Consider perceived value and market rates

6. **Family Sharing (Optional)**
Decide whether to enable Family Sharing, which allows up to 6 family members to access the subscription.
Caution
Once Family Sharing is enabled, it cannot be turned off for this product.
**Enable if:**
* Content is appropriate for family use
* You want to increase value proposition
* Your business model supports it
**Donât enable if:**
* Subscription is for individual use only
* Content is personalized to the user
* You want to maximize revenue per user
7. **Add Localizations**
Add subscription display information in all languages your app supports:
**Subscription Display Name**: Customer-facing name (e.g., âPremium Monthlyâ)
**Description**: Brief description of what the subscription includes
* Keep it concise and benefit-focused
* Mention key features
* Highlight value proposition

8. **App Store Promotional Image (Optional)**
Upload a promotional image for this subscription (312x390 pixels):
* Shows in the App Store subscription page
* Should match your appâs design
* Include subscription name for clarity
Note
While images are optional for initial submission, theyâre required for promotional display in the App Store. You can add them later.
9. **Save and Submit**
Click **Save** to create the subscription.
**For First Subscription:**
* Must be submitted with a new app version
* Include in your next App Store submission
* Cannot submit independently
**For Subsequent Subscriptions:**
* Can be submitted directly from the Subscriptions page
* Donât require a new app version
* Available after first subscription is approved
## Subscription Status
[Section titled âSubscription Statusâ](#subscription-status)
Your subscription will have one of these statuses:
| Status | Description | Can Test? |
| ---------------------- | -------------------------- | ------------- |
| **Missing Metadata** | Incomplete setup | Yes (sandbox) |
| **Ready to Submit** | Complete but not submitted | Yes (sandbox) |
| **Waiting for Review** | Submitted to Apple | Yes (sandbox) |
| **In Review** | Being reviewed by Apple | Yes (sandbox) |
| **Approved** | Available for purchase | Yes |
| **Rejected** | Needs changes | Yes (sandbox) |
Tip
You can test subscriptions in sandbox mode even if they show âMissing Metadataâ or âReady to Submitâ!
## Using in Your App
[Section titled âUsing in Your Appâ](#using-in-your-app)
Once created, reference the subscription in your app using the product ID:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Fetch subscription products direct from StoreKit
const { products } = await NativePurchases.getProducts({
productIdentifiers: [
'com.yourcompany.yourapp.premium_monthly',
'com.yourcompany.yourapp.premium_annual',
],
productType: PURCHASE_TYPE.SUBS,
});
products.forEach((product) => {
console.log(`${product.title}: ${product.priceString}`);
console.log(`Duration: ${product.subscriptionPeriod}`);
console.log(`Description: ${product.description}`);
});
// Purchase a subscription (StoreKit 2 automatically handles intro pricing and offers)
try {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.yourcompany.yourapp.premium_monthly',
productType: PURCHASE_TYPE.SUBS,
});
console.log('Transaction ID:', transaction.transactionId);
// StoreKit receipts are included on iOS for server-side validation
await sendReceiptToBackend(transaction.receipt);
} catch (error) {
console.error('Purchase failed:', error);
}
// Check subscription status using the store's data
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const premium = purchases.find(
(purchase) => purchase.productIdentifier === 'com.yourcompany.yourapp.premium_monthly',
);
if (premium?.isActive) {
console.log('Expires:', premium.expirationDate);
console.log('Will renew:', premium.willCancel === false);
console.log('Store state:', premium.subscriptionState);
unlockPremiumFeatures();
} else {
showPaywall();
}
```
## Monthly with 12-Month Commitment Plans
[Section titled âMonthly with 12-Month Commitment Plansâ](#monthly-with-12-month-commitment-plans)
If your App Store Connect subscription is configured with a monthly billing plan and 12-month commitment, StoreKit can return additional pricing terms for that product. Use those terms to show the monthly charge, total commitment price, and full commitment period before purchase.
```typescript
const yearlyProduct = products.find(
(product) => product.identifier === 'com.yourcompany.yourapp.premium_annual',
);
const monthlyCommitment = yearlyProduct?.pricingTerms?.find(
(term) => term.billingPlanType === 'monthly',
);
if (yearlyProduct && monthlyCommitment) {
console.log('Monthly charge:', monthlyCommitment.billingDisplayPrice);
console.log('Total commitment:', monthlyCommitment.commitmentInfo?.priceString);
await NativePurchases.purchaseProduct({
productIdentifier: yearlyProduct.identifier,
productType: PURCHASE_TYPE.SUBS,
billingPlanType: 'monthly',
});
}
```
For the full paywall and entitlement flow, see [iOS monthly commitment billing plans](/docs/plugins/native-purchases/ios-monthly-commitment/).
## Best Practices
[Section titled âBest Practicesâ](#best-practices)
### Pricing Strategy
[Section titled âPricing Strategyâ](#pricing-strategy)
* **Monthly plans**: Lower barrier to entry, builds habit
* **Annual plans**: Better value, higher LTV, lower churn
* **Multiple tiers**: Basic, Premium, Ultimate for different user segments
* **Competitive analysis**: Research similar appsâ pricing
### Product IDs
[Section titled âProduct IDsâ](#product-ids)
* Use consistent naming: `company.app.tier_duration`
* Include tier and duration in ID: `premium_monthly`, `ultimate_annual`
* Avoid changing product IDs (theyâre permanent)
* Document all product IDs for your team
### Family Sharing
[Section titled âFamily Sharingâ](#family-sharing)
* Enable for family-oriented apps (games, educational, entertainment)
* Consider impact on revenue
* Test sharing behavior thoroughly
* Communicate sharing capability in marketing
### Localization
[Section titled âLocalizationâ](#localization)
* Translate all subscription names and descriptions
* Consider regional pricing differences
* Test display in all supported languages
* Use culturally appropriate marketing language
### Promotional Images
[Section titled âPromotional Imagesâ](#promotional-images)
* Maintain consistent visual style
* Include subscription name and key benefit
* Update for seasonal promotions
* Match appâs overall design language
## Common Subscription Patterns
[Section titled âCommon Subscription Patternsâ](#common-subscription-patterns)
### Single Tier (Freemium)
[Section titled âSingle Tier (Freemium)â](#single-tier-freemium)
```plaintext
Free App + Premium Subscription
- Basic: Free (limited features)
- Premium Monthly: $4.99
- Premium Annual: $39.99 (save 33%)
```
### Multi-Tier (Good, Better, Best)
[Section titled âMulti-Tier (Good, Better, Best)â](#multi-tier-good-better-best)
```plaintext
- Basic Monthly: $4.99
- Premium Monthly: $9.99
- Ultimate Monthly: $19.99
- Basic Annual: $49.99
- Premium Annual: $99.99
- Ultimate Annual: $199.99
```
### Consumable + Subscription Hybrid
[Section titled âConsumable + Subscription Hybridâ](#consumable--subscription-hybrid)
```plaintext
- Credit packs (consumable)
- Monthly subscription (unlimited credits)
- Annual subscription (unlimited + bonus features)
```
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Subscription not loading in app:**
* Verify product ID matches exactly (case-sensitive)
* Check subscription is in subscription group
* Ensure bundle identifier matches App Store Connect
* Wait 2-3 hours after creating product
**Cannot submit subscription:**
* Complete all required fields (name, description, price)
* Add at least one localization
* Verify banking/tax info is approved
* Check if first subscription (requires app version)
**Family Sharing toggle disabled:**
* Already enabled (cannot be disabled)
* Check in subscription details
* Contact Apple Support if stuck
**Price tier not available:**
* May be restricted in some territories
* Choose alternative tier
* Contact Apple for pricing questions
**âInvalid Product IDâ error:**
* Must be reverse domain format
* Cannot contain spaces or special characters
* Check for typos
* Verify uniqueness across all products
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
* [Create an introductory offer](/docs/plugins/native-purchases/ios-introductory-offer/) to attract new subscribers
* [Merchandise monthly commitment billing plans](/docs/plugins/native-purchases/ios-monthly-commitment/) for supported annual subscription offers
* [Configure sandbox testing](/docs/plugins/native-purchases/ios-sandbox-testing/) to test your subscriptions
* Set up promotional offers for win-back and retention
* Implement subscription analytics tracking
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
For more details, refer to the [official Apple documentation on auto-renewable subscriptions](https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers).
## Keep going from Create iOS Auto-Renewable Subscription
[Section titled âKeep going from Create iOS Auto-Renewable Subscriptionâ](#keep-going-from-create-ios-auto-renewable-subscription)
If you are using **Create iOS Auto-Renewable Subscription** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# Create iOS Subscription Introductory Offer
> Learn how to create introductory offers for auto-renewable subscriptions on iOS to attract and convert new subscribers.
Introductory offers allow you to provide eligible users with free trials or discounted introductory pricing to reduce barriers to entry and increase subscription conversions.
## Overview
[Section titled âOverviewâ](#overview)
Introductory offers are one of the most effective tools for growing your subscriber base. They allow users to:
* Try your premium features risk-free
* Experience value before committing
* Start at a lower price point
* Build confidence in your product
## Offer Types
[Section titled âOffer Typesâ](#offer-types)
iOS supports three types of introductory offers:
### 1. Free Trial
[Section titled â1. Free Trialâ](#1-free-trial)
Customers get complimentary access for a specified duration. After the trial, theyâre charged at standard rates if they donât cancel.
**Examples:**
* 7 days free
* 14 days free
* 1 month free
**Best for:**
* High-value subscriptions
* Feature-rich apps
* Building user habit
### 2. Pay Up Front
[Section titled â2. Pay Up Frontâ](#2-pay-up-front)
Customers pay a single discounted price that covers the introductory period.
**Examples:**
* $1.99 for 2 months (then $9.99/month)
* $9.99 for 3 months (then $19.99/month)
**Best for:**
* Commitment signals
* Cash flow needs
* Testing price sensitivity
### 3. Pay As You Go
[Section titled â3. Pay As You Goâ](#3-pay-as-you-go)
Customers pay a reduced price for multiple billing cycles.
**Examples:**
* $1.99/month for 3 months (then $9.99/month)
* $4.99/month for 6 months (then $14.99/month)
**Best for:**
* Gradual commitment
* Long-term value demonstration
* Reducing perceived risk
## Eligibility Requirements
[Section titled âEligibility Requirementsâ](#eligibility-requirements)
Users can only receive introductory offers if they:
* Havenât previously received an introductory offer for the product
* Havenât received an introductory offer for any product in the same subscription group
* Havenât had an active subscription to the product
Note
Apple handles eligibility checking automatically. The native-purchases plugin provides methods to check eligibility before presenting offers.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
You must first [create an auto-renewable subscription](/docs/plugins/native-purchases/ios-create-subscription/) before adding an introductory offer.
## Creating an Introductory Offer
[Section titled âCreating an Introductory Offerâ](#creating-an-introductory-offer)
1. **Navigate to Subscription**
In App Store Connect, go to your appâs **Monetize > Subscriptions** section and select the subscription you want to add an offer to.
2. **Add Subscription Price**
Click the **+** icon next to âSubscription Pricesâ to open the pricing modal.
3. **Create Introductory Offer**
Select **âCreate introductory offerâ** from the options.

4. **Configure Countries and Start Date**
**Countries and Regions**: Select where the offer will be available
* Choose all countries for maximum reach
* Or limit to specific markets for testing
**Start Date**: When the offer becomes available
* Can be immediate or scheduled for the future
* Useful for coordinating with marketing campaigns
**End Date (Optional)**: When the offer expires
* Leave blank for ongoing availability
* Set a date for limited-time promotions
5. **Select Offer Type**
Choose one of the three offer types:
**Free** (Free Trial)
* Select duration (days, weeks, months)
* Examples: 7 days, 2 weeks, 1 month
**Pay Up Front**
* Set single payment price
* Set duration covered by payment
* Example: $1.99 for 2 months
**Pay As You Go**
* Set discounted price per period
* Set number of periods
* Example: $2.99/month for 3 months
6. **Review and Confirm**
Review the summary showing:
* Offer type and duration
* Pricing details
* Regular price after intro period
* Availability dates and countries
7. **Save**
Click **Save** to create the introductory offer. It will be available for testing immediately in sandbox mode.
## Offer Configuration Examples
[Section titled âOffer Configuration Examplesâ](#offer-configuration-examples)
### Example 1: Standard Free Trial
[Section titled âExample 1: Standard Free Trialâ](#example-1-standard-free-trial)
```plaintext
Type: Free
Duration: 7 days
Then: $9.99/month
```
**User Journey:**
* Day 1-7: Free access
* Day 8: First charge of $9.99
* Monthly charges continue
### Example 2: Upfront Discounted Period
[Section titled âExample 2: Upfront Discounted Periodâ](#example-2-upfront-discounted-period)
```plaintext
Type: Pay Up Front
Price: $4.99
Duration: 3 months
Then: $9.99/month
```
**User Journey:**
* Day 1: Charged $4.99
* 90 days access
* Day 91: Charged $9.99/month
### Example 3: Gradual Introduction
[Section titled âExample 3: Gradual Introductionâ](#example-3-gradual-introduction)
```plaintext
Type: Pay As You Go
Price: $2.99/month
Periods: 6 months
Then: $9.99/month
```
**User Journey:**
* Months 1-6: $2.99/month
* Month 7+: $9.99/month
## Using in Your App
[Section titled âUsing in Your Appâ](#using-in-your-app)
The native-purchases plugin automatically handles introductory offer presentation and eligibility:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Fetch products with intro offer information
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['com.yourapp.premium_monthly'],
productType: PURCHASE_TYPE.SUBS,
});
const product = products[0];
// Display intro offer details (StoreKit sends localized metadata)
if (product.introductoryPrice) {
console.log('Intro price:', product.introductoryPriceString);
console.log('Intro period:', product.introductoryPricePeriod);
console.log('Intro cycles:', product.introductoryPriceCycles);
console.log('Regular price:', product.priceString);
} else {
console.log('No intro offer configured');
}
// Purchase (StoreKit automatically applies intro pricing if eligible)
try {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.yourapp.premium_monthly',
productType: PURCHASE_TYPE.SUBS,
});
console.log('Subscription active, receipt length:', transaction.receipt?.length);
await validateReceiptOnServer(transaction.receipt);
} catch (error) {
console.error('Purchase failed:', error);
}
```
## Displaying Intro Offers to Users
[Section titled âDisplaying Intro Offers to Usersâ](#displaying-intro-offers-to-users)
### Best Practices for UI
[Section titled âBest Practices for UIâ](#best-practices-for-ui)
**Clear Value Proposition:**
```plaintext
Try Premium Free for 7 Days
Then $9.99/month. Cancel anytime.
```
**Emphasize Savings:**
```plaintext
Start at Just $1.99
Get 3 months of Premium for only $1.99
Then $9.99/month
```
**Transparent Communication:**
```plaintext
Your Free Trial
âą Access all premium features
âą No charge for 7 days
âą $9.99/month after trial
âą Cancel anytime, even during trial
```
### Example Implementation
[Section titled âExample Implementationâ](#example-implementation)
```typescript
function formatIntroOffer(product: any): string {
if (!product.introductoryPrice) {
return `${product.priceString} per ${product.subscriptionPeriod}`;
}
const intro = product.introductoryPrice;
const regular = product.priceString;
if (intro.price === 0) {
// Free trial
return `Try free for ${intro.periodString}, then ${regular}`;
} else if (intro.cycles === 1) {
// Pay up front
return `${intro.priceString} for ${intro.periodString}, then ${regular}`;
} else {
// Enterprise
return `${intro.priceString} for ${intro.cycles} ${intro.periodString}s, then ${regular}`;
}
}
```
## Marketing Best Practices
[Section titled âMarketing Best Practicesâ](#marketing-best-practices)
### Trial Length Strategy
[Section titled âTrial Length Strategyâ](#trial-length-strategy)
* **3-7 days**: Quick decision apps, games
* **7-14 days**: Standard for most apps
* **14-30 days**: Complex tools, professional apps
* **30+ days**: High-value B2B or enterprise
### Pricing Psychology
[Section titled âPricing Psychologyâ](#pricing-psychology)
* **$0.99-$1.99**: Very low barrier, good for testing
* **50% off**: Strong perceived value
* **First month free**: Common, familiar pattern
### Communication Timing
[Section titled âCommunication Timingâ](#communication-timing)
* **Before trial ends**: Remind users of upcoming charge
* **Highlight value**: Show usage stats, achievements
* **Easy cancellation**: Build trust with transparent process
## Testing Intro Offers
[Section titled âTesting Intro Offersâ](#testing-intro-offers)
Use sandbox testing to verify behavior:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// In sandbox mode, accelerated subscription durations apply:
// - 3 days free trial = 3 minutes
// - 1 week free trial = 3 minutes
// - 1 month free trial = 5 minutes
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['premium_monthly'],
productType: PURCHASE_TYPE.SUBS,
});
// Purchase with intro offer
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'premium_monthly',
productType: PURCHASE_TYPE.SUBS,
});
console.log('Intro purchase transaction:', transaction.transactionId);
// Wait for accelerated renewal
setTimeout(async () => {
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const premium = purchases.find((purchase) => purchase.productIdentifier === 'premium_monthly');
console.log('After trial state:', premium?.subscriptionState);
}, 180000); // 3 minutes for weekly trial
```
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Eligibility Rules
[Section titled âEligibility Rulesâ](#eligibility-rules)
* One intro offer per user per subscription group (lifetime)
* Applies to new subscribers only
* Cannot be used again after cancellation
* Not available for subscription upgrades/crossgrades
### StoreKit API
[Section titled âStoreKit APIâ](#storekit-api)
* `introductoryPrice` shows intro offer details
* `eligibility` method checks if user qualifies
* Automatically applied at purchase time
* No special purchase method needed
### Limitations
[Section titled âLimitationsâ](#limitations)
* Only one intro offer active per subscription at a time
* Cannot combine with other discount types
* Cannot change eligibility rules
* Apple controls eligibility checking
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Intro offer not showing:**
* Check if offer is activated in App Store Connect
* Verify user hasnât used an intro offer before
* Ensure user hasnât subscribed to anything in the group
* Test with new sandbox account
**Eligibility check failing:**
* Wait for App Store sync (can take 2-3 hours)
* Verify product ID is correct
* Check subscription group configuration
* Test in sandbox with fresh test account
**Wrong price displaying:**
* Check regional pricing settings
* Verify currency conversion
* Ensure offer dates are current
* Refresh product information
**Sandbox testing issues:**
* Use accelerated durations (3 min = 1 week)
* Create new test accounts for each test
* Wait for trial to complete naturally
* Check renewal count (max 6 in sandbox)
## Analytics and Optimization
[Section titled âAnalytics and Optimizationâ](#analytics-and-optimization)
### Track These Metrics
[Section titled âTrack These Metricsâ](#track-these-metrics)
* Intro offer acceptance rate
* Trial-to-paid conversion rate
* Cancellation during trial
* Retention after first charge
* Revenue impact
### A/B Testing Ideas
[Section titled âA/B Testing Ideasâ](#ab-testing-ideas)
* Free trial vs. paid intro
* Trial length variations
* Discount percentage
* Single payment vs. recurring discount
### Optimization Strategy
[Section titled âOptimization Strategyâ](#optimization-strategy)
```typescript
// Track offer performance
analytics.track('intro_offer_displayed', {
product_id: product.identifier,
offer_type: product.introductoryPriceType,
offer_duration: product.introductoryPricePeriod
});
analytics.track('intro_offer_accepted', {
product_id: product.identifier
});
// Monitor conversion
NativePurchases.addListener('transactionUpdated', (transaction) => {
if (transaction.productIdentifier === product.identifier && transaction.isActive) {
analytics.track('trial_converted', {
transactionId: transaction.transactionId,
productId: transaction.productIdentifier,
});
}
});
```
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
* [Configure sandbox testing](/docs/plugins/native-purchases/ios-sandbox-testing/) to test your intro offers
* Set up promotional offers for win-back campaigns
* Implement subscription analytics
* Create targeted marketing campaigns
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
For more details, refer to the [official Apple documentation on introductory offers](https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers/implementing_introductory_offers_in_your_app).
## Keep going from Create iOS Subscription Introductory Offer
[Section titled âKeep going from Create iOS Subscription Introductory Offerâ](#keep-going-from-create-ios-subscription-introductory-offer)
If you are using **Create iOS Subscription Introductory Offer** to plan payments and purchases, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [Capgo Pricing](/pricing/) for the product workflow in Capgo Pricing, [Payment system](/docs/webapp/payment/) for the implementation detail in Payment system, [@capgo/native-purchases](/docs/plugins/native-purchases/) for the implementation detail in @capgo/native-purchases, and [Getting Started](/docs/plugins/native-purchases/getting-started/) for the implementation detail in Getting Started.
# iOS Monthly Commitment Billing Plans
> Learn how to merchandise and purchase StoreKit monthly subscriptions with a 12-month commitment using @capgo/native-purchases.
Apple supports subscriptions that bill monthly while committing the customer to a longer 12-month term. When StoreKit and the running OS support this billing plan, `@capgo/native-purchases` exposes the pricing terms, lets you select the monthly billing plan during purchase, and returns commitment metadata on transactions and renewal info.
Use this for annual subscription offers where users prefer monthly payments but you still want the retention and predictability of a committed annual term.
## Requirements
[Section titled âRequirementsâ](#requirements)
* Configure the monthly with 12-month commitment billing plan in App Store Connect or in StoreKit Testing in Xcode.
* Build the iOS app with an Xcode SDK that contains StoreKit `pricingTerms` and `billingPlanType`.
* Run on an iOS version that supports StoreKit commitment billing plans.
* Keep a standard billing option available for devices or storefronts that do not return commitment pricing terms.
Note
The JavaScript API is safe to call on all platforms. Unsupported platforms simply will not return `pricingTerms`, and Android ignores `billingPlanType`.
## Load and Display Pricing Terms
[Section titled âLoad and Display Pricing Termsâ](#load-and-display-pricing-terms)
Call `getProducts()` as usual. For supported iOS subscription products, each product can include `pricingTerms`.
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['com.example.app.premium.yearly'],
productType: PURCHASE_TYPE.SUBS,
});
const premiumYearly = products.find(
(product) => product.identifier === 'com.example.app.premium.yearly',
);
const monthlyCommitment = premiumYearly?.pricingTerms?.find(
(term) => term.billingPlanType === 'monthly',
);
```
Render the store-provided values instead of hardcoded pricing:
| Field | Use it for |
| ---------------------------- | ------------------------------------------------------------------------------- |
| `billingDisplayPrice` | The recurring billing amount shown to the user, for example the monthly charge. |
| `billingPeriod` | The billing cadence for the displayed billing price. |
| `commitmentInfo.priceString` | The total commitment price formatted for the userâs storefront. |
| `commitmentInfo.period` | The full commitment period. |
| `subscriptionOffers` | Introductory or promotional offers attached to the pricing term. |
Example paywall copy:
```typescript
function commitmentLabel(term: NonNullable) {
const total = term.commitmentInfo?.priceString;
return total
? `${term.billingDisplayPrice} billed monthly, ${total} total commitment`
: term.billingDisplayPrice;
}
```
## Purchase the Monthly Commitment Plan
[Section titled âPurchase the Monthly Commitment Planâ](#purchase-the-monthly-commitment-plan)
When the user selects the monthly commitment plan, pass `billingPlanType: 'monthly'`.
```typescript
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.example.app.premium.yearly',
productType: PURCHASE_TYPE.SUBS,
billingPlanType: 'monthly',
appAccountToken: userStoreUuid,
});
```
Use `billingPlanType: 'upFront'` only when you need to explicitly select the standard up-front billing plan. If you omit `billingPlanType`, StoreKit uses its default purchase behavior for the product.
Tip
Keep your paywall state tied to the pricing term the user tapped. If `pricingTerms` is missing, hide the commitment option and show the regular subscription purchase button.
## Read Commitment Metadata
[Section titled âRead Commitment Metadataâ](#read-commitment-metadata)
Transactions include the billing plan and commitment progress when StoreKit provides it:
```typescript
if (transaction.billingPlanType === 'monthly') {
console.log('Current billing period:', transaction.commitmentInfo?.billingPeriodNumber);
console.log('Total billing periods:', transaction.commitmentInfo?.totalBillingPeriods);
console.log('Commitment ends:', transaction.commitmentInfo?.expirationDate);
console.log('Commitment total:', transaction.commitmentInfo?.price);
}
```
Renewal info can include the next commitment state:
```typescript
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
onlyCurrentEntitlements: true,
});
for (const purchase of purchases) {
const commitment = purchase.renewalInfo?.commitmentInfo;
if (!commitment) continue;
console.log('Renews into another commitment:', commitment.willAutoRenew);
console.log('Next billing plan:', commitment.renewalBillingPlanType);
console.log('Next renewal date:', commitment.renewalDate);
}
```
Use the top-level `expirationDate` and `isActive` fields for entitlement decisions. Use `commitmentInfo.expirationDate` to explain the full commitment timeline to the customer.
## App Store Review Notes
[Section titled âApp Store Review Notesâ](#app-store-review-notes)
* Show both the recurring billing price and the total commitment price before purchase.
* Make the commitment length explicit near the purchase button.
* Use `product.title`, `product.priceString`, `pricingTerms[].billingDisplayPrice`, and `pricingTerms[].commitmentInfo.priceString` from StoreKit instead of hardcoded prices.
* Provide restore purchases and manage subscription actions on the paywall or account screen.
## Related Guides
[Section titled âRelated Guidesâ](#related-guides)
* [Create iOS subscriptions](/docs/plugins/native-purchases/ios-create-subscription/)
* [Configure iOS sandbox testing](/docs/plugins/native-purchases/ios-sandbox-testing/)
* [iOS App Store review guidelines](/docs/plugins/native-purchases/ios-app-store-review/)
## Keep going from iOS Monthly Commitment Billing Plans
[Section titled âKeep going from iOS Monthly Commitment Billing Plansâ](#keep-going-from-ios-monthly-commitment-billing-plans)
If you are using **iOS Monthly Commitment Billing Plans** to plan payments and purchases, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [Capgo Pricing](/pricing/) for the product workflow in Capgo Pricing, [Payment system](/docs/webapp/payment/) for the implementation detail in Payment system, [@capgo/native-purchases](/docs/plugins/native-purchases/) for the implementation detail in @capgo/native-purchases, and [Getting Started](/docs/plugins/native-purchases/getting-started/) for the implementation detail in Getting Started.
# Configure iOS Sandbox Testing
> Learn how to set up sandbox testing for in-app purchases on iOS using App Store Connect and Xcode.
Testing in-app purchases on iOS requires proper configuration in App Store Connect and on your test devices. This guide covers everything you need to get started with sandbox testing.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
* **Apple Developer Program**: Active membership with annual renewal
* **Agreements**: Signed âPaid Applications Agreementâ with banking and tax information completed
* **Xcode Project**: Configured with proper bundle identifier and capabilities
Note
Banking and tax setup approval can take hours to days. Complete this well in advance of testing.
## Setup Process
[Section titled âSetup Processâ](#setup-process)
1. **Sign Paid Applications Agreement**
In App Store Connect, navigate to **Agreements, Tax, and Banking** and complete:
* Sign the Paid Applications Agreement
* Add your banking information
* Complete tax forms
Wait for Apple to approve your information (this can take 24-48 hours).
2. **Create Sandbox Test User**
In App Store Connect, go to **Users and Access > Sandbox Testers**.
Click the **+** button to create a new sandbox tester.
**Important**: Use an email address that is NOT already associated with an Apple ID. You can use email aliases:
* Gmail: `youremail+test@gmail.com`
* iCloud: `youremail+test@icloud.com`

3. **Configure Test Device (iOS 12+)**
Starting with iOS 12, you no longer need to sign out of your iTunes account to test purchases.
On your iOS device:
1. Open **Settings**
2. Tap **App Store**
3. Scroll to the bottom
4. Tap **Sandbox Account**
5. Sign in with your sandbox test account
Tip
This is much more convenient than the old method of signing out of your iTunes account!
4. **Configure Xcode Project**
Ensure your Xcode project has:
**Bundle Identifier**
* Must match the identifier in your Developer Center
* Must match the identifier in App Store Connect
**In-App Purchase Capability**
1. Select your project in Xcode
2. Go to **Signing & Capabilities**
3. Click **+ Capability**
4. Add **In-App Purchase**
5. **Create In-App Purchase Products**
In App Store Connect, navigate to your app and create your in-app purchase products (subscriptions, consumables, etc.).
Products must be in at least âReady to Submitâ status for sandbox testing.
6. **Test Your Implementation**
Build and run your app on a test device. When you attempt a purchase, you should see:
> **\[Environment: Sandbox]**
This confirmation indicates youâre in the sandbox environment and wonât be charged real money.
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Sandbox Environment Characteristics
[Section titled âSandbox Environment Characteristicsâ](#sandbox-environment-characteristics)
* **No real charges**: All purchases are free in sandbox mode
* **Accelerated subscriptions**: Subscription durations are shortened for faster testing
* 1 week subscription = 3 minutes
* 1 month subscription = 5 minutes
* 2 months subscription = 10 minutes
* 3 months subscription = 15 minutes
* 6 months subscription = 30 minutes
* 1 year subscription = 1 hour
* **Auto-renewal limit**: Subscriptions auto-renew up to 6 times in sandbox
* **Immediate cancellation**: Cancelled subscriptions expire immediately
### Sandbox Account Management
[Section titled âSandbox Account Managementâ](#sandbox-account-management)
* Create multiple test accounts for different scenarios
* Use test accounts only on test devices
* Donât use personal Apple ID for sandbox testing
* Test accounts can purchase any product regardless of region
## Using Sandbox Testing
[Section titled âUsing Sandbox Testingâ](#using-sandbox-testing)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) {
throw new Error('StoreKit not supported on this device');
}
// Fetch products (automatically uses sandbox when available)
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['premium_monthly'],
productType: PURCHASE_TYPE.SUBS,
});
// Make test purchase
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'premium_monthly',
productType: PURCHASE_TYPE.SUBS,
});
console.log('Test purchase successful!', transaction.transactionId);
```
## Verification
[Section titled âVerificationâ](#verification)
When properly configured, you should observe:
1. **Sandbox banner** during purchase: â\[Environment: Sandbox]â
2. **Products load** successfully
3. **Purchases complete** without actual charges
4. **Receipts validate** correctly
5. **Subscriptions renew** automatically (at accelerated rate)
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Products not loading:**
* Verify bundle identifier matches App Store Connect
* Check that agreements are signed and approved
* Ensure products are at least âReady to Submitâ status
* Wait 2-3 hours after creating products
**âCannot connect to iTunes Storeâ:**
* Verify sandbox account is configured correctly
* Check device is connected to internet
* Try signing out and back into sandbox account
* Restart the app
**Purchases failing silently:**
* Check Xcode console for error messages
* Verify In-App Purchase capability is enabled
* Ensure sandbox account email is not a real Apple ID
* Try creating a new sandbox test account
**Receipt validation errors:**
* Use sandbox receipt validation endpoint in testing
* Production endpoint: `https://buy.itunes.apple.com/verifyReceipt`
* Sandbox endpoint: `https://sandbox.itunes.apple.com/verifyReceipt`
* The native-purchases plugin handles this automatically
**Wrong subscription duration:**
* Remember subscriptions are accelerated in sandbox
* Use the conversion chart above for expected durations
* Subscriptions auto-renew max 6 times in sandbox
**âThis Apple ID has not yet been used in the iTunes Storeâ:**
* This is normal for new sandbox accounts
* Proceed with the purchase to activate the account
* Only happens on first use
## Best Practices
[Section titled âBest Practicesâ](#best-practices)
1. **Create multiple test accounts** for different test scenarios
2. **Test all subscription durations** to verify behavior
3. **Test cancellation and renewal** flows
4. **Verify receipt validation** works correctly
5. **Test restore purchases** functionality
6. **Check subscription upgrade/downgrade** behavior
7. **Test with poor network conditions**
## Production vs. Sandbox
[Section titled âProduction vs. Sandboxâ](#production-vs-sandbox)
| Feature | Sandbox | Production |
| --------------------- | ----------- | -------------- |
| Real charges | No | Yes |
| Subscription duration | Accelerated | Normal |
| Auto-renewal limit | 6 times | Unlimited |
| Cancellation effect | Immediate | End of period |
| Receipt endpoint | Sandbox URL | Production URL |
| Test accounts only | Yes | No |
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
For more details, refer to the [official Apple StoreKit documentation](https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox) on sandbox testing.
## Keep going from Configure iOS Sandbox Testing
[Section titled âKeep going from Configure iOS Sandbox Testingâ](#keep-going-from-configure-ios-sandbox-testing)
If you are using **Configure iOS Sandbox Testing** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# Create iOS Subscription Group
> Learn how to create and configure subscription groups in App Store Connect for organizing your app's subscription offerings.
Subscription groups are essential for organizing and managing multiple subscription levels in your iOS app. Understanding how they work is crucial for implementing upgrade, downgrade, and crossgrade functionality.
## What is a Subscription Group?
[Section titled âWhat is a Subscription Group?â](#what-is-a-subscription-group)
A subscription group is a collection of related subscriptions that users can choose between. Users can only subscribe to one subscription within a group at a time. When they switch subscriptions, Apple handles the transition automatically.
## Why Subscription Groups Matter
[Section titled âWhy Subscription Groups Matterâ](#why-subscription-groups-matter)
Subscription groups enable:
* **Tiered pricing**: Offer basic, premium, and ultimate plans
* **Different durations**: Monthly, yearly, and lifetime options
* **Upgrade/downgrade logic**: Automatic handling of subscription changes
* **Simplified management**: Group related subscriptions together
## Subscription Levels
[Section titled âSubscription Levelsâ](#subscription-levels)
Within a group, each subscription should be ranked from highest value (level 1) to lowest value. This ranking determines how subscription changes are classified:

### Level Examples
[Section titled âLevel Examplesâ](#level-examples)
**Level 1** (Highest Value)
* Premium Annual ($99.99/year)
* Ultimate Monthly ($19.99/month)
**Level 2** (Medium Value)
* Standard Annual ($49.99/year)
* Premium Monthly ($9.99/month)
**Level 3** (Lowest Value)
* Basic Annual ($29.99/year)
* Standard Monthly ($4.99/month)
## Subscription Change Types
[Section titled âSubscription Change Typesâ](#subscription-change-types)
Apple automatically handles three types of subscription changes based on the level ranking:
### 1. Upgrade
[Section titled â1. Upgradeâ](#1-upgrade)
Moving to a **higher-tier** subscription (e.g., level 2 â level 1).
**Behavior:**
* Takes effect **immediately**
* User receives **prorated refund** for remaining time
* New subscription starts right away
**Example:**
```typescript
// User currently has: Standard Monthly (Level 2)
// User upgrades to: Premium Annual (Level 1)
// Result: Immediate access to Premium, refund for unused Standard time
```
### 2. Downgrade
[Section titled â2. Downgradeâ](#2-downgrade)
Moving to a **lower-tier** subscription (e.g., level 1 â level 2).
**Behavior:**
* Takes effect at **next renewal date**
* User keeps current subscription until period ends
* New subscription starts automatically after expiration
**Example:**
```typescript
// User currently has: Premium Annual (Level 1)
// User downgrades to: Standard Monthly (Level 2)
// Result: Premium access continues until annual renewal date, then switches
```
### 3. Crossgrade
[Section titled â3. Crossgradeâ](#3-crossgrade)
Switching to another subscription **at the same tier level**.
**Behavior depends on duration:**
**Different Duration** â Behaves like **downgrade**
* Takes effect at next renewal date
* Example: Monthly Premium (Level 1) â Annual Premium (Level 1)
**Same Duration** â Behaves like **upgrade**
* Takes effect immediately
* Example: Premium Monthly (Level 1) â Ultimate Monthly (Level 1)
## Creating a Subscription Group
[Section titled âCreating a Subscription Groupâ](#creating-a-subscription-group)
1. **Navigate to Subscriptions**
In App Store Connect, select your app and go to **Monetize > Subscriptions**.
2. **Create Group**
Click **+** next to âSubscription Groupsâ to create a new group.
3. **Name the Group**
Choose a descriptive name that reflects the subscriptions it contains:
* âPremium Accessâ
* âCloud Storage Plansâ
* âPro Featuresâ
4. **Add Subscriptions**
After creating the group, add individual subscriptions to it. Each subscription will have a level ranking.
5. **Set Level Rankings**
Arrange subscriptions from highest value (1) to lowest value. Consider:
* Annual plans typically rank higher than monthly
* Higher-priced tiers rank above lower-priced ones
* Ultimate/premium tiers rank highest
## Using in Your App
[Section titled âUsing in Your Appâ](#using-in-your-app)
The native-purchases plugin automatically handles subscription group logic:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Fetch all subscriptions in a group
const { products } = await NativePurchases.getProducts({
productIdentifiers: ['premium_monthly', 'premium_annual', 'ultimate_monthly'],
productType: PURCHASE_TYPE.SUBS,
});
// Display current subscription using StoreKit transactions
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const activeSubs = purchases.filter((purchase) => purchase.isActive);
// Detect pending downgrade/cancellation (StoreKit sets willCancel === true)
const pendingChange = purchases.find((purchase) => purchase.willCancel === true);
if (pendingChange) {
console.log('Subscription will stop auto-renewing on', pendingChange.expirationDate);
}
// Purchase (StoreKit handles upgrades/downgrades automatically)
await NativePurchases.purchaseProduct({
productIdentifier: 'premium_annual',
productType: PURCHASE_TYPE.SUBS,
});
// Listen for StoreKit updates (fires on upgrades/downgrades/refunds)
NativePurchases.addListener('transactionUpdated', (transaction) => {
console.log('Subscription updated:', transaction);
});
```
## Handling Subscription Changes
[Section titled âHandling Subscription Changesâ](#handling-subscription-changes)
### Detecting Change Type
[Section titled âDetecting Change Typeâ](#detecting-change-type)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
// Get current subscription info
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
const currentSubscription = purchases.find(
(purchase) => purchase.subscriptionState === 'subscribed',
);
if (currentSubscription) {
// StoreKit reports if user cancelled auto-renew
if (currentSubscription.willCancel) {
console.log(
`User cancelled. Access remains until ${currentSubscription.expirationDate}`,
);
}
if (currentSubscription.isUpgraded) {
console.log('User recently upgraded to this plan.');
}
}
// Listen for automatic upgrades/downgrades
NativePurchases.addListener('transactionUpdated', (transaction) => {
console.log('Subscription changed!', transaction);
if (transaction.subscriptionState === 'revoked') {
revokeAccess();
} else if (transaction.isActive) {
unlockPremiumFeatures();
}
});
```
### User Communication
[Section titled âUser Communicationâ](#user-communication)
Always communicate the change behavior clearly:
**For Upgrades:**
> âYouâll get immediate access to Premium features. Weâll prorate your current subscription.â
**For Downgrades:**
> âYouâll keep Premium access until \[renewal date], then switch to Standard.â
**For Crossgrades:**
> âYour plan will change to Annual billing at the next renewal on \[date].â
## Server monitoring
[Section titled âServer monitoringâ](#server-monitoring)
Use Appleâs App Store Server Notifications v2 or your own receipt-validation backend to mirror StoreKit changes in your database. Pair server notifications with the `transactionUpdated` listener so both client and backend stay in sync.
## Best Practices
[Section titled âBest Practicesâ](#best-practices)
### Group Organization
[Section titled âGroup Organizationâ](#group-organization)
* Keep related subscriptions in the same group
* Donât mix unrelated features (e.g., storage and ad removal)
* Create separate groups for different feature sets
### Level Ranking Strategy
[Section titled âLevel Ranking Strategyâ](#level-ranking-strategy)
* Annual plans â Higher level than monthly (for same tier)
* Higher-priced tiers â Higher level
* Consider value, not just price
### User Experience
[Section titled âUser Experienceâ](#user-experience)
* Show current subscription clearly
* Display all available options in the group
* Indicate which changes are immediate vs. at renewal
* Allow easy switching between plans
### Testing
[Section titled âTestingâ](#testing)
* Test all upgrade scenarios
* Test all downgrade scenarios
* Verify crossgrade behavior
* Check webhook firing
## Common Scenarios
[Section titled âCommon Scenariosâ](#common-scenarios)
### Scenario 1: Three-Tier Monthly Plans
[Section titled âScenario 1: Three-Tier Monthly Plansâ](#scenario-1-three-tier-monthly-plans)
```plaintext
Level 1: Ultimate Monthly ($19.99)
Level 2: Premium Monthly ($9.99)
Level 3: Basic Monthly ($4.99)
```
* Basic â Premium: Upgrade (immediate)
* Premium â Ultimate: Upgrade (immediate)
* Ultimate â Premium: Downgrade (at renewal)
* Basic â Ultimate: Upgrade (immediate)
### Scenario 2: Mixed Duration Plans
[Section titled âScenario 2: Mixed Duration Plansâ](#scenario-2-mixed-duration-plans)
```plaintext
Level 1: Premium Annual ($99.99/year)
Level 2: Premium Monthly ($9.99/month)
```
* Monthly â Annual: Crossgrade (at renewal)
* Annual â Monthly: Downgrade (at renewal)
### Scenario 3: Multi-Tier Multi-Duration
[Section titled âScenario 3: Multi-Tier Multi-Durationâ](#scenario-3-multi-tier-multi-duration)
```plaintext
Level 1: Ultimate Annual ($199/year)
Level 2: Ultimate Monthly ($19.99/month)
Level 3: Premium Annual ($99/year)
Level 4: Premium Monthly ($9.99/month)
Level 5: Basic Annual ($49/year)
Level 6: Basic Monthly ($4.99/month)
```
This setup provides maximum flexibility while maintaining clear upgrade/downgrade logic.
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
**Subscription not appearing in group:**
* Verify itâs assigned to the correct group
* Check that itâs in at least âReady to Submitâ status
* Ensure product ID is correct
**Wrong upgrade/downgrade behavior:**
* Review level rankings (1 = highest)
* Verify subscription tiers make sense
* Check that levels are set correctly
**Products from different groups:**
* Users can subscribe to multiple groups simultaneously
* This is intentional - keep related products in same group
**getActiveProducts showing multiple subscriptions:**
* Check if subscriptions are in different groups
* Verify user isnât subscribed via Family Sharing
* Review subscription status in App Store Connect
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
For more details, refer to the [official Apple documentation on subscription groups](https://developer.apple.com/app-store/subscriptions/).
## Keep going from Create iOS Subscription Group
[Section titled âKeep going from Create iOS Subscription Groupâ](#keep-going-from-create-ios-subscription-group)
If you are using **Create iOS Subscription Group** to plan store approval and distribution, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [@capgo/capacitor-in-app-review](/docs/plugins/in-app-review/) for the implementation detail in @capgo/capacitor-in-app-review, [Using @capgo/capacitor-in-app-review](/plugins/capacitor-in-app-review/) for the native capability in Using @capgo/capacitor-in-app-review, [@capgo/capacitor-native-market](/docs/plugins/native-market/) for the implementation detail in @capgo/capacitor-native-market, and [Using @capgo/capacitor-native-market](/plugins/capacitor-native-market/) for the native capability in Using @capgo/capacitor-native-market.
# Revenue Playbook
> Learn how to turn a Capacitor app into revenue with a focused MVP, store discovery, paywall placement, pricing, analytics, and @capgo/native-purchases.

The purchase SDK is only one part of making money from an app. Revenue comes from a clear problem, a small product that users can try, reliable store billing, and a paywall that teaches you what people are willing to buy.
Use this playbook when you are adding subscriptions or premium unlocks with `@capgo/native-purchases`.
## Start with a simple revenue target
[Section titled âStart with a simple revenue targetâ](#start-with-a-simple-revenue-target)
Make the first target concrete. For example:
| Monthly price | Active subscribers needed for about $1K MRR |
| ------------- | ------------------------------------------------- |
| $4.99 | 201 |
| $7.99 | 126 |
| $9.99 | 101 |
| $29.99 yearly | About 400 annual subscribers, depending on timing |
These numbers are before store fees, taxes, refunds, and currency differences. They are still useful because they keep the launch plan practical: you need a few hundred motivated users, not a huge audience.
## Build the smallest paid product
[Section titled âBuild the smallest paid productâ](#build-the-smallest-paid-product)
1. **Pick one painful use case**
Build around one outcome users already search for. Examples: a workout plan for new parents, a budget tracker for couples, a receipt scanner for freelancers, or a language drill app for one exam.
2. **Check demand in the stores**
Search App Store and Google Play for the core keyword. Read low and mid-score reviews of competing apps to find missing features, confusing onboarding, pricing complaints, and UI friction.
3. **Ship a narrow MVP**
The first version should include onboarding, one useful core action, basic error handling, and enough analytics to see whether users reach the value moment.
4. **Add purchases early**
Do not wait until the app feels complete. A basic paywall helps you learn whether users understand the value and whether your pricing is plausible.
## Instrument the funnel before optimizing
[Section titled âInstrument the funnel before optimizingâ](#instrument-the-funnel-before-optimizing)
Track these events before you start changing prices or screens:
| Event | Why it matters |
| ----------------------------------------- | --------------------------------------- |
| `install` or first open | Baseline traffic |
| `onboarding_completed` | Whether users understand the setup |
| `core_action_completed` | Whether the product delivers value |
| `paywall_viewed` | Whether users reach monetization |
| `trial_started` | Whether the offer is compelling |
| `purchase_completed` | Paid conversion |
| `restore_started` and `restore_completed` | Purchase recovery and review compliance |
| `subscription_status_checked` | Entitlement reliability |
| `cancel_feedback_submitted` | Churn reason |
If many users do not see the paywall, fix onboarding before changing the paywall. If users see the paywall but do not start a trial, improve the offer, proof, or price presentation.
## Choose one monetization model
[Section titled âChoose one monetization modelâ](#choose-one-monetization-model)
Start with one model so the data is readable.
| Model | Good fit | First version |
| ----------------------- | ------------------------------------------------ | -------------------------------------------------------- |
| Freemium | Daily utilities, trackers, tools with repeat use | Free core action, paid limits or premium features |
| Paywall plus free trial | Apps that deliver quick value after onboarding | Paywall after onboarding with 3- to 14-day trial |
| One-time unlock | Small tools with limited recurring value | Lifetime product plus optional future subscription later |
Avoid shipping three tiers, many bundles, and complex upgrade paths on day one. Use one monthly plan and one annual plan when you need subscriptions. Add localized pricing after you see meaningful traffic from a country.
## Configure products for revenue learning
[Section titled âConfigure products for revenue learningâ](#configure-products-for-revenue-learning)
Keep product identifiers stable and readable:
```text
com.example.app.premium.monthly
com.example.app.premium.yearly
com.example.app.premium.lifetime
```
Use store product names that reinforce the value users are searching for, such as âMeal Planner Pro Monthlyâ instead of only âMonthlyâ. Store metadata and in-app purchase names can help discovery and clarity.
Load product data from the stores so pricing, currency, and introductory offers are always accurate:
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { products } = await NativePurchases.getProducts({
productIdentifiers: [
'com.example.app.premium.monthly',
'com.example.app.premium.yearly',
],
productType: PURCHASE_TYPE.SUBS,
});
const monthly = products.find((product) => product.identifier.endsWith('.monthly'));
const yearly = products.find((product) => product.identifier.endsWith('.yearly'));
```
Never hardcode store pricing in the UI. Render `product.priceString`, localized product title, billing period, and trial terms from store data whenever possible.
## Build a first paywall
[Section titled âBuild a first paywallâ](#build-a-first-paywall)
A first paywall should be clear, not clever:
* Headline: the paid outcome, such as âUnlock unlimited workout plansâ.
* Benefits: 3 to 5 concrete improvements, not a long feature list.
* Plans: monthly and annual, with real annual savings if offered.
* Trial: exact trial length and what happens after it ends.
* CTA: âStart free trialâ or âUpgrade nowâ.
* Links: terms, privacy policy, restore purchases, and manage subscriptions.
Place the first paywall after onboarding, once the user understands what the app does. Later, test additional triggers such as usage limits, premium feature taps, or completed core actions.
## Purchase and restore flow
[Section titled âPurchase and restore flowâ](#purchase-and-restore-flow)
```typescript
import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
export async function buyYearly(appAccountToken: string) {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: 'com.example.app.premium.yearly',
planIdentifier: 'yearly-plan',
productType: PURCHASE_TYPE.SUBS,
appAccountToken,
});
await fetch('/api/purchases/validate', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
transactionId: transaction.transactionId,
receipt: transaction.receipt,
purchaseToken: transaction.purchaseToken,
productIdentifier: transaction.productIdentifier,
}),
});
return transaction;
}
export async function restorePurchases() {
await NativePurchases.restorePurchases();
return NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
}
```
Always validate purchases on your backend before granting durable entitlements. Keep a local entitlement cache for fast UI, but treat the store and your backend as the source of truth.
## Bring in the first users
[Section titled âBring in the first usersâ](#bring-in-the-first-users)
Revenue needs traffic. Start with channels that can work before you have a brand:
* ASO: title, subtitle, keywords, screenshots, app description, icon, ratings, and in-app purchase names.
* Short-form video: post quick demos, problem/solution clips, and before/after examples for the target country.
* Reddit and communities: join the conversation first, then share what you built as a useful story instead of an ad.
* Beta groups: TestFlight, Google Play internal testing, Discord, and niche forums.
Each channel should send users into the same measured funnel so you can compare retention, paywall views, trials, and purchases.
## Read churn correctly
[Section titled âRead churn correctlyâ](#read-churn-correctly)
Some churn means users tried the app and decided it was not for them. That is normal. What matters is the pattern:
* Cancels during trial: unclear value, poor onboarding, or wrong traffic.
* Cancels after one cycle: not enough repeat value or weak habit loop.
* Refunds: pricing mismatch, accidental purchase risk, or unclear terms.
* No restores: broken entitlement handling or missing restore UI.
Add a one-question cancellation survey when possible. Use the answers to improve onboarding, feature scope, store screenshots, and paywall copy.
## Launch checklist
[Section titled âLaunch checklistâ](#launch-checklist)
* Product solves one clear paid problem.
* Store products are active and tested on iOS and Android.
* Paywall displays store-loaded prices and terms.
* Purchase, restore, manage subscription, and backend validation are implemented.
* Funnel events are tracked from first open to purchase.
* App store metadata explains the value in the first screenshots.
* At least one acquisition channel is active before launch.
* Churn feedback is collected from the first subscribers.
## Related guides
[Section titled âRelated guidesâ](#related-guides)
* [Getting started](/docs/plugins/native-purchases/getting-started/)
* [Create iOS subscriptions](/docs/plugins/native-purchases/ios-create-subscription/)
* [Create Android subscriptions](/docs/plugins/native-purchases/android-create-subscription/)
* [iOS sandbox testing](/docs/plugins/native-purchases/ios-sandbox-testing/)
* [Android sandbox testing](/docs/plugins/native-purchases/android-sandbox-testing/)
## Keep going from Revenue Playbook
[Section titled âKeep going from Revenue Playbookâ](#keep-going-from-revenue-playbook)
If you are using **Revenue Playbook** to plan payments and purchases, connect it with [Using @capgo/native-purchases](/plugins/capacitor-native-purchases/) for the native capability in Using @capgo/native-purchases, [Capgo Pricing](/pricing/) for the product workflow in Capgo Pricing, [Payment system](/docs/webapp/payment/) for the implementation detail in Payment system, [@capgo/native-purchases](/docs/plugins/native-purchases/) for the implementation detail in @capgo/native-purchases, and [Getting Started](/docs/plugins/native-purchases/getting-started/) for the implementation detail in Getting Started.
# @capgo/capacitor-nativegeocoder
> Capacitor plugin for native forward and reverse geocoding.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for native forward and reverse geocoding.
Package name changed.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `reverseGeocode` - Convert latitude and longitude to an address.
* `forwardGeocode` - Convert an address to latitude and longitude.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | --------------------------------------------- |
| `reverseGeocode` | Convert latitude and longitude to an address. |
| `forwardGeocode` | Convert an address to latitude and longitude. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder/).
## Keep going from @capgo/capacitor-nativegeocoder
[Section titled âKeep going from @capgo/capacitor-nativegeocoderâ](#keep-going-from-capgocapacitor-nativegeocoder)
If you are using **@capgo/capacitor-nativegeocoder** to plan native plugin work, connect it with [Using @capgo/capacitor-nativegeocoder](/plugins/capacitor-nativegeocoder/) for the native capability in Using @capgo/capacitor-nativegeocoder, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-nativegeocoder and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-nativegeocoder` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-nativegeocoder
npx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `reverseGeocode`
[Section titled âreverseGeocodeâ](#reversegeocode)
Convert latitude and longitude to an address
```typescript
import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder';
await NativeGeocoder.reverseGeocode({} as ReverseOptions);
```
### `forwardGeocode`
[Section titled âforwardGeocodeâ](#forwardgeocode)
Convert an address to latitude and longitude
```typescript
import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder';
await NativeGeocoder.forwardGeocode({} as ForwardOptions);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `ReverseOptions`
[Section titled âReverseOptionsâ](#reverseoptions)
```typescript
export interface ReverseOptions {
/**
* latitude is a number representing the latitude of the location.
*/
latitude: number;
/**
* longitude is a number representing the longitude of the location.
*/
longitude: number;
/**
* Localise the results to the given locale.
*/
useLocale?: boolean;
/**
* locale is a string in the format of language_country, for example en_US.
*/
defaultLocale?: string;
/**
* Max number of results to return.
*/
maxResults?: number;
/**
* Only used for web platform to use google api
*/
apiKey?: string;
/**
* Only used for web platform to use google api
*/
resultType?: string;
}
```
### `Address`
[Section titled âAddressâ](#address)
```typescript
export interface Address {
latitude: number;
longitude: number;
countryCode: string;
countryName: string;
postalCode: string;
administrativeArea: string;
subAdministrativeArea: string;
locality: string;
subLocality: string;
thoroughfare: string;
subThoroughfare: string;
areasOfInterest: string[];
}
```
### `ForwardOptions`
[Section titled âForwardOptionsâ](#forwardoptions)
```typescript
export interface ForwardOptions {
/**
* address is a string of the address to be geocoded.
*/
addressString: string;
/**
* Localise the results to the given locale.
*/
useLocale?: boolean;
/**
* locale is a string in the format of language_country, for example en_US.
*/
defaultLocale?: string;
/**
* Max number of results to return.
*/
maxResults?: number;
/**
* Only used for web platform to use google api
*/
apiKey?: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-nativegeocoder](/plugins/capacitor-nativegeocoder/) for the native capability in Using @capgo/capacitor-nativegeocoder, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-navigation-bar
> Capacitor Navigation Bar Plugin for customizing the Android navigation bar.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Navigation Bar Plugin for customizing the Android navigation bar.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `setNavigationBarColor` - Set the navigation bar color and button theme.
* `getNavigationBarColor` - Get the current navigation bar color and button theme.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ----------------------- | ------------------------------------------------------ |
| `setNavigationBarColor` | Set the navigation bar color and button theme. |
| `getNavigationBarColor` | Get the current navigation bar color and button theme. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-navigation-bar](https://github.com/Cap-go/capacitor-navigation-bar/).
## Keep going from @capgo/capacitor-navigation-bar
[Section titled âKeep going from @capgo/capacitor-navigation-barâ](#keep-going-from-capgocapacitor-navigation-bar)
If you are using **@capgo/capacitor-navigation-bar** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-navigation-bar](/plugins/capacitor-navigation-bar/) for the native capability in Using @capgo/capacitor-navigation-bar, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-navigation-bar and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-navigation-bar` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-navigation-bar
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { NavigationBar } from '@capgo/capacitor-navigation-bar';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `setNavigationBarColor`
[Section titled âsetNavigationBarColorâ](#setnavigationbarcolor)
Set the navigation bar color and button theme.
```typescript
import { NavigationBar } from '@capgo/capacitor-navigation-bar';
// Set to white with dark buttons
await NavigationBar.setNavigationBarColor({
color: NavigationBarColor.WHITE,
darkButtons: true
});
// Set to custom color
await NavigationBar.setNavigationBarColor({
color: '#FF5733',
darkButtons: false
});
// Set a custom divider color on Android 9+
await NavigationBar.setNavigationBarColor({
color: NavigationBarColor.WHITE,
darkButtons: true,
dividerColor: '#D9D9D9'
});
```
### `getNavigationBarColor`
[Section titled âgetNavigationBarColorâ](#getnavigationbarcolor)
Get the current navigation bar color and button theme.
```typescript
import { NavigationBar } from '@capgo/capacitor-navigation-bar';
const { color, darkButtons } = await NavigationBar.getNavigationBarColor();
console.log('Current color:', color);
console.log('Using dark buttons:', darkButtons);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `NavigationBarColor`
[Section titled âNavigationBarColorâ](#navigationbarcolor)
Predefined navigation bar colors.
```typescript
export enum NavigationBarColor {
/** White color */
WHITE = '#FFFFFF',
/** Black color */
BLACK = '#000000',
/** Transparent color */
TRANSPARENT = 'transparent',
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-navigation-bar](/plugins/capacitor-navigation-bar/) for the native capability in Using @capgo/capacitor-navigation-bar, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# @capgo/capacitor-network-diagnostics
> Native diagnostics for blocked ports, captive portals, WebSockets, download speed, and packet loss.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-network-diagnostics` helps support teams diagnose restricted Wi-Fi, access point, carrier, firewall, captive portal, DNS, and proxy problems from the same native network stack used by the app.
Use it when a user can open the app but cannot reach a specific API, TCP port, WebSocket endpoint, or download URL from a locked-down network.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `getNetworkStatus` - Read the native connection type and platform network flags.
* `testUrl` - Check HTTP or HTTPS URL reachability with status code and latency.
* `testPort` - Open a native TCP socket to a host and port.
* `testWebSocket` - Validate a `ws://` or `wss://` handshake.
* `testDownloadSpeed` - Measure native download throughput from your own test file endpoint.
* `testPacketLoss` - Estimate application-level packet loss with repeated TCP or HTTP probes.
* `runDiagnostics` - Run a combined diagnostic pass and return a compact issue list.
Raw ICMP ping is not consistently available to App Store and Play Store apps, so packet loss uses repeated TCP or HTTP probes.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `getNetworkStatus` | Read the current native connection type and platform network flags. |
| `testUrl` | Test whether an HTTP or HTTPS URL can be reached from native networking. |
| `testPort` | Test whether a TCP host and port can be opened from native networking. |
| `testWebSocket` | Test whether a WebSocket URL can complete its native handshake. |
| `testDownloadSpeed` | Measure download throughput from a native HTTP request. |
| `testPacketLoss` | Estimate application-level packet loss with repeated TCP or HTTP probes. |
| `runDiagnostics` | Run several diagnostics and return a compact issue list. |
| `getPluginVersion` | Return the native plugin version marker. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-network-diagnostics](https://github.com/Cap-go/capacitor-network-diagnostics/).
# Android Setup
> Android behavior and setup notes for @capgo/capacitor-network-diagnostics.
The plugin declares the required Android permissions automatically:
```xml
```
## Native behavior
[Section titled âNative behaviorâ](#native-behavior)
* Connection status uses `ConnectivityManager` and `NetworkCapabilities`.
* URL and download tests use native `HttpURLConnection`.
* TCP port tests use native sockets.
* WebSocket tests perform a native WebSocket upgrade handshake over TCP or TLS.
## Captive portal and validated internet
[Section titled âCaptive portal and validated internetâ](#captive-portal-and-validated-internet)
Android can expose whether the active network is internet validated or marked as captive portal capable. These flags help explain cases where the device is connected to Wi-Fi but the app cannot reach your backend.
## Cleartext traffic
[Section titled âCleartext trafficâ](#cleartext-traffic)
Prefer `https://` and `wss://` targets. If you need to diagnose a plain `http://` or `ws://` endpoint on Android 9 or later, the host app may need a Network Security Config or `android:usesCleartextTraffic="true"` for that diagnostic target.
# Getting Started
> Install @capgo/capacitor-network-diagnostics and run native network checks from your Capacitor app.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-network-diagnostics` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-network-diagnostics
npx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { NetworkDiagnostics } from '@capgo/capacitor-network-diagnostics';
```
## Run a complete diagnostic report
[Section titled âRun a complete diagnostic reportâ](#run-a-complete-diagnostic-report)
`runDiagnostics` is the fastest way to collect a support report for a user on a restricted network.
```typescript
const report = await NetworkDiagnostics.runDiagnostics({
urls: [{ url: 'https://api.example.com/health', method: 'HEAD' }],
ports: [{ host: 'api.example.com', port: 443 }],
websockets: [{ url: 'wss://ws.example.com/socket' }],
download: {
url: 'https://speed.example.com/5mb.bin',
maxBytes: 5 * 1024 * 1024,
},
packetLoss: {
mode: 'tcp',
host: 'api.example.com',
port: 443,
count: 10,
},
});
console.log(report.status.connectionType);
console.log(report.issues);
```
## Check current network status
[Section titled âCheck current network statusâ](#check-current-network-status)
```typescript
const status = await NetworkDiagnostics.getNetworkStatus();
console.log(status.connected);
console.log(status.connectionType);
console.log(status.internetReachable);
console.log(status.captivePortal);
```
The exact flags depend on the platform. Android can report validated internet and captive portal state. iOS reports path status, interface type, expensive paths, and constrained paths through `Network.framework`.
## Test API reachability
[Section titled âTest API reachabilityâ](#test-api-reachability)
```typescript
const result = await NetworkDiagnostics.testUrl({
url: 'https://api.example.com/health',
method: 'HEAD',
timeoutMs: 5000,
followRedirects: true,
});
if (!result.reachable) {
console.warn(result.errorCode, result.errorMessage);
}
```
Use a real health endpoint from your backend. A browser-only check can be hidden by WebView, CORS, proxy, or captive portal behavior, while this plugin uses native networking.
## Test a TCP port
[Section titled âTest a TCP portâ](#test-a-tcp-port)
```typescript
const port = await NetworkDiagnostics.testPort({
host: 'api.example.com',
port: 443,
timeoutMs: 3000,
});
console.log(port.open, port.durationMs);
```
This is useful when a Wi-Fi access point blocks non-standard ports, MQTT, custom gateways, or a private backend while still allowing normal browsing.
## Test WebSocket connectivity
[Section titled âTest WebSocket connectivityâ](#test-websocket-connectivity)
```typescript
const socket = await NetworkDiagnostics.testWebSocket({
url: 'wss://ws.example.com/socket',
timeoutMs: 5000,
});
console.log(socket.open, socket.statusCode);
```
Use this when proxies or captive portals allow HTTPS pages but block WebSocket upgrade requests.
## Measure download speed
[Section titled âMeasure download speedâ](#measure-download-speed)
```typescript
const speed = await NetworkDiagnostics.testDownloadSpeed({
url: 'https://speed.example.com/5mb.bin',
maxBytes: 5 * 1024 * 1024,
timeoutMs: 30000,
});
console.log(speed.mbps);
```
Use your own static file endpoint so the result reflects the network path your app needs.
## Estimate packet loss
[Section titled âEstimate packet lossâ](#estimate-packet-loss)
```typescript
const loss = await NetworkDiagnostics.testPacketLoss({
mode: 'tcp',
host: 'api.example.com',
port: 443,
count: 10,
timeoutMs: 3000,
intervalMs: 250,
});
console.log(loss.lossPercent);
```
Raw ICMP ping is not portable in App Store and Play Store apps. This method measures application-level packet loss by repeating TCP or HTTP probes.
## Web fallback
[Section titled âWeb fallbackâ](#web-fallback)
The web implementation is meant for development. Browsers cannot open raw TCP sockets, and URL checks may be limited by CORS. Use iOS or Android builds for real support diagnostics.
# iOS Setup
> iOS behavior and setup notes for @capgo/capacitor-network-diagnostics.
No extra iOS permissions are required.
## Native behavior
[Section titled âNative behaviorâ](#native-behavior)
* Connection status uses `Network.framework`.
* URL and download tests use native `URLSession`.
* TCP port tests use `NWConnection`.
* WebSocket tests use `URLSessionWebSocketTask`.
## App Transport Security
[Section titled âApp Transport Securityâ](#app-transport-security)
Prefer `https://` and `wss://` targets. If you need to diagnose a plain `http://` or `ws://` endpoint, configure App Transport Security exceptions in the host appâs `Info.plist`.
## Interpreting constrained paths
[Section titled âInterpreting constrained pathsâ](#interpreting-constrained-paths)
iOS can mark a path as constrained or expensive. Treat those flags as context for support reports, not as hard failure states. A constrained path can still pass URL, port, and WebSocket checks.
# @capgo/capacitor-nfc
> Public API surface for the Capacitor NFC plugin.
## Overview
[Section titled âOverviewâ](#overview)
Public API surface for the Capacitor NFC plugin.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `startScanning` - Starts listening for NFC tags.
* `stopScanning` - Stops the ongoing NFC scanning session.
* `write` - Writes the provided NDEF records to the last discovered tag.
* `erase` - Attempts to erase the last discovered tag by writing an empty NDEF message.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | --------------------------------------------------------------------------- |
| `startScanning` | Starts listening for NFC tags. |
| `stopScanning` | Stops the ongoing NFC scanning session. |
| `write` | Writes the provided NDEF records to the last discovered tag. |
| `erase` | Attempts to erase the last discovered tag by writing an empty NDEF message. |
| `makeReadOnly` | Attempts to make the last discovered tag read-only. |
| `share` | Shares an NDEF message with another device via peer-to-peer (Android only). |
| `unshare` | Stops sharing previously provided NDEF message (Android only). |
| `getStatus` | Returns the current NFC adapter status. |
| `showSettings` | Opens the system settings page where the user can enable NFC. |
| `getPluginVersion` | Returns the version string baked into the native plugin. |
| `isSupported` | Checks whether the device has NFC hardware support. |
| `addListener` | See the source definitions for current behavior. |
| `addListener` | See the source definitions for current behavior. |
| `addListener` | See the source definitions for current behavior. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-nfc](https://github.com/Cap-go/capacitor-nfc/).
## Keep going from @capgo/capacitor-nfc
[Section titled âKeep going from @capgo/capacitor-nfcâ](#keep-going-from-capgocapacitor-nfc)
If you are using **@capgo/capacitor-nfc** to plan native plugin work, connect it with [Using @capgo/capacitor-nfc](/plugins/capacitor-nfc/) for the native capability in Using @capgo/capacitor-nfc, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-nfc and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-nfc` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-nfc
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `startScanning`
[Section titled âstartScanningâ](#startscanning)
Starts listening for NFC tags.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.startScanning();
```
### `stopScanning`
[Section titled âstopScanningâ](#stopscanning)
Stops the ongoing NFC scanning session.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.stopScanning();
```
### `write`
[Section titled âwriteâ](#write)
Writes the provided NDEF records to the last discovered tag.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.write({} as WriteTagOptions);
```
### `erase`
[Section titled âeraseâ](#erase)
Attempts to erase the last discovered tag by writing an empty NDEF message.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.erase();
```
### `makeReadOnly`
[Section titled âmakeReadOnlyâ](#makereadonly)
Attempts to make the last discovered tag read-only.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.makeReadOnly();
```
### `share`
[Section titled âshareâ](#share)
Shares an NDEF message with another device via peer-to-peer (Android only).
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.share({} as ShareTagOptions);
```
### `unshare`
[Section titled âunshareâ](#unshare)
Stops sharing previously provided NDEF message (Android only).
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.unshare();
```
### `getStatus`
[Section titled âgetStatusâ](#getstatus)
Returns the current NFC adapter status.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.getStatus();
```
### `showSettings`
[Section titled âshowSettingsâ](#showsettings)
Opens the system settings page where the user can enable NFC.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.showSettings();
```
### `isSupported`
[Section titled âisSupportedâ](#issupported)
Checks whether the device has NFC hardware support.
Returns `true` if NFC hardware is present on the device, regardless of whether NFC is currently enabled or disabled. Returns `false` if the device does not have NFC hardware.
Use this method to determine if NFC features should be shown in your appâs UI. To check if NFC is currently enabled, use `getStatus()`.
```typescript
import { CapacitorNfc } from '@capgo/capacitor-nfc';
await CapacitorNfc.isSupported();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `StartScanningOptions`
[Section titled âStartScanningOptionsâ](#startscanningoptions)
Options controlling the behaviour of .
```typescript
export interface StartScanningOptions {
/**
* iOS-only: closes the NFC session automatically after the first successful tag read.
* Defaults to `true`.
*/
invalidateAfterFirstRead?: boolean;
/**
* iOS-only: custom message displayed in the NFC system sheet while scanning.
*/
alertMessage?: string;
/**
* iOS-only: session type to use for NFC scanning.
* - `'ndef'`: Uses NFCNDEFReaderSession (default). Only detects NDEF-formatted tags.
* - `'tag'`: Uses NFCTagReaderSession. Detects both NDEF and non-NDEF tags (e.g., raw MIFARE tags).
* Allows reading UID from unformatted tags.
* **Requires** the `Near Field Communication Tag Reader Session Formats` entitlement
* in your app with the `TAG` format included. Without it the session will fail to
* start and the promise will reject with a `NO_NFC` error code.
* Defaults to `'ndef'` for backward compatibility.
*/
iosSessionType?: 'ndef' | 'tag';
/**
* Android-only: raw flags passed to `NfcAdapter.enableReaderMode`.
* Defaults to enabling all tag types with skipping NDEF checks.
*/
androidReaderModeFlags?: number;
}
```
### `WriteTagOptions`
[Section titled âWriteTagOptionsâ](#writetagoptions)
Options used when writing an NDEF message on the current tag.
```typescript
export interface WriteTagOptions {
/**
* Array of records that compose the NDEF message to be written.
*/
records: NdefRecord[];
/**
* When `true`, the plugin attempts to format NDEF-formattable tags before writing.
* Defaults to `true`.
*/
allowFormat?: boolean;
}
```
### `ShareTagOptions`
[Section titled âShareTagOptionsâ](#sharetagoptions)
Options used when sharing an NDEF message with another device using Android Beam / P2P mode.
```typescript
export interface ShareTagOptions {
records: NdefRecord[];
}
```
### `NfcStatus`
[Section titled âNfcStatusâ](#nfcstatus)
Possible NFC adapter states returned by .
```typescript
export type NfcStatus = 'NFC_OK' | 'NO_NFC' | 'NFC_DISABLED' | 'NDEF_PUSH_DISABLED';
```
### `NfcEvent`
[Section titled âNfcEventâ](#nfcevent)
Generic NFC discovery event dispatched by the plugin.
```typescript
export interface NfcEvent {
type: NfcEventType;
tag: NfcTag;
}
```
### `NfcStateChangeEvent`
[Section titled âNfcStateChangeEventâ](#nfcstatechangeevent)
Event emitted whenever the NFC adapter availability changes.
```typescript
export interface NfcStateChangeEvent {
status: NfcStatus;
enabled: boolean;
}
```
### `NdefRecord`
[Section titled âNdefRecordâ](#ndefrecord)
JSON structure representing a single NDEF record.
```typescript
export interface NdefRecord {
/**
* Type Name Format identifier.
*/
tnf: number;
/**
* Type field expressed as an array of byte values.
*/
type: number[];
/**
* Record identifier expressed as an array of byte values.
*/
id: number[];
/**
* Raw payload expressed as an array of byte values.
*/
payload: number[];
}
```
### `NfcEventType`
[Section titled âNfcEventTypeâ](#nfceventtype)
Event type describing the kind of NFC discovery that happened.
```typescript
export type NfcEventType = 'tag' | 'ndef' | 'ndef-mime' | 'ndef-formatable';
```
### `NfcTag`
[Section titled âNfcTagâ](#nfctag)
Representation of the full tag information returned by the native layers.
```typescript
export interface NfcTag {
/**
* Raw identifier bytes for the tag.
*/
id?: number[];
/**
* List of Android tech strings (e.g. `android.nfc.tech.Ndef`).
*/
techTypes?: string[];
/**
* Human readable tag type when available (e.g. `NFC Forum Type 2`, `MIFARE Ultralight`).
*/
type?: string | null;
/**
* Maximum writable size in bytes for tags that expose NDEF information.
*/
maxSize?: number | null;
/**
* Indicates whether the tag can be written to.
*/
isWritable?: boolean | null;
/**
* Indicates whether the tag can be permanently locked.
*/
canMakeReadOnly?: boolean | null;
/**
* Array of NDEF records discovered on the tag.
*/
ndefMessage?: NdefRecord[] | null;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-nfc](/plugins/capacitor-nfc/) for the native capability in Using @capgo/capacitor-nfc, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-notifications
> Capgo-managed native notifications for campaigns, badges, user lookup, delivery stats, and silent live-update checks.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-notifications` connects a Capacitor app to Capgoâs native notification pipeline. Your app registers a signed customer user ID with Capgo, Capgo keeps the latest active device state in Cloudflare Analytics Engine, and you can send notifications, set badges, inspect delivery stats, and trigger silent Capgo live-update checks from the Capgo dashboard or API.
The feature is designed to avoid a high-cardinality notification device table in Capgo Postgres. Postgres stores only low-cardinality control plane data such as app settings, platform credential metadata, campaign drafts, schedules, and aggregate campaign records. Active device state and notification events are append-only Analytics Engine rows.
## What Capgo Stores
[Section titled âWhat Capgo Storesâ](#what-capgo-stores)
| Data | Where it lives | Why |
| -------------------------------- | --------------------------- | ------------------------------------------------------------------------------ |
| Platform credential metadata | Capgo Postgres | Needed to manage app setup and permissions. |
| Private platform secrets | Worker environment | Avoids storing private credentials in customer-facing tables. |
| Campaign drafts and settings | Capgo Postgres | Low-cardinality control plane data. |
| Active device registration state | Cloudflare Analytics Engine | Cheap append-only active-device registry. |
| Notification events | Cloudflare Analytics Engine | Cheap stats for queued, sent, received, opened, failed, and background events. |
## Identity Model
[Section titled âIdentity Modelâ](#identity-model)
Capgo does not ask you to upload a list of users or maintain a device table.
* Your backend knows the real customer user ID.
* Your backend asks Capgo for a short-lived `identityProof`.
* The app registers with `externalId` and that proof after your own user authentication succeeds.
* Capgo derives deterministic internal keys for the recipient and the native install.
* Sending to the same `externalId` finds the userâs active devices again.
This lets you look up a userâs devices, set badges, and target notification campaigns without storing per-device notification rows in Capgo Postgres.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* Register devices with an authenticated external user ID.
* Refresh tags, attributes, consent, and token state on app start or token changes.
* Receive foreground notifications in JavaScript.
* Track notification received and opened events.
* Handle background data notifications.
* Set, clear, and increment app badges.
* Create Android notification channels.
* Send one-off notifications, campaign notifications, badge updates, and silent update checks.
* Trigger `@capgo/capacitor-updater` from a silent notification.
## Stats
[Section titled âStatsâ](#stats)
Capgo writes notification events to Cloudflare Analytics Engine so dashboard stats stay cheap:
* `queued`
* `sent`
* `provider_accepted`
* `received`
* `opened`
* `failed`
* `permission_changed`
* `background_started`
* `background_finished`
Stats are operational signals, not a permanent per-recipient ledger. Analytics Engine retention means active devices must refresh registration periodically.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| --------------------------------------- | ------------------------------------------------------------------------ |
| `configure` | Sets the Capgo app ID, API host, and updater integration behavior. |
| `register` | Registers the current install for an external customer user ID. |
| `setExternalId` | Changes the external customer user ID after login or account switch. |
| `setTags` | Replaces the tags used for audience targeting. |
| `setBadge` | Sets the native app badge. |
| `clearBadge` | Clears the native app badge. |
| `incrementBadge` | Increments the native app badge. |
| `enableUpdaterIntegration` | Enables silent update-check handling through `@capgo/capacitor-updater`. |
| `runUpdateCheck` | Runs the updater integration manually for testing. |
| `trackReceived` | Manually writes a received event. |
| `trackOpened` | Manually writes an opened event. |
| `addListener('notificationReceived')` | Fires when a notification is received while JavaScript is active. |
| `addListener('notificationOpened')` | Fires when a user opens a notification. |
| `addListener('backgroundNotification')` | Fires for background data notifications and exposes `finish()`. |
| `addListener('registrationChanged')` | Fires when the native push token changes. |
## Delivery Guarantees
[Section titled âDelivery Guaranteesâ](#delivery-guarantees)
Native push is at-least-once. A campaign can be retried, and a device can receive a duplicate if the platform accepts a repeated message. Use collapse IDs for update checks and make background work idempotent.
iOS background notifications are best-effort. The OS can throttle or skip background execution based on battery, user behavior, force-quit state, and system policy. Do not rely on silent notifications for hard deadlines.
## Keep going from @capgo/capacitor-notifications
[Section titled âKeep going from @capgo/capacitor-notificationsâ](#keep-going-from-capgocapacitor-notifications)
If you are using **@capgo/capacitor-notifications** to plan push messaging, connect it with [Getting Started](/docs/plugins/notifications/getting-started/) for setup, [Debugging](/docs/plugins/notifications/debugging/) for troubleshooting, [@capgo/capacitor-updater](/docs/plugins/updater/) for silent update checks, and [Capgo Plugin Directory](/plugins/) for other native plugins.
# Debugging
> Troubleshoot Capgo native notification setup, device registration, delivery, foreground events, background notifications, badges, and silent update checks.
Use this checklist when a notification does not register, does not arrive, does not show, or does not update Capgo stats.
## Start With The Device Record
[Section titled âStart With The Device Recordâ](#start-with-the-device-record)
Before debugging native code, confirm that Capgo can see the device.
1. Open the app and sign in as the user you want to test.
2. Call `CapgoNotifications.register(...)` after sign-in.
3. In Capgo, open **Notifications > Recipient lookup**.
4. Search by the same external customer ID.
You should see at least one active device with:
* `recipientKey`
* `deviceKey`
* platform `android` or `ios`
* permission state
* app version
* plugin version
* tags and attributes
If lookup returns no device, the send path cannot target that user.
## Add Temporary Debug Listeners
[Section titled âAdd Temporary Debug Listenersâ](#add-temporary-debug-listeners)
Add temporary listeners while testing. Remove noisy logs before shipping.
```typescript
await CapgoNotifications.addListener('registrationChanged', (token) => {
console.log('[CapgoNotifications] registrationChanged', token.value.slice(0, 12))
})
await CapgoNotifications.addListener('notificationReceived', (notification) => {
console.log('[CapgoNotifications] notificationReceived', notification.id, notification.data)
})
await CapgoNotifications.addListener('notificationOpened', (event) => {
console.log('[CapgoNotifications] notificationOpened', event.notification.id, event.actionId)
})
await CapgoNotifications.addListener('backgroundNotification', async (event) => {
console.log('[CapgoNotifications] backgroundNotification', event.notification.id, event.notification.data)
await event.finish()
})
```
## Collect This Information
[Section titled âCollect This Informationâ](#collect-this-information)
When debugging with your team or Capgo support, collect:
* Capgo app ID.
* App package ID or iOS bundle ID.
* Device platform and OS version.
* App version and build number.
* Plugin version.
* External customer ID.
* `recipientKey` and `deviceKey` from registration or recipient lookup.
* Campaign ID or notification ID.
* Whether the app was foreground, background, force-closed, or freshly installed.
* Device logs from the run that reproduced the issue.
## Use Device Logs
[Section titled âUse Device Logsâ](#use-device-logs)
Keep one real device connected while you send a test notification.
On Android:
* Open Android Studio Logcat.
* Filter by the app package ID.
* Watch for the notification permission request, native token refresh, message receive, and JavaScript listener logs.
* If a visible notification does not show, inspect the notification channel importance and Android 13+ permission state first.
On iOS:
* Run the app from Xcode on a physical device.
* Open the Xcode console or **Devices and Simulators** logs.
* Filter by the bundle ID and `CapgoNotifications`.
* Confirm `AppDelegate.swift` forwards remote notifications and that the background mode capability is enabled.
Send one foreground test first, then one background test, then one silent update-check test. This order separates JavaScript listener issues from OS background delivery limits.
## Registration Problems
[Section titled âRegistration Problemsâ](#registration-problems)
### CLI Setup Did Not Finish
[Section titled âCLI Setup Did Not Finishâ](#cli-setup-did-not-finish)
Run the setup command from the folder that contains `capacitor.config.*`:
```bash
npx @capgo/cli@latest notifications setup com.example.app
```
If the command cannot infer your app ID, pass it explicitly as shown above. If package installation fails, confirm Capgo has enabled private-preview package access for your npm account, then rerun the command.
### Device Does Not Appear In Recipient Lookup
[Section titled âDevice Does Not Appear In Recipient Lookupâ](#device-does-not-appear-in-recipient-lookup)
Check:
* `register` is called after your app has an authenticated user.
* `externalId` matches the user ID you search in the dashboard.
* `identityProof` was minted by your backend for the same `appId` and `externalId`.
* `appId` in `configure` matches the Capgo app.
* `consent` is not set to `false` unless the user opted out.
* The device has network access to `https://api.capgo.app`.
* The native push token was created. Use `registrationChanged` to confirm token refresh.
### Invalid Identity Proof
[Section titled âInvalid Identity Proofâ](#invalid-identity-proof)
The proof is bound to the Capgo app ID and external ID. If either value changes, mint a new proof.
Do not cache one proof forever or reuse a proof across apps. Mint it from your backend after login, return it to the app, and call `register`.
### Device Registered But Has Permission Denied
[Section titled âDevice Registered But Has Permission Deniedâ](#device-registered-but-has-permission-denied)
The plugin can register device state even when the user denied permission. You can still see the device, but visible notifications will not show.
Use a permission primer screen before the OS prompt. Explain what the user gets, then ask for permission only when the action makes sense.
## Delivery Problems
[Section titled âDelivery Problemsâ](#delivery-problems)
### Queued But Not Sent
[Section titled âQueued But Not Sentâ](#queued-but-not-sent)
Check:
* Platform credential status is `configured` in Capgo.
* The worker environment contains the exact secret reference shown by the dashboard.
* The package ID or bundle ID in the app matches the platform push setup.
* The target audience resolves to at least one active device.
* The campaign is not limited to a tag or segment the device does not have.
### Sent But Not Received
[Section titled âSent But Not Receivedâ](#sent-but-not-received)
Check:
* The device is online.
* The app was not force-stopped by the user.
* The OS notification permission is granted.
* Android battery restrictions are not blocking the app during testing.
* iOS Low Power Mode and background refresh restrictions are not affecting background delivery.
* The notification was not replaced by another notification with the same collapse ID.
Native push platforms can accept a notification and still delay, throttle, coalesce, or drop delivery later. Treat provider accepted stats as âaccepted for deliveryâ, not proof that the device displayed it.
### Received But Not Displayed
[Section titled âReceived But Not Displayedâ](#received-but-not-displayed)
Check:
* The app was not foregrounded. Foreground notifications are usually delivered to JavaScript so your app can decide what UI to show.
* Android notification channel importance is high enough to display an alert.
* Android 13+ notification permission is granted.
* iOS Focus, notification summary, or per-app notification settings are not hiding the notification.
* Badge clearing or app-open logic is not removing delivered notifications during testing.
## Background Notification Problems
[Section titled âBackground Notification Problemsâ](#background-notification-problems)
### Background Callback Does Not Run
[Section titled âBackground Callback Does Not Runâ](#background-callback-does-not-run)
Background notifications are best-effort. The OS can skip them.
Check:
* iOS has **Background Modes > Remote notifications** enabled.
* iOS `AppDelegate.swift` forwards remote notifications to `CapgoNotificationsRemoteNotification`.
* You test iOS background behavior on a physical device.
* The app was not force-quit by the user.
* The background handler calls `finish()`.
* Work inside the callback is short, network-safe, and idempotent.
On iOS, background pushes may be throttled if you send too many, use too much time, or the user rarely opens the app. This is expected platform behavior.
### Background Started But Not Finished
[Section titled âBackground Started But Not Finishedâ](#background-started-but-not-finished)
If stats show `background_started` without `background_finished`, the JavaScript handler likely threw, timed out, or did not call `finish()`.
Wrap the handler in `try/finally`:
```typescript
await CapgoNotifications.addListener('backgroundNotification', async (event) => {
try {
await doShortBackgroundWork(event.notification.data)
} finally {
await event.finish()
}
})
```
## Silent Update Check Problems
[Section titled âSilent Update Check Problemsâ](#silent-update-check-problems)
### Update Check Notification Arrives But No Update Installs
[Section titled âUpdate Check Notification Arrives But No Update Installsâ](#update-check-notification-arrives-but-no-update-installs)
Check:
* `@capgo/capacitor-updater` is installed and configured.
* `autoUpdater` is `true` or `enableUpdaterIntegration` was called.
* The appâs Notifications settings allow push update checks.
* The target device belongs to the channel you expect.
* The app has a newer bundle available in Capgo.
* Your update install mode is correct: `next` queues for the next restart or background cycle, `set` installs as soon as the updater can safely do it.
Run a manual check while the app is open:
```typescript
const result = await CapgoNotifications.runUpdateCheck({
enabled: true,
installMode: 'next',
})
console.log(result)
```
If the manual check returns `unavailable`, inspect the updater plugin setup first.
## Badge Problems
[Section titled âBadge Problemsâ](#badge-problems)
Check:
* The target resolves to the right device in recipient lookup.
* The platform supports app badges for the launcher or home screen being tested.
* The user has not disabled badges in OS notification settings.
* The app does not clear badges immediately on startup.
* You are not racing local `setBadge` calls against backend badge sends.
## Stats Problems
[Section titled âStats Problemsâ](#stats-problems)
### Stats Look Duplicated
[Section titled âStats Look Duplicatedâ](#stats-look-duplicated)
Notification sending is at-least-once. Queue retry and platform retry can duplicate a send. Use notification IDs and collapse IDs when your app action must be idempotent.
### Stats Are Missing For Old Devices
[Section titled âStats Are Missing For Old Devicesâ](#stats-are-missing-for-old-devices)
The Analytics Engine registry is for active devices, not a forever database. The plugin should refresh registration on app start, token refresh, external ID change, and periodically before the active-device retention window.
### Open Events Are Missing
[Section titled âOpen Events Are Missingâ](#open-events-are-missing)
Check:
* The notification includes a stable `id`.
* `notificationOpened` listener is registered during app startup.
* The app is not replacing the native open flow with custom code before the plugin sees it.
* The user actually tapped the notification rather than opening the app manually.
## API Debug Commands
[Section titled âAPI Debug Commandsâ](#api-debug-commands)
Lookup a recipient:
```bash
curl -X POST 'https://api.capgo.app/notifications/recipients/lookup' \
-H 'Content-Type: application/json' \
-H 'x-api-key: CAPGO_API_KEY' \
-d '{
"appId": "com.example.app",
"externalId": "customer-user-123"
}'
```
Read stats:
```bash
curl 'https://api.capgo.app/notifications/stats?app_id=com.example.app&days=7' \
-H 'x-api-key: CAPGO_API_KEY'
```
Send a foreground test:
```bash
curl -X POST 'https://api.capgo.app/notifications/send' \
-H 'Content-Type: application/json' \
-H 'x-api-key: CAPGO_API_KEY' \
-d '{
"appId": "com.example.app",
"target": { "externalId": "customer-user-123" },
"payload": {
"title": "Capgo test",
"body": "Open this notification to test events.",
"data": { "debug": "true" }
}
}'
```
## Common Root Causes
[Section titled âCommon Root Causesâ](#common-root-causes)
| Symptom | Likely cause |
| ------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Device missing from lookup | `register` not called, proof mismatch, consent false, app ID mismatch. |
| Permission denied | OS prompt denied or not requested yet. |
| Queued but no sent stats | Platform credentials are missing or disabled. |
| Sent but no received stats | Device offline, OS throttling, app force-stopped, or token invalid. |
| Foreground notification logs but no banner | App is foregrounded and must show its own in-app UI. |
| Background never runs on iOS | Missing capabilities, missing AppDelegate forwarding, force-quit app, or OS throttling. |
| Update check does nothing | Updater integration disabled, no newer bundle, wrong channel, or install mode misunderstood. |
| Badge resets | App startup code clears badges or local and backend badge writes race. |
## Keep going from Debugging
[Section titled âKeep going from Debuggingâ](#keep-going-from-debugging)
After the device registers and a test notification works, use [Getting Started](/docs/plugins/notifications/getting-started/) to wire badges, campaign targeting, and silent update checks into your production app.
# Getting Started
> Install @capgo/capacitor-notifications, configure platform credentials, register devices, send test notifications, and enable silent update checks.
`@capgo/capacitor-notifications` is the first-party Capgo plugin for native iOS and Android push notifications. It is built for Capgoâs dashboard, public API, Analytics Engine device registry, campaign stats, badge updates, and silent live-update checks.
## Requirements
[Section titled âRequirementsâ](#requirements)
* A Capacitor app already added to Capgo.
* Access to the Capgo appâs Notifications tab.
* A Capgo API key with write access for backend proof minting and API sends.
* iOS and/or Android platform push authority for the app.
* `@capgo/capacitor-updater` if you want silent push update checks.
## 1. Configure Capgo Platform Credentials
[Section titled â1. Configure Capgo Platform Credentialsâ](#1-configure-capgo-platform-credentials)
Open the app in Capgo, then go to **Notifications**.
Add one platform credential entry for each platform you want to support:
* **Android** - app package ID and Android push project metadata.
* **iOS** - bundle ID, team ID, key ID, and the matching iOS push key metadata.
Capgo shows the exact environment secret name that must exist in the API worker before the platform is marked configured. The dashboard stores metadata and the expected secret reference. The private credential itself stays in the worker environment.
## 2. Install
[Section titled â2. Installâ](#2-install)
For the fastest setup, run the Capgo CLI from your app project:
```bash
npx @capgo/cli@latest notifications setup com.example.app
```
The command installs the notification package, saves the Capacitor plugin config, creates a small helper file, and runs Capacitor sync. Use this path for new apps unless you need to wire every file manually.
Manual install:
```bash
npm install @capgo/capacitor-notifications @capgo/capacitor-updater
npx cap sync
```
If you are not using silent Capgo update checks, you can omit `@capgo/capacitor-updater`.
## 3. Configure The Plugin
[Section titled â3. Configure The Pluginâ](#3-configure-the-plugin)
Configure the plugin once when your app starts.
```typescript
import { CapgoNotifications } from '@capgo/capacitor-notifications'
await CapgoNotifications.configure({
appId: 'com.example.app',
autoUpdater: true,
updateInstallMode: 'next',
})
```
Use `updateInstallMode: 'next'` to download an update and install it on the next restart or background cycle. Use `updateInstallMode: 'set'` only when you want Capgo to install the update as soon as the updater can safely do it.
## 4. Mint An Identity Proof
[Section titled â4. Mint An Identity Proofâ](#4-mint-an-identity-proof)
Do not put your Capgo API key in the mobile app. Your backend should ask Capgo for an `identityProof` after your own user authentication succeeds.
```bash
curl -X POST 'https://api.capgo.app/notifications/recipients/proof' \
-H 'Content-Type: application/json' \
-H 'x-api-key: CAPGO_API_KEY' \
-d '{
"appId": "com.example.app",
"externalId": "customer-user-123"
}'
```
Return the `identityProof` to the app with your own session response.
## 5. Register The Device
[Section titled â5. Register The Deviceâ](#5-register-the-device)
Register only after you know which customer user is signed in.
```typescript
const registration = await CapgoNotifications.register({
externalId: 'customer-user-123',
identityProof,
tags: ['paid', 'beta'],
attributes: {
plan: 'team',
locale: 'en-US',
},
consent: true,
})
console.log(registration.recipientKey, registration.deviceKey)
```
Call `register` again when:
* The app starts.
* The native push token changes.
* The signed-in user changes.
* Tags, attributes, or consent change.
* The app has not refreshed registration for a long time.
## 6. Add Event Listeners
[Section titled â6. Add Event Listenersâ](#6-add-event-listeners)
Register listeners during app startup so foreground, opened, and background events are visible to JavaScript.
```typescript
await CapgoNotifications.addListener('registrationChanged', () => {
void CapgoNotifications.register({
externalId: currentUser.id,
identityProof: currentUser.capgoNotificationProof,
tags: currentUser.notificationTags,
consent: currentUser.pushConsent,
})
})
await CapgoNotifications.addListener('notificationReceived', (notification) => {
console.log('Notification received', notification)
})
await CapgoNotifications.addListener('notificationOpened', (event) => {
console.log('Notification opened', event.notification.id)
})
await CapgoNotifications.addListener('backgroundNotification', async (event) => {
try {
console.log('Background notification', event.notification.data)
} finally {
await event.finish()
}
})
```
Always call `finish()` for background notifications after your work is done. Keep the work short and idempotent.
## 7. iOS Setup
[Section titled â7. iOS Setupâ](#7-ios-setup)
In Xcode, open the app target and enable:
* **Push Notifications**
* **Background Modes > Remote notifications**
Forward remote notifications from `ios/App/App/AppDelegate.swift`:
```swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
NotificationCenter.default.post(name: Notification.Name("CapgoNotificationsRemoteNotification"), object: userInfo)
completionHandler(.newData)
}
```
Then run:
```bash
npx cap sync ios
```
Use a physical iOS device when testing background notifications. Simulators are useful for UI work but do not represent production background push behavior.
## 8. Android Setup
[Section titled â8. Android Setupâ](#8-android-setup)
Run:
```bash
npx cap sync android
```
Then verify:
* Your Android platform credential is configured in Capgo.
* The app package ID matches the package ID used for platform push setup.
* Android 13+ notification permission is requested before expecting visible notifications.
* The app has a notification icon and channel strategy that matches your brand.
* You test on a physical device or emulator with Google Play services.
Create a default Android channel when your app starts:
```typescript
await CapgoNotifications.configure({ appId: 'com.example.app' })
await CapgoNotifications.register({
externalId: currentUser.id,
identityProof: currentUser.capgoNotificationProof,
consent: true,
})
```
The plugin declares the Android push messaging service. Keep app backup, data extraction, network security, and notification icon policy in the host app.
## 9. Send A Test Notification
[Section titled â9. Send A Test Notificationâ](#9-send-a-test-notification)
Use **Notifications > Test send** in Capgo, or call the public API from your backend:
```bash
curl -X POST 'https://api.capgo.app/notifications/send' \
-H 'Content-Type: application/json' \
-H 'x-api-key: CAPGO_API_KEY' \
-d '{
"appId": "com.example.app",
"target": { "externalId": "customer-user-123" },
"payload": {
"title": "Hello from Capgo",
"body": "This is a test notification.",
"data": { "screen": "inbox" }
}
}'
```
For a campaign, create it in the dashboard or call `/notifications/campaigns`, then send to an external ID, tag, segment, or broadcast audience.
## 10. Set Badges
[Section titled â10. Set Badgesâ](#10-set-badges)
```typescript
await CapgoNotifications.setBadge(4)
await CapgoNotifications.incrementBadge()
await CapgoNotifications.clearBadge()
```
From your backend:
```bash
curl -X POST 'https://api.capgo.app/notifications/badge' \
-H 'Content-Type: application/json' \
-H 'x-api-key: CAPGO_API_KEY' \
-d '{
"appId": "com.example.app",
"target": { "externalId": "customer-user-123" },
"badge": 4
}'
```
## 11. Enable Silent Update Checks
[Section titled â11. Enable Silent Update Checksâ](#11-enable-silent-update-checks)
Silent update checks connect this plugin with `@capgo/capacitor-updater`.
In the app:
```typescript
await CapgoNotifications.enableUpdaterIntegration({
enabled: true,
installMode: 'next',
})
```
In Capgo, enable **Push update to users** in the appâs Notifications settings. Then send an update check from the dashboard or API:
```bash
curl -X POST 'https://api.capgo.app/notifications/update-check' \
-H 'Content-Type: application/json' \
-H 'x-api-key: CAPGO_API_KEY' \
-d '{
"appId": "com.example.app",
"target": { "externalId": "customer-user-123" },
"installMode": "next"
}'
```
The notification is silent and uses a collapse ID so repeated update checks replace each other when the platform supports collapse behavior.
## Validation Checklist
[Section titled âValidation Checklistâ](#validation-checklist)
* The app appears in Capgo recipient lookup for the expected `externalId`.
* Permission is `granted` or the user has accepted notification permission.
* The registered platform is `android` or `ios`.
* `registrationChanged` fires after a token refresh.
* A foreground test logs `notificationReceived`.
* Opening a notification logs `notificationOpened`.
* Dashboard stats show queued and sent events, then received/opened when the device reports them.
* Silent update checks log a result from `runUpdateCheck` or the updater integration.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If setup does not work, use [Debugging](/docs/plugins/notifications/debugging/) before changing app code. Most failures are caused by identity proof mismatch, platform credential setup, OS permission state, background throttling, or app/package ID mismatch.
# @capgo/capacitor-passkey
> Keep your browser-style WebAuthn code in a Capacitor app while the plugin handles native passkey calls and native host patching.
Browser-style API
Keep `navigator.credentials.create()` and `navigator.credentials.get()` in your app instead of rewriting your passkey flow around a custom API.
Minimal app changes
Add plugin config once, call `CapacitorPasskey.autoShimWebAuthn()` during bootstrap, and keep the rest of your WebAuthn code close to the browser implementation.
Build-time native wiring
The plugin patches the generated iOS and Android host projects during sync so you do not need to keep hand-editing those files.
Platform notes included
Follow the [Getting Started](/docs/plugins/passkey/getting-started/), [iOS setup](/docs/plugins/passkey/ios/), [Android setup](/docs/plugins/passkey/android/), and [backend notes](/docs/plugins/passkey/backend/).
## Core API
[Section titled âCore APIâ](#core-api)
* `shimWebAuthn(options?)` installs the browser-style shim immediately, with an optional HTTPS origin override.
* `getConfiguration()` reads the resolved runtime config from `plugins.CapacitorPasskey`.
* `autoShimWebAuthn(options?)` reads that config and installs the shim in one step during app bootstrap.
* `createCredential(options)` and `getCredential(options)` call the native passkey APIs directly with JSON-safe WebAuthn payloads.
* `isSupported()` reports runtime availability and `getPluginVersion()` returns the native implementation version marker.
## Keep going from @capgo/capacitor-passkey
[Section titled âKeep going from @capgo/capacitor-passkeyâ](#keep-going-from-capgocapacitor-passkey)
If you are using **@capgo/capacitor-passkey** to plan authentication and account flows, connect it with [Using @capgo/capacitor-passkey](/plugins/capacitor-passkey/) for the native capability in Using @capgo/capacitor-passkey, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication, and [SSO (Enterprise)](/docs/webapp/enterprise-sso/) for the implementation detail in SSO (Enterprise).
# Android Setup
> Configure passkeys on Android for @capgo/capacitor-passkey with Digital Asset Links and assetlinks.json.
On Android, passkeys work with your website when the app and the relying-party domain are connected through Digital Asset Links.
## What the plugin handles
[Section titled âWhat the plugin handlesâ](#what-the-plugin-handles)
After you add the plugin config and run `bunx cap sync`, the plugin patches the generated Android host project:
* injects the `asset_statements` manifest metadata
* writes the generated string resource referenced by that metadata
## What you still need to host
[Section titled âWhat you still need to hostâ](#what-you-still-need-to-host)
You must publish `assetlinks.json` on the relying-party domain:
```text
https://signin.example.com/.well-known/assetlinks.json
```
Example:
```json
[
{
"relation": [
"delegate_permission/common.handle_all_urls",
"delegate_permission/common.get_login_creds"
],
"target": {
"namespace": "android_app",
"package_name": "app.capgo.passkey.example",
"sha256_cert_fingerprints": [
"AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
]
}
}
]
```
## Checklist
[Section titled âChecklistâ](#checklist)
1. Set `origin` and `domains` in `plugins.CapacitorPasskey` in `capacitor.config.*`.
2. Run `bunx cap sync`.
3. Use your real Android package name in `assetlinks.json`.
4. Add every signing certificate fingerprint you need, including debug or internal signing keys if you test those builds.
5. Host the file on the same domain you use as the relying-party ID.
## Important behavior difference from a browser
[Section titled âImportant behavior difference from a browserâ](#important-behavior-difference-from-a-browser)
With Digital Asset Links configured, Android can use the same relying party and passkeys as your website. The remaining difference is the literal origin reported in native `clientDataJSON`.
* A normal Android app does not behave like a privileged browser.
* The assertion origin can be tied to the Android app signature instead of your website origin.
* If your backend strictly validates `clientDataJSON.origin`, accept the Android app origin alongside the website origin.
## Keep going from Android Setup
[Section titled âKeep going from Android Setupâ](#keep-going-from-android-setup)
If you are using **Android Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-passkey](/plugins/capacitor-passkey/) for the native capability in Using @capgo/capacitor-passkey, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Backend Notes
> Understand the backend contract for @capgo/capacitor-passkey, including WebAuthn challenge handling and Android origin validation.
Your backend still owns the normal WebAuthn ceremony:
* generate registration and authentication challenges
* verify attestation and assertion responses
* enforce relying-party ID and challenge validation
* store credentials and counters the same way you would for a browser flow
## What stays the same
[Section titled âWhat stays the sameâ](#what-stays-the-same)
The plugin is designed to preserve the front-end shape of your existing WebAuthn code.
* On the web, it forwards to the real browser WebAuthn API.
* On native Capacitor, it returns browser-like credential objects backed by native passkey APIs.
* Your backend can keep the same challenge and verification pipeline.
## What changes on Android
[Section titled âWhat changes on Androidâ](#what-changes-on-android)
Android native passkeys are not identical to a browser trust model.
* Digital Asset Links let Android share the same relying party and credential ecosystem as your website.
* The literal `clientDataJSON.origin` value can still differ from the website origin.
* If your server rejects anything except `https://your-domain`, Android native assertions can fail even when the passkey is otherwise valid.
## Recommended backend rule
[Section titled âRecommended backend ruleâ](#recommended-backend-rule)
Allow the expected browser origin and the expected Android app origin for the same relying party when you support native Android passkeys.
That gives you:
* browser support for the website
* native passkey support in the Capacitor app
* one passkey ecosystem for the same relying-party domain
## If you need direct JSON-safe calls
[Section titled âIf you need direct JSON-safe callsâ](#if-you-need-direct-json-safe-calls)
If your backend already returns `PublicKeyCredentialCreationOptionsJSON` and `PublicKeyCredentialRequestOptionsJSON`, you can also use the direct plugin API instead of the browser-style shim:
```ts
import { CapacitorPasskey } from '@capgo/capacitor-passkey';
const registration = await CapacitorPasskey.createCredential({
origin: 'https://signin.example.com',
publicKey: registrationOptionsFromBackend,
});
const authentication = await CapacitorPasskey.getCredential({
origin: 'https://signin.example.com',
publicKey: requestOptionsFromBackend,
});
```
## Keep going from Backend Notes
[Section titled âKeep going from Backend Notesâ](#keep-going-from-backend-notes)
If you are using **Backend Notes** to plan authentication and account flows, connect it with [Using @capgo/capacitor-passkey](/plugins/capacitor-passkey/) for the native capability in Using @capgo/capacitor-passkey, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Getting Started
> Install @capgo/capacitor-passkey, configure the plugin once, and keep your browser-style WebAuthn code in a Capacitor app.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-passkey` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```sh
bun add @capgo/capacitor-passkey
```
2. **Sync native projects**
```sh
bunx cap sync
```
3. **Add the plugin config**
```ts
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'app.capgo.passkey.example',
appName: 'My App',
webDir: 'dist',
plugins: {
CapacitorPasskey: {
origin: 'https://signin.example.com',
autoShim: true,
domains: ['signin.example.com'],
},
},
};
export default config;
```
4. **Install the shim during bootstrap**
```ts
import { CapacitorPasskey } from '@capgo/capacitor-passkey';
await CapacitorPasskey.autoShimWebAuthn();
```
5. **Keep your normal WebAuthn flow**
```ts
const registration = await navigator.credentials.create({
publicKey: registrationOptions,
});
const authentication = await navigator.credentials.get({
publicKey: requestOptions,
});
```
## What the plugin config does
[Section titled âWhat the plugin config doesâ](#what-the-plugin-config-does)
The config is read from `plugins.CapacitorPasskey` in `capacitor.config.*`.
* `origin`: primary HTTPS relying-party origin used by the shim and direct API
* `domains`: extra relying-party hostnames to patch into native config during sync
* `autoShim`: defaults to `true` and controls the native `cap sync` auto-configuration hook
## What sync patches for you
[Section titled âWhat sync patches for youâ](#what-sync-patches-for-you)
When you run `bunx cap sync`, the plugin updates the generated native host project:
* iOS: associated domains entitlements and Xcode entitlements wiring when needed
* Android: `asset_statements` metadata and the generated resource used by the manifest
The hook does not publish your website trust files for you. You still need to host:
* `https://your-domain/.well-known/apple-app-site-association`
* `https://your-domain/.well-known/assetlinks.json`
## Platform guides
[Section titled âPlatform guidesâ](#platform-guides)
[ iOS setup](/docs/plugins/passkey/ios/)
[Associated Domains and `apple-app-site-association`.](/docs/plugins/passkey/ios/)
[ Android setup](/docs/plugins/passkey/android/)
[Digital Asset Links and `assetlinks.json`.](/docs/plugins/passkey/android/)
[ Backend notes](/docs/plugins/passkey/backend/)
[Origin validation and Android caveats.](/docs/plugins/passkey/backend/)
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan authentication and account flows, connect it with [Using @capgo/capacitor-passkey](/plugins/capacitor-passkey/) for the native capability in Using @capgo/capacitor-passkey, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# iOS Setup
> Configure passkeys on iOS for @capgo/capacitor-passkey with Associated Domains and the apple-app-site-association file.
On iOS, passkeys only work when the app is associated with the same relying-party domain as the website.
## What the plugin handles
[Section titled âWhat the plugin handlesâ](#what-the-plugin-handles)
After you add the plugin config and run `bunx cap sync`, the plugin patches the generated iOS host project so you do not need to keep editing it manually:
* adds the `webcredentials:` associated domains entries for the configured domains
* wires `CODE_SIGN_ENTITLEMENTS` when the generated app target does not already point at an entitlements file
## What you still need to host
[Section titled âWhat you still need to hostâ](#what-you-still-need-to-host)
You must publish `apple-app-site-association` on the relying-party domain:
```text
https://signin.example.com/.well-known/apple-app-site-association
```
Example:
```json
{
"webcredentials": {
"apps": ["ABCDE12345.app.capgo.passkey.example"]
}
}
```
## Checklist
[Section titled âChecklistâ](#checklist)
1. Set `origin` and `domains` in `plugins.CapacitorPasskey` in `capacitor.config.*`.
2. Run `bunx cap sync`.
3. Confirm your Apple Team ID and app bundle ID, then build the `TEAMID.bundleId` value for the association file.
4. Host `apple-app-site-association` with HTTP `200` and no `.json` extension.
5. Make sure the relying-party ID used by your backend matches the associated domain.
## Notes
[Section titled âNotesâ](#notes)
* The website file must be served from the exact passkey domain you use as the relying-party ID.
* On iOS 17.4 and newer, the plugin uses the browser-style client-data API so the configured HTTPS origin is reflected in `clientDataJSON`.
* The plugin can patch native project files during sync, but it cannot create or host the website association file on your domain.
## Keep going from iOS Setup
[Section titled âKeep going from iOS Setupâ](#keep-going-from-ios-setup)
If you are using **iOS Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-passkey](/plugins/capacitor-passkey/) for the native capability in Using @capgo/capacitor-passkey, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# @capgo/capacitor-pay
> Capacitor plugin to trigger native payment for iOS(Apple pay) and Android(Google Pay).
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin to trigger native payment for iOS(Apple pay) and Android(Google Pay).
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `isPayAvailable` - Checks whether native pay is available on the current platform. On iOS this evaluates Apple Pay, on Android it evaluates Google Pay.
* `requestPayment` - Presents the native pay sheet for the current platform. Provide the Apple Pay configuration on iOS and the Google Pay configuration on Android.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `isPayAvailable` | Checks whether native pay is available on the current platform. On iOS this evaluates Apple Pay, on Android it evaluates Google Pay. |
| `requestPayment` | Presents the native pay sheet for the current platform. Provide the Apple Pay configuration on iOS and the Google Pay configuration on Android. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-pay](https://github.com/Cap-go/capacitor-pay/).
## Keep going from @capgo/capacitor-pay
[Section titled âKeep going from @capgo/capacitor-payâ](#keep-going-from-capgocapacitor-pay)
If you are using **@capgo/capacitor-pay** to plan payments and purchases, connect it with [Using @capgo/capacitor-pay](/plugins/capacitor-pay/) for the native capability in Using @capgo/capacitor-pay, [Capgo Pricing](/pricing/) for the product workflow in Capgo Pricing, [Payment system](/docs/webapp/payment/) for the implementation detail in Payment system, [@capgo/native-purchases](/docs/plugins/native-purchases/) for the implementation detail in @capgo/native-purchases, and [Getting Started](/docs/plugins/native-purchases/getting-started/) for the implementation detail in Getting Started.
# Getting Started
> Install @capgo/capacitor-pay and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-pay` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-pay
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { Pay } from '@capgo/capacitor-pay';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `isPayAvailable`
[Section titled âisPayAvailableâ](#ispayavailable)
Checks whether native pay is available on the current platform. On iOS this evaluates Apple Pay, on Android it evaluates Google Pay.
```typescript
import { Pay } from '@capgo/capacitor-pay';
await Pay.isPayAvailable();
```
### `requestPayment`
[Section titled ârequestPaymentâ](#requestpayment)
Presents the native pay sheet for the current platform. Provide the Apple Pay configuration on iOS and the Google Pay configuration on Android.
This promise is the completion path on both platforms.
```typescript
import { Pay } from '@capgo/capacitor-pay';
await Pay.requestPayment({} as PayPaymentOptions);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `PayAvailabilityOptions`
[Section titled âPayAvailabilityOptionsâ](#payavailabilityoptions)
```typescript
export interface PayAvailabilityOptions {
apple?: ApplePayAvailabilityOptions;
google?: GooglePayAvailabilityOptions;
}
```
### `PayAvailabilityResult`
[Section titled âPayAvailabilityResultâ](#payavailabilityresult)
```typescript
export interface PayAvailabilityResult {
available: boolean;
platform: PayPlatform;
apple?: ApplePayAvailabilityResult;
google?: GooglePayAvailabilityResult;
}
```
### `PayPaymentOptions`
[Section titled âPayPaymentOptionsâ](#paypaymentoptions)
```typescript
export interface PayPaymentOptions {
apple?: ApplePayPaymentOptions;
google?: GooglePayPaymentOptions;
}
```
### `PayPaymentResult`
[Section titled âPayPaymentResultâ](#paypaymentresult)
```typescript
export type PayPaymentResult = ApplePayRequestPaymentResult | GooglePayRequestPaymentResult;
```
### `ApplePayAvailabilityOptions`
[Section titled âApplePayAvailabilityOptionsâ](#applepayavailabilityoptions)
```typescript
export interface ApplePayAvailabilityOptions {
/**
* Optional list of payment networks you intend to use.
* Passing networks determines the return value of `canMakePaymentsUsingNetworks`.
*/
supportedNetworks?: ApplePayNetwork[];
}
```
### `GooglePayAvailabilityOptions`
[Section titled âGooglePayAvailabilityOptionsâ](#googlepayavailabilityoptions)
```typescript
export interface GooglePayAvailabilityOptions {
/**
* Environment used to construct the Google Payments client. Defaults to `'test'`.
*/
environment?: GooglePayEnvironment;
/**
* Raw `IsReadyToPayRequest` JSON as defined by the Google Pay API.
* Supply the card networks and auth methods you intend to support at runtime.
*
* @see https://developers.google.com/pay/api/android/reference/request-objects#IsReadyToPayRequest
*/
isReadyToPayRequest?: GooglePayIsReadyToPayRequest;
}
```
### `PayPlatform`
[Section titled âPayPlatformâ](#payplatform)
```typescript
export type PayPlatform = 'ios' | 'android' | 'web';
```
### `ApplePayAvailabilityResult`
[Section titled âApplePayAvailabilityResultâ](#applepayavailabilityresult)
```typescript
export interface ApplePayAvailabilityResult {
/**
* Indicates whether the device can make Apple Pay payments in general.
*/
canMakePayments: boolean;
/**
* Indicates whether the device can make Apple Pay payments with the supplied networks.
*/
canMakePaymentsUsingNetworks: boolean;
}
```
### `GooglePayAvailabilityResult`
[Section titled âGooglePayAvailabilityResultâ](#googlepayavailabilityresult)
```typescript
export interface GooglePayAvailabilityResult {
/**
* Whether the user is able to provide payment information through the Google Pay payment sheet.
*/
isReady: boolean;
/**
* The current user's ability to pay with one or more of the payment methods specified in `IsReadyToPayRequest.allowedPaymentMethods`.
*
* This property only exists if `IsReadyToPayRequest.existingPaymentMethodRequired` was set to `true`. The property value will always be `true` if the request is configured for a test environment.
*/
paymentMethodPresent: boolean | undefined;
}
```
### `ApplePayPaymentOptions`
[Section titled âApplePayPaymentOptionsâ](#applepaypaymentoptions)
```typescript
export interface ApplePayPaymentOptions {
/**
* Merchant identifier created in the Apple Developer portal.
*/
merchantIdentifier: string;
/**
* Two-letter ISO 3166 country code.
*/
countryCode: string;
/**
* Three-letter ISO 4217 currency code.
*/
currencyCode: string;
/**
* Payment summary items displayed in the Apple Pay sheet.
*/
paymentSummaryItems: ApplePaySummaryItem[];
/**
* Card networks to support.
*/
supportedNetworks: ApplePayNetwork[];
/**
* Merchant payment capabilities. Defaults to ['3DS'] when omitted.
*/
merchantCapabilities?: ApplePayMerchantCapability[];
/**
* Contact fields that must be supplied for shipping.
*/
requiredShippingContactFields?: ApplePayContactField[];
/**
* Contact fields that must be supplied for billing.
*/
requiredBillingContactFields?: ApplePayContactField[];
/**
* Controls the shipping flow presented to the user.
*/
shippingType?: ApplePayShippingType;
/**
* Optional ISO 3166 country codes where the merchant is supported.
*/
supportedCountries?: string[];
/**
* Optional opaque application data passed back in the payment token.
*/
applicationData?: string;
/**
* Recurring payment configuration (iOS 16+).
*/
recurringPaymentRequest?: ApplePayRecurringPaymentRequest;
}
```
### `GooglePayPaymentOptions`
[Section titled âGooglePayPaymentOptionsâ](#googlepaypaymentoptions)
```typescript
export interface GooglePayPaymentOptions {
/**
* Environment used to construct the Google Payments client. Defaults to `'test'`.
*/
environment?: GooglePayEnvironment;
/**
* Raw `PaymentDataRequest` JSON as defined by the Google Pay API.
* Provide transaction details, merchant info, and tokenization parameters.
*
* @see https://developers.google.com/pay/api/android/reference/request-objects#PaymentDataRequest
*/
paymentDataRequest: GooglePayPaymentDataRequest;
}
```
### `ApplePayRequestPaymentResult`
[Section titled âApplePayRequestPaymentResultâ](#applepayrequestpaymentresult)
```typescript
export interface ApplePayRequestPaymentResult {
/**
* Platform that resolved the payment request.
*/
platform: 'ios';
/**
* Apple Pay payment payload.
*/
apple: ApplePayPaymentResult;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-pay](/plugins/capacitor-pay/) for the native capability in Using @capgo/capacitor-pay, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-pdf-generator
> Generate PDF files from HTML strings or URLs on iOS and Android.
## Overview
[Section titled âOverviewâ](#overview)
Generate PDF files from HTML strings or URLs on iOS and Android.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `fromURL` - Generates a PDF from the provided URL.
* `fromData` - Generates a PDF from a raw HTML string.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ---------------------------------------- |
| `fromURL` | Generates a PDF from the provided URL. |
| `fromData` | Generates a PDF from a raw HTML string. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-pdf-generator](https://github.com/Cap-go/capacitor-pdf-generator/).
## Keep going from @capgo/capacitor-pdf-generator
[Section titled âKeep going from @capgo/capacitor-pdf-generatorâ](#keep-going-from-capgocapacitor-pdf-generator)
If you are using **@capgo/capacitor-pdf-generator** to plan storage and file handling, connect it with [Using @capgo/capacitor-pdf-generator](/plugins/capacitor-pdf-generator/) for the native capability in Using @capgo/capacitor-pdf-generator, [@capgo/capacitor-data-storage-sqlite](/docs/plugins/data-storage-sqlite/) for the implementation detail in @capgo/capacitor-data-storage-sqlite, [Using @capgo/capacitor-data-storage-sqlite](/plugins/capacitor-data-storage-sqlite/) for the native capability in Using @capgo/capacitor-data-storage-sqlite, [@capgo/capacitor-file](/docs/plugins/file/) for the implementation detail in @capgo/capacitor-file, and [Using @capgo/capacitor-file](/plugins/capacitor-file/) for the native capability in Using @capgo/capacitor-file.
# Getting Started
> Install @capgo/capacitor-pdf-generator and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-pdf-generator` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-pdf-generator
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { PdfGenerator } from '@capgo/capacitor-pdf-generator';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `fromURL`
[Section titled âfromURLâ](#fromurl)
Generates a PDF from the provided URL.
```typescript
import { PdfGenerator } from '@capgo/capacitor-pdf-generator';
await PdfGenerator.fromURL({} as PdfGeneratorFromUrlOptions);
```
### `fromData`
[Section titled âfromDataâ](#fromdata)
Generates a PDF from a raw HTML string.
```typescript
import { PdfGenerator } from '@capgo/capacitor-pdf-generator';
await PdfGenerator.fromData({} as PdfGeneratorFromDataOptions);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `PdfGeneratorFromUrlOptions`
[Section titled âPdfGeneratorFromUrlOptionsâ](#pdfgeneratorfromurloptions)
```typescript
export interface PdfGeneratorFromUrlOptions extends PdfGeneratorCommonOptions {
url: string;
}
```
### `PdfGeneratorResult`
[Section titled âPdfGeneratorResultâ](#pdfgeneratorresult)
```typescript
export type PdfGeneratorResult =
| {
type: 'base64';
base64: string;
}
| {
type: 'share';
completed: boolean;
};
```
### `PdfGeneratorFromDataOptions`
[Section titled âPdfGeneratorFromDataOptionsâ](#pdfgeneratorfromdataoptions)
```typescript
export interface PdfGeneratorFromDataOptions extends PdfGeneratorCommonOptions {
/**
* HTML document to render.
*/
data: string;
/**
* Base URL to use when resolving relative resources inside the HTML string.
* When omitted, `about:blank` is used.
*/
baseUrl?: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan storage and file handling, connect it with [Using @capgo/capacitor-pdf-generator](/plugins/capacitor-pdf-generator/) for the native capability in Using @capgo/capacitor-pdf-generator, [@capgo/capacitor-data-storage-sqlite](/docs/plugins/data-storage-sqlite/) for the implementation detail in @capgo/capacitor-data-storage-sqlite, [Using @capgo/capacitor-data-storage-sqlite](/plugins/capacitor-data-storage-sqlite/) for the native capability in Using @capgo/capacitor-data-storage-sqlite, [@capgo/capacitor-file](/docs/plugins/file/) for the implementation detail in @capgo/capacitor-file, and [Using @capgo/capacitor-file](/plugins/capacitor-file/) for the native capability in Using @capgo/capacitor-file.
# @capgo/capacitor-pedometer
> Capacitor plugin for accessing pedometer data including steps, distance, pace, cadence, and floors.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for accessing pedometer data including steps, distance, pace, cadence, and floors.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `getMeasurement` - Get pedometer measurements for a specified time range.
* `isAvailable` - Check which pedometer features are available on this device.
* `startMeasurementUpdates` - Start receiving real-time pedometer measurement updates.
* `stopMeasurementUpdates` - Stop receiving real-time pedometer measurement updates.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------------- | ------------------------------------------------------------ |
| `getMeasurement` | Get pedometer measurements for a specified time range. |
| `isAvailable` | Check which pedometer features are available on this device. |
| `startMeasurementUpdates` | Start receiving real-time pedometer measurement updates. |
| `stopMeasurementUpdates` | Stop receiving real-time pedometer measurement updates. |
| `checkPermissions` | Check permission to access pedometer data. |
| `requestPermissions` | Request permission to access pedometer data. |
| `addListener` | Called when a new pedometer measurement is received. |
| `removeAllListeners` | Remove all listeners for this plugin. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-pedometer](https://github.com/Cap-go/capacitor-pedometer/).
## Keep going from @capgo/capacitor-pedometer
[Section titled âKeep going from @capgo/capacitor-pedometerâ](#keep-going-from-capgocapacitor-pedometer)
If you are using **@capgo/capacitor-pedometer** to plan native plugin work, connect it with [Using @capgo/capacitor-pedometer](/plugins/capacitor-pedometer/) for the native capability in Using @capgo/capacitor-pedometer, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-pedometer and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-pedometer` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-pedometer
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `getMeasurement`
[Section titled âgetMeasurementâ](#getmeasurement)
Get pedometer measurements for a specified time range.
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
await CapacitorPedometer.getMeasurement();
```
### `isAvailable`
[Section titled âisAvailableâ](#isavailable)
Check which pedometer features are available on this device.
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
await CapacitorPedometer.isAvailable();
```
### `startMeasurementUpdates`
[Section titled âstartMeasurementUpdatesâ](#startmeasurementupdates)
Start receiving real-time pedometer measurement updates.
On **Android** and **iOS**, the `measurement` event is only fired after calling `startMeasurementUpdates()`.
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
await CapacitorPedometer.startMeasurementUpdates();
```
### `stopMeasurementUpdates`
[Section titled âstopMeasurementUpdatesâ](#stopmeasurementupdates)
Stop receiving real-time pedometer measurement updates.
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
await CapacitorPedometer.stopMeasurementUpdates();
```
### `checkPermissions`
[Section titled âcheckPermissionsâ](#checkpermissions)
Check permission to access pedometer data.
On **Android**, this checks the `ACTIVITY_RECOGNITION` permission. On **iOS**, this checks the motion usage permission.
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
await CapacitorPedometer.checkPermissions();
```
### `requestPermissions`
[Section titled ârequestPermissionsâ](#requestpermissions)
Request permission to access pedometer data.
On **Android**, this requests the `ACTIVITY_RECOGNITION` permission. On **iOS**, this requests motion usage permission.
```typescript
import { CapacitorPedometer } from '@capgo/capacitor-pedometer';
await CapacitorPedometer.requestPermissions();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `GetMeasurementOptions`
[Section titled âGetMeasurementOptionsâ](#getmeasurementoptions)
```typescript
export interface GetMeasurementOptions {
/**
* The start time for the measurement query (milliseconds since epoch).
*
* Required on **iOS**.
*
* @since 0.0.1
*/
start?: number;
/**
* The end time for the measurement query (milliseconds since epoch).
*
* Required on **iOS**.
*
* @since 0.0.1
*/
end?: number;
}
```
### `Measurement`
[Section titled âMeasurementâ](#measurement)
```typescript
export interface Measurement {
/**
* The number of steps taken by the user.
*
* @since 0.0.1
*/
numberOfSteps?: number;
/**
* The estimated distance (in meters) traveled by the user.
*
* Only available on **iOS**.
*
* @since 0.0.1
*/
distance?: number;
/**
* The approximate number of floors ascended.
*
* Only available on **iOS**.
*
* @since 0.0.1
*/
floorsAscended?: number;
/**
* The approximate number of floors descended.
*
* Only available on **iOS**.
*
* @since 0.0.1
*/
floorsDescended?: number;
/**
* The current pace (in seconds per meter).
*
* Only available on **iOS**.
*
* @since 0.0.1
*/
currentPace?: number;
/**
* The current cadence (steps per second).
*
* Only available on **iOS**.
*
* @since 0.0.1
*/
currentCadence?: number;
/**
* The average active pace (in seconds per meter).
*
* Only available on **iOS**.
*
* @since 0.0.1
*/
averageActivePace?: number;
/**
* The start time of this measurement (milliseconds since epoch).
*
* @since 0.0.1
*/
startDate?: number;
/**
* The end time of this measurement (milliseconds since epoch).
*
* @since 0.0.1
*/
endDate?: number;
}
```
### `IsAvailableResult`
[Section titled âIsAvailableResultâ](#isavailableresult)
```typescript
export interface IsAvailableResult {
/**
* Whether step counting is available.
*
* @since 0.0.1
*/
stepCounting: boolean;
/**
* Whether distance measurement is available.
*
* Only `true` on **iOS** devices that support distance tracking.
*
* @since 0.0.1
*/
distance: boolean;
/**
* Whether pace measurement is available.
*
* Only `true` on **iOS** devices that support pace tracking.
*
* @since 0.0.1
*/
pace: boolean;
/**
* Whether cadence measurement is available.
*
* Only `true` on **iOS** devices that support cadence tracking.
*
* @since 0.0.1
*/
cadence: boolean;
/**
* Whether floor counting is available.
*
* Only `true` on **iOS** devices that support floor tracking.
*
* @since 0.0.1
*/
floorCounting: boolean;
}
```
### `PermissionStatus`
[Section titled âPermissionStatusâ](#permissionstatus)
```typescript
export interface PermissionStatus {
/**
* Permission state for activity recognition.
*
* On **Android**, this is the `ACTIVITY_RECOGNITION` permission.
* On **iOS**, this is the motion usage permission.
*
* @since 0.0.1
*/
activityRecognition: 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied';
}
```
### `MeasurementEvent`
[Section titled âMeasurementEventâ](#measurementevent)
```typescript
export type MeasurementEvent = Measurement;
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-pedometer](/plugins/capacitor-pedometer/) for the native capability in Using @capgo/capacitor-pedometer, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-persistent-account
> Capacitor Persistent Account Plugin.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Persistent Account Plugin.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `readAccount` - Reads the stored account data from persistent storage.
* `saveAccount` - Saves account data to persistent storage.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------ |
| `readAccount` | Reads the stored account data from persistent storage. |
| `saveAccount` | Saves account data to persistent storage. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-persistent-account](https://github.com/Cap-go/capacitor-persistent-account/).
## Keep going from @capgo/capacitor-persistent-account
[Section titled âKeep going from @capgo/capacitor-persistent-accountâ](#keep-going-from-capgocapacitor-persistent-account)
If you are using **@capgo/capacitor-persistent-account** to plan native plugin work, connect it with [Using @capgo/capacitor-persistent-account](/plugins/capacitor-persistent-account/) for the native capability in Using @capgo/capacitor-persistent-account, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-persistent-account and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-persistent-account` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-persistent-account
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorPersistentAccount } from '@capgo/capacitor-persistent-account';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `readAccount`
[Section titled âreadAccountâ](#readaccount)
Reads the stored account data from persistent storage.
Retrieves account data that was previously saved using saveAccount(). The data persists across app sessions and survives app reinstallation on supported platforms.
```typescript
import { CapacitorPersistentAccount } from '@capgo/capacitor-persistent-account';
const result = await CapacitorPersistentAccount.readAccount();
if (result.data) {
console.log('Account data:', result.data);
} else {
console.log('No account data found');
}
```
### `saveAccount`
[Section titled âsaveAccountâ](#saveaccount)
Saves account data to persistent storage.
Stores the provided account data using platform-specific secure storage mechanisms. The data will persist across app sessions and survive app reinstallation. Any existing account data will be overwritten.
```typescript
import { CapacitorPersistentAccount } from '@capgo/capacitor-persistent-account';
await CapacitorPersistentAccount.saveAccount({
data: {
userId: '12345',
username: 'john.doe',
email: 'john@example.com'
}
});
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-persistent-account](/plugins/capacitor-persistent-account/) for the native capability in Using @capgo/capacitor-persistent-account, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-persistent-uuid
> Persistent app UUID for Capacitor.
## Overview
[Section titled âOverviewâ](#overview)
The Persistent UUID plugin creates a random RFC 4122 UUID once and stores it with native persistence. Use it when your app needs a stable, app-scoped identifier that can survive app reinstall flows, Android Studio reinstall cycles, app updates, and device OS updates.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* getId - Read the stored UUID, creating one if none exists for the selected scope.
* resetId - Rotate the UUID for logout, account reset, privacy reset, or test cleanup flows.
* scope - Use a stable namespace when debug and production builds use different package identifiers but should share one identifier.
## Platform Storage
[Section titled âPlatform Storageâ](#platform-storage)
| Platform | Storage | Default scope |
| -------- | -------------------------------------------------------- | ----------------- |
| Android | AccountManager account owned by the plugin authenticator | App package name |
| iOS | Keychain generic password, device-only accessibility | Bundle identifier |
| Web | localStorage fallback | web |
This is not a hardware identifier. It does not survive factory reset, manual account removal, Keychain clearing, browser storage clearing, or an explicit resetId call.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ---------------- | ----------------------------------------------- |
| getId | Read or create the persistent UUID for a scope. |
| resetId | Replace the stored UUID for a scope. |
| getPluginVersion | Return the native plugin version marker. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from src/definitions.ts in [capacitor-persistent-uuid](https://github.com/Cap-go/capacitor-persistent-uuid/).
## Keep going from @capgo/capacitor-persistent-uuid
[Section titled âKeep going from @capgo/capacitor-persistent-uuidâ](#keep-going-from-capgocapacitor-persistent-uuid)
If you are using **@capgo/capacitor-persistent-uuid** to identify an app install across reinstall flows, connect it with [Getting Started](/docs/plugins/persistent-uuid/getting-started/) for install and usage, [Android behavior](/docs/plugins/persistent-uuid/android/) for AccountManager details, [iOS behavior](/docs/plugins/persistent-uuid/ios/) for Keychain details, [Using @capgo/capacitor-persistent-uuid](/plugins/capacitor-persistent-uuid/) for the tutorial, and [@capgo/capacitor-persistent-account](/docs/plugins/persistent-account/) when you need to persist account data instead of an identifier.
# Android Behavior
> How @capgo/capacitor-persistent-uuid persists identifiers on Android.
## Storage Model
[Section titled âStorage Modelâ](#storage-model)
On Android, the plugin stores the UUID in AccountManager under a plugin-owned authenticator account. The default account name uses the app package name as the scope.
This lets the UUID survive common reinstall paths where app-private storage would be removed, including Android Studio uninstall/reinstall cycles and installs signed with different debug or Play signing keys when the package name stays the same.
## Stable Scope Rules
[Section titled âStable Scope Rulesâ](#stable-scope-rules)
Use the default scope when the app package name is stable across builds.
```typescript
const result = await PersistentUuid.getId();
```
Use a custom scope when debug, staging, and production builds use different package identifiers but should share one persistent UUID.
```typescript
const result = await PersistentUuid.getId({ scope: 'com.example.app' });
```
## Limits
[Section titled âLimitsâ](#limits)
The UUID can be lost if the user removes the account from Android settings, the device is factory reset, the package/scope changes, or the app calls resetId.
## Keep going from Android Behavior
[Section titled âKeep going from Android Behaviorâ](#keep-going-from-android-behavior)
If you are validating Android reinstall behavior, connect this page with [Getting Started](/docs/plugins/persistent-uuid/getting-started/) for API usage, [iOS behavior](/docs/plugins/persistent-uuid/ios/) for Apple platform differences, and [Using @capgo/capacitor-persistent-uuid](/plugins/capacitor-persistent-uuid/) for a complete walkthrough.
# Getting Started
> Install @capgo/capacitor-persistent-uuid and create a stable app UUID.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-persistent-uuid` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-persistent-uuid
npx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { PersistentUuid } from '@capgo/capacitor-persistent-uuid';
```
## Read Or Create The UUID
[Section titled âRead Or Create The UUIDâ](#read-or-create-the-uuid)
```typescript
import { PersistentUuid } from '@capgo/capacitor-persistent-uuid';
const result = await PersistentUuid.getId();
console.log(result.id);
console.log(result.scope);
console.log(result.created);
```
The first call creates and stores a UUID. Later calls return the same UUID for the same scope.
## Use A Stable Custom Scope
[Section titled âUse A Stable Custom Scopeâ](#use-a-stable-custom-scope)
The native default scope is the package name on Android and the bundle identifier on iOS. If debug and production builds use different package identifiers but should share one UUID, pass a shared scope.
```typescript
const result = await PersistentUuid.getId({
scope: 'com.example.app',
});
```
## Reset The UUID
[Section titled âReset The UUIDâ](#reset-the-uuid)
Call resetId when the user logs out, requests a privacy reset, or when automated tests need a new identifier.
```typescript
const replacement = await PersistentUuid.resetId();
console.log(replacement.id);
```
## Persistence Expectations
[Section titled âPersistence Expectationsâ](#persistence-expectations)
* Android can survive uninstall and reinstall, including Android Studio and Play installs with different signing keys, when the package name or custom scope is stable.
* iOS survives app updates and iOS updates while Keychain access rules remain the same.
* Web uses localStorage and is only a development fallback.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to add persistent app identity, connect it with [@capgo/capacitor-persistent-uuid](/docs/plugins/persistent-uuid/) for the overview, [Android behavior](/docs/plugins/persistent-uuid/android/) for reinstall behavior, [iOS behavior](/docs/plugins/persistent-uuid/ios/) for Keychain behavior, [Using @capgo/capacitor-persistent-uuid](/plugins/capacitor-persistent-uuid/) for the tutorial, and [Capgo Plugin Directory](/plugins/) for other native plugins.
# iOS Behavior
> How @capgo/capacitor-persistent-uuid persists identifiers on iOS.
## Storage Model
[Section titled âStorage Modelâ](#storage-model)
On iOS, the plugin stores the UUID in Keychain as a generic password item. The item is device-only and uses the bundle identifier as the default scope.
This survives app updates and iOS updates. It also survives reinstall flows as long as iOS keeps the Keychain item and the app keeps compatible Keychain access through the same bundle and Apple team rules.
## Stable Scope Rules
[Section titled âStable Scope Rulesâ](#stable-scope-rules)
Use the default scope when the bundle identifier is stable.
```typescript
const result = await PersistentUuid.getId();
```
Use a custom scope when multiple build variants should resolve to one app identifier.
```typescript
const result = await PersistentUuid.getId({ scope: 'com.example.app' });
```
## Limits
[Section titled âLimitsâ](#limits)
The UUID can be lost if the user erases the device, Keychain data is cleared, Keychain access changes, the bundle/team access changes, or the app calls resetId.
## Keep going from iOS Behavior
[Section titled âKeep going from iOS Behaviorâ](#keep-going-from-ios-behavior)
If you are validating iOS persistence, connect this page with [Getting Started](/docs/plugins/persistent-uuid/getting-started/) for API usage, [Android behavior](/docs/plugins/persistent-uuid/android/) for Android reinstall behavior, and [Using @capgo/capacitor-persistent-uuid](/plugins/capacitor-persistent-uuid/) for a complete walkthrough.
# @capgo/capacitor-intune
> Capacitor plugin for Microsoft Intune MAM enrollment, app protection policies, app config, and MSAL authentication.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for Microsoft Intune MAM enrollment, app protection policies, app config, and MSAL authentication.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `acquireToken` - Present the Microsoft sign-in flow and return an access token plus the account metadata.
* `acquireTokenSilent` - Acquire a token from the MSAL cache for a previously signed-in user.
* `registerAndEnrollAccount` - Register a previously authenticated account with Intune and start enrollment.
* `loginAndEnrollAccount` - Ask Intune to authenticate and enroll a user without first requesting an app token.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------- |
| `acquireToken` | Present the Microsoft sign-in flow and return an access token plus the account metadata. |
| `acquireTokenSilent` | Acquire a token from the MSAL cache for a previously signed-in user. |
| `registerAndEnrollAccount` | Register a previously authenticated account with Intune and start enrollment. |
| `loginAndEnrollAccount` | Ask Intune to authenticate and enroll a user without first requesting an app token. |
| `enrolledAccount` | Return the currently enrolled Intune account, if one is available. |
| `deRegisterAndUnenrollAccount` | Deregister the account from Intune and trigger selective wipe when applicable. |
| `logoutOfAccount` | Sign the user out of MSAL without unenrolling the Intune account. |
| `appConfig` | Fetch the remote Intune app configuration for a managed account. |
| `getPolicy` | Fetch the currently effective Intune app protection policy for a managed account. |
| `groupName` | Convenience helper that resolves the `GroupName` app configuration value when present. |
| `sdkVersion` | Return the native Intune and MSAL SDK versions bundled by this plugin. |
| `displayDiagnosticConsole` | Show the native Intune diagnostics UI. |
| `addListener` | Listen for remote app configuration refreshes. |
| `addListener` | Listen for remote app protection policy refreshes. |
| `removeAllListeners` | Remove all registered listeners for this plugin instance. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-persona](https://github.com/Cap-go/capacitor-persona/).
## Keep going from @capgo/capacitor-intune
[Section titled âKeep going from @capgo/capacitor-intuneâ](#keep-going-from-capgocapacitor-intune)
If you are using **@capgo/capacitor-intune** to plan authentication and account flows, connect it with [Using @capgo/capacitor-intune](/plugins/capacitor-persona/) for the native capability in Using @capgo/capacitor-intune, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Getting Started
> Install @capgo/capacitor-intune and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-intune` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-intune
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `acquireToken`
[Section titled âacquireTokenâ](#acquiretoken)
Present the Microsoft sign-in flow and return an access token plus the account metadata.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.acquireToken({} as AcquireTokenOptions);
```
### `acquireTokenSilent`
[Section titled âacquireTokenSilentâ](#acquiretokensilent)
Acquire a token from the MSAL cache for a previously signed-in user.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.acquireTokenSilent({} as AcquireTokenSilentOptions);
```
### `registerAndEnrollAccount`
[Section titled âregisterAndEnrollAccountâ](#registerandenrollaccount)
Register a previously authenticated account with Intune and start enrollment.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.registerAndEnrollAccount({} as RegisterAndEnrollAccountOptions);
```
### `loginAndEnrollAccount`
[Section titled âloginAndEnrollAccountâ](#loginandenrollaccount)
Ask Intune to authenticate and enroll a user without first requesting an app token.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.loginAndEnrollAccount();
```
### `enrolledAccount`
[Section titled âenrolledAccountâ](#enrolledaccount)
Return the currently enrolled Intune account, if one is available.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.enrolledAccount();
```
### `deRegisterAndUnenrollAccount`
[Section titled âdeRegisterAndUnenrollAccountâ](#deregisterandunenrollaccount)
Deregister the account from Intune and trigger selective wipe when applicable.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.deRegisterAndUnenrollAccount({} as IntuneMAMUser);
```
### `logoutOfAccount`
[Section titled âlogoutOfAccountâ](#logoutofaccount)
Sign the user out of MSAL without unenrolling the Intune account.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.logoutOfAccount({} as IntuneMAMUser);
```
### `appConfig`
[Section titled âappConfigâ](#appconfig)
Fetch the remote Intune app configuration for a managed account.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.appConfig({} as IntuneMAMUser);
```
### `getPolicy`
[Section titled âgetPolicyâ](#getpolicy)
Fetch the currently effective Intune app protection policy for a managed account.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.getPolicy({} as IntuneMAMUser);
```
### `groupName`
[Section titled âgroupNameâ](#groupname)
Convenience helper that resolves the `GroupName` app configuration value when present.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.groupName({} as IntuneMAMUser);
```
### `sdkVersion`
[Section titled âsdkVersionâ](#sdkversion)
Return the native Intune and MSAL SDK versions bundled by this plugin.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.sdkVersion();
```
### `displayDiagnosticConsole`
[Section titled âdisplayDiagnosticConsoleâ](#displaydiagnosticconsole)
Show the native Intune diagnostics UI.
```typescript
import { IntuneMAM } from '@capgo/capacitor-intune';
await IntuneMAM.displayDiagnosticConsole();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `AcquireTokenOptions`
[Section titled âAcquireTokenOptionsâ](#acquiretokenoptions)
Interactive token acquisition options.
```typescript
export interface AcquireTokenOptions {
/**
* Scopes to request, for example `https://graph.microsoft.com/.default`.
*/
scopes: string[];
/**
* When true, always show the Microsoft account picker or sign-in UI.
*
* @default false
*/
forcePrompt?: boolean;
/**
* Optional login hint for the interactive sign-in flow.
*/
loginHint?: string;
}
```
### `IntuneMAMAcquireToken`
[Section titled âIntuneMAMAcquireTokenâ](#intunemamacquiretoken)
```typescript
export interface IntuneMAMAcquireToken {
accountId: string;
accessToken: string;
accountIdentifier: string;
idToken?: string;
username?: string;
tenantId?: string;
authority?: string;
}
```
### `AcquireTokenSilentOptions`
[Section titled âAcquireTokenSilentOptionsâ](#acquiretokensilentoptions)
Silent token acquisition options.
```typescript
export interface AcquireTokenSilentOptions {
/**
* Scopes to request, for example `https://graph.microsoft.com/.default`.
*/
scopes: string[];
/**
* Microsoft Entra object ID returned by `acquireToken` or `enrolledAccount`.
*/
accountId: string;
/**
* When true, bypass the cached access token and request a fresh one.
*
* @default false
*/
forceRefresh?: boolean;
}
```
### `RegisterAndEnrollAccountOptions`
[Section titled âRegisterAndEnrollAccountOptionsâ](#registerandenrollaccountoptions)
```typescript
export interface RegisterAndEnrollAccountOptions {
/**
* Microsoft Entra object ID returned by `acquireToken`.
*/
accountId: string;
}
```
### `IntuneMAMUser`
[Section titled âIntuneMAMUserâ](#intunemamuser)
```typescript
export interface IntuneMAMUser {
accountId: string;
accountIdentifier?: string;
username?: string;
tenantId?: string;
authority?: string;
}
```
### `IntuneMAMAppConfig`
[Section titled âIntuneMAMAppConfigâ](#intunemamappconfig)
```typescript
export interface IntuneMAMAppConfig {
accountId: string;
fullData: Record[];
values: Record;
conflicts: string[];
}
```
### `IntuneMAMPolicy`
[Section titled âIntuneMAMPolicyâ](#intunemampolicy)
```typescript
export interface IntuneMAMPolicy {
accountId: string;
isPinRequired?: boolean;
isManagedBrowserRequired?: boolean;
isScreenCaptureAllowed?: boolean;
isContactSyncAllowed?: boolean;
isAppSharingAllowed?: boolean;
isFileEncryptionRequired?: boolean;
notificationPolicy?: string;
}
```
### `IntuneMAMGroupName`
[Section titled âIntuneMAMGroupNameâ](#intunemamgroupname)
```typescript
export interface IntuneMAMGroupName {
accountId: string;
groupName?: string;
}
```
### `IntuneMAMVersionInfo`
[Section titled âIntuneMAMVersionInfoâ](#intunemamversioninfo)
```typescript
export interface IntuneMAMVersionInfo {
platform: 'ios' | 'android';
intuneSdkVersion: string;
msalVersion?: string;
}
```
### `IntuneMAMChangeEvent`
[Section titled âIntuneMAMChangeEventâ](#intunemamchangeevent)
```typescript
export interface IntuneMAMChangeEvent {
accountId?: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-intune](/plugins/capacitor-persona/) for the native capability in Using @capgo/capacitor-intune, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-photo-library
> Capacitor plugin Displays photo gallery as web page, or boring native screen which you cannot modify but require no authorization.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin Displays photo gallery as web page, or boring native screen which you cannot modify but require no authorization.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `checkAuthorization` - Returns the current authorization status without prompting the user.
* `requestAuthorization` - Requests access to the photo library if needed.
* `getAlbums` - Retrieves the available albums.
* `getLibrary` - Retrieves library assets along with URLs that can be displayed in the web view.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `checkAuthorization` | Returns the current authorization status without prompting the user. |
| `requestAuthorization` | Requests access to the photo library if needed. |
| `getAlbums` | Retrieves the available albums. |
| `getLibrary` | Retrieves library assets along with URLs that can be displayed in the web view. |
| `getPhotoUrl` | Retrieves a displayable URL for the full resolution version of the asset. If you already called `getLibrary` with `includeFullResolutionData`, you normally do not need this method. |
| `getThumbnailUrl` | Retrieves a displayable URL for a resized thumbnail of the asset. |
| `pickMedia` | Opens the native system picker so the user can select media without granting full photo library access. The selected files are copied into the application cache and returned with portable URLs. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-photo-library](https://github.com/Cap-go/capacitor-photo-library/).
## Keep going from @capgo/capacitor-photo-library
[Section titled âKeep going from @capgo/capacitor-photo-libraryâ](#keep-going-from-capgocapacitor-photo-library)
If you are using **@capgo/capacitor-photo-library** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-photo-library](/plugins/capacitor-photo-library/) for the native capability in Using @capgo/capacitor-photo-library, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-photo-library and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-photo-library` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-photo-library
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `checkAuthorization`
[Section titled âcheckAuthorizationâ](#checkauthorization)
Returns the current authorization status without prompting the user.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.checkAuthorization();
```
### `requestAuthorization`
[Section titled ârequestAuthorizationâ](#requestauthorization)
Requests access to the photo library if needed.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.requestAuthorization();
```
### `getAlbums`
[Section titled âgetAlbumsâ](#getalbums)
Retrieves the available albums.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.getAlbums();
```
### `getLibrary`
[Section titled âgetLibraryâ](#getlibrary)
Retrieves library assets along with URLs that can be displayed in the web view.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.getLibrary();
```
### `getPhotoUrl`
[Section titled âgetPhotoUrlâ](#getphotourl)
Retrieves a displayable URL for the full resolution version of the asset. If you already called `getLibrary` with `includeFullResolutionData`, you normally do not need this method.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.getPhotoUrl({} as { id: string });
```
### `getThumbnailUrl`
[Section titled âgetThumbnailUrlâ](#getthumbnailurl)
Retrieves a displayable URL for a resized thumbnail of the asset.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.getThumbnailUrl({} as {
id: string;
width?: number;
height?: number;
quality?: number;
});
```
### `pickMedia`
[Section titled âpickMediaâ](#pickmedia)
Opens the native system picker so the user can select media without granting full photo library access. The selected files are copied into the application cache and returned with portable URLs.
```typescript
import { PhotoLibrary } from '@capgo/capacitor-photo-library';
await PhotoLibrary.pickMedia();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `PhotoLibraryAuthorizationState`
[Section titled âPhotoLibraryAuthorizationStateâ](#photolibraryauthorizationstate)
```typescript
export type PhotoLibraryAuthorizationState = 'authorized' | 'limited' | 'denied' | 'notDetermined';
```
### `PhotoLibraryAlbum`
[Section titled âPhotoLibraryAlbumâ](#photolibraryalbum)
```typescript
export interface PhotoLibraryAlbum {
id: string;
title: string;
assetCount: number;
}
```
### `GetLibraryOptions`
[Section titled âGetLibraryOptionsâ](#getlibraryoptions)
```typescript
export interface GetLibraryOptions {
/**
* Number of assets to skip from the beginning of the query.
*/
offset?: number;
/**
* Maximum number of assets to return. Omit to return everything that matches.
*/
limit?: number;
/**
* Include images in the result. Defaults to `true`.
*/
includeImages?: boolean;
/**
* Include videos in the result. Defaults to `false`.
*/
includeVideos?: boolean;
/**
* Include information about the albums each asset belongs to. Defaults to `false`.
*/
includeAlbumData?: boolean;
/**
* Include assets stored in the cloud (iCloud / Google Photos). Defaults to `true`.
*/
includeCloudData?: boolean;
/**
* If `true`, use the original filenames reported by the OS when available.
*/
useOriginalFileNames?: boolean;
/**
* Width of the generated thumbnails. Defaults to `512`.
*/
thumbnailWidth?: number;
/**
* Height of the generated thumbnails. Defaults to `384`.
*/
thumbnailHeight?: number;
/**
* JPEG quality for generated thumbnails (0-1). Defaults to `0.5`.
*/
thumbnailQuality?: number;
/**
* When `true`, copies the full sized asset into the app cache and returns its URL.
* Defaults to `false`.
*/
includeFullResolutionData?: boolean;
}
```
### `GetLibraryResult`
[Section titled âGetLibraryResultâ](#getlibraryresult)
```typescript
export interface GetLibraryResult {
assets: PhotoLibraryAsset[];
/**
* Total number of assets matching the query in the library. `assets.length` can be less
* than this value when pagination is used.
*/
totalCount: number;
/** Whether more assets are available when using pagination. */
hasMore: boolean;
}
```
### `PhotoLibraryFile`
[Section titled âPhotoLibraryFileâ](#photolibraryfile)
```typescript
export interface PhotoLibraryFile {
/** Absolute path on the native file system. */
path: string;
/**
* URL that can be used inside a web view. Usually produced by `Capacitor.convertFileSrc(path)`.
*/
webPath: string;
mimeType: string;
/** Size in bytes if known, otherwise `-1`. */
size: number;
}
```
### `PickMediaOptions`
[Section titled âPickMediaOptionsâ](#pickmediaoptions)
```typescript
export interface PickMediaOptions {
/**
* Maximum number of items the user can select. Use `0` to allow unlimited selection.
* Defaults to `1`.
*/
selectionLimit?: number;
/** Allow the user to select images. Defaults to `true`. */
includeImages?: boolean;
/** Allow the user to select videos. Defaults to `false`. */
includeVideos?: boolean;
/** Width of the generated thumbnails for picked items. Defaults to `256`. */
thumbnailWidth?: number;
/** Height of the generated thumbnails for picked items. Defaults to `256`. */
thumbnailHeight?: number;
/** JPEG quality for generated thumbnails (0-1). Defaults to `0.7`. */
thumbnailQuality?: number;
}
```
### `PickMediaResult`
[Section titled âPickMediaResultâ](#pickmediaresult)
```typescript
export interface PickMediaResult {
assets: PhotoLibraryAsset[];
}
```
### `PhotoLibraryAsset`
[Section titled âPhotoLibraryAssetâ](#photolibraryasset)
```typescript
export interface PhotoLibraryAsset {
id: string;
fileName: string;
type: PhotoAssetType;
width: number;
height: number;
duration?: number;
creationDate?: string;
modificationDate?: string;
latitude?: number;
longitude?: number;
mimeType: string;
/** Size in bytes reported by the OS for the underlying asset, if available. */
size?: number;
albumIds?: string[];
thumbnail?: PhotoLibraryFile;
file?: PhotoLibraryFile;
}
```
### `PhotoAssetType`
[Section titled âPhotoAssetTypeâ](#photoassettype)
```typescript
export type PhotoAssetType = 'image' | 'video';
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-photo-library](/plugins/capacitor-photo-library/) for the native capability in Using @capgo/capacitor-photo-library, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-pretty-toast
> Show native-looking toast notifications on iOS, Android, and Web with queueing, updates, actions, icons, and promise states.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-pretty-toast` provides a `toast.*` API for native-first toast notifications in Capacitor apps. It renders native overlays on iOS and Android and a DOM renderer on Web.
Use it for success messages, errors, loading states, long-running promise feedback, and action toasts that should feel integrated with the platform.
## Demo
[Section titled âDemoâ](#demo)

Toast notification flow
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `toast.show` - Shows a custom toast from a full config object.
* `toast.success`, `toast.error`, `toast.info`, `toast.warning` - Shows common semantic toasts with a title and optional config.
* `toast.loading` - Shows a persistent loading toast.
* `toast.update` - Updates an existing toast in place.
* `toast.promise` - Tracks a promise with loading, success, and error states.
* `toast.dismiss` and `toast.dismissAll` - Removes one toast or clears the queue.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ----------------------------------------- | ------------------------------------------------------------ |
| `toast.show(config, options?)` | Shows a toast using the full `ToastConfig`. |
| `toast.success(title, config?, options?)` | Shows a success toast. |
| `toast.error(title, config?, options?)` | Shows an error toast. |
| `toast.info(title, config?, options?)` | Shows an informational toast. |
| `toast.warning(title, config?, options?)` | Shows a warning toast. |
| `toast.loading(title, config?, options?)` | Shows a loading toast that does not auto-dismiss by default. |
| `toast.update(id, partial)` | Updates an existing toast. |
| `toast.promise(promise, messages)` | Shows loading state, then updates for success or error. |
| `toast.dismiss(id?)` | Dismisses one toast or the active toast. |
| `toast.dismissAll()` | Dismisses all visible and queued toasts. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-pretty-toast](https://github.com/Cap-go/capacitor-pretty-toast/).
## Keep going from @capgo/capacitor-pretty-toast
[Section titled âKeep going from @capgo/capacitor-pretty-toastâ](#keep-going-from-capgocapacitor-pretty-toast)
If you are using **@capgo/capacitor-pretty-toast** to plan native plugin work, connect it with [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives, and [Capgo Native Builds](/native-build/) for the product workflow in Capgo Native Builds.
# Getting Started
> Install @capgo/capacitor-pretty-toast and show native-first toast notifications.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-pretty-toast` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-pretty-toast
npx cap sync
```
## Import
[Section titled âImportâ](#import)
```ts
import { toast } from '@capgo/capacitor-pretty-toast';
```
## Show A Toast
[Section titled âShow A Toastâ](#show-a-toast)
```ts
toast.success('Saved', {
message: 'Your changes are ready.',
});
```
## Update A Loading Toast
[Section titled âUpdate A Loading Toastâ](#update-a-loading-toast)
```ts
const id = toast.loading('Uploading', {
message: 'Waiting for the server response.',
});
setTimeout(() => {
toast.update(id, {
title: 'Upload complete',
message: 'The file was stored successfully.',
icon: 'checkmark.circle.fill',
autoDismiss: true,
});
}, 1500);
```
## Track A Promise
[Section titled âTrack A Promiseâ](#track-a-promise)
```ts
await toast.promise(uploadFile(), {
loading: {
title: 'Uploading',
message: 'Keep the app open while the file is sent.',
},
success: 'Uploaded',
error: 'Upload failed',
});
```
## Icons And Images
[Section titled âIcons And Imagesâ](#icons-and-images)
Use `icon` for a symbol name or raw SVG markup:
```ts
toast.info('New message', {
icon: 'message.fill',
message: 'Open the inbox to reply.',
});
```
Use `iconSource` for URI-like images. It supports `https://`, `http://`, `file://`, `data:`, `blob:`, absolute file paths, or `{ uri }`.
```ts
toast.show({
title: 'Profile updated',
message: 'Your avatar changed.',
iconSource: 'https://example.com/avatar.png',
});
```
`iconSource` takes precedence over `icon`.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native plugin work, connect it with [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives, and [Capgo Native Builds](/native-build/) for the product workflow in Capgo Native Builds.
# @capgo/capacitor-printer
> Capacitor plugin for printing documents, HTML, PDFs, images and web views.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for printing documents, HTML, PDFs, images and web views.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `printBase64` - Presents the printing UI to print files encoded as base64 strings.
* `printFile` - Presents the printing UI to print device files.
* `printHtml` - Presents the printing UI to print HTML documents.
* `printPdf` - Presents the printing UI to print PDF documents.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------------------ |
| `printBase64` | Presents the printing UI to print files encoded as base64 strings. |
| `printFile` | Presents the printing UI to print device files. |
| `printHtml` | Presents the printing UI to print HTML documents. |
| `printPdf` | Presents the printing UI to print PDF documents. |
| `printWebView` | Presents the printing UI to print web view content. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-printer](https://github.com/Cap-go/capacitor-printer/).
## Keep going from @capgo/capacitor-printer
[Section titled âKeep going from @capgo/capacitor-printerâ](#keep-going-from-capgocapacitor-printer)
If you are using **@capgo/capacitor-printer** to plan native plugin work, connect it with [Using @capgo/capacitor-printer](/plugins/capacitor-printer/) for the native capability in Using @capgo/capacitor-printer, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-printer and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-printer` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-printer
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { Printer } from '@capgo/capacitor-printer';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `printBase64`
[Section titled âprintBase64â](#printbase64)
Presents the printing UI to print files encoded as base64 strings.
**Platform Behavior:**
* **iOS**: Uses UIPrintInteractionController with base64 decoded data
* **Android**: Uses PrintManager with base64 decoded data
* **Web**: Creates a blob from base64 data and opens print dialog
**Performance Warning:** Large files can lead to app crashes due to memory constraints when decoding base64. For files larger than 5MB, itâs recommended to use printFile() instead.
```typescript
import { Printer } from '@capgo/capacitor-printer';
// Print a base64 encoded PDF
await Printer.printBase64({
name: 'Invoice #12345',
data: 'base64-encoded-pdf-data',
mimeType: 'application/pdf',
});
// Print a base64 encoded image
await Printer.printBase64({
name: 'Product Photo',
data: '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDA...',
mimeType: 'image/jpeg',
});
```
### `printFile`
[Section titled âprintFileâ](#printfile)
Presents the printing UI to print device files.
**Platform Behavior:**
* **iOS**: Uses UIPrintInteractionController with file URL. Supports file:// paths or paths relative to appâs documents directory.
* **Android**: Uses PrintManager with file path. Supports both content:// URIs and file:// paths.
* **Web**: Reads file and opens print dialog
**Supported File Types:**
* PDF documents (application/pdf)
* Images: JPEG, PNG, GIF, HEIC, HEIF
```typescript
import { Printer } from '@capgo/capacitor-printer';
// iOS: Print from app documents directory
await Printer.printFile({
name: 'Contract',
path: 'file:///var/mobile/Containers/Data/Application/.../Documents/contract.pdf',
});
// Android: Print from content URI
await Printer.printFile({
name: 'Receipt',
path: 'content://com.android.providers.downloads.documents/document/123',
mimeType: 'application/pdf',
});
// Android: Print from file path
await Printer.printFile({
name: 'Photo',
path: 'file:///storage/emulated/0/Download/photo.jpg',
mimeType: 'image/jpeg',
});
```
### `printHtml`
[Section titled âprintHtmlâ](#printhtml)
Presents the printing UI to print HTML documents.
**Platform Behavior:**
* **iOS**: Renders HTML in WKWebView, then prints using UIPrintInteractionController
* **Android**: Renders HTML in WebView, then prints using PrintManager
* **Web**: Creates iframe with HTML content and triggers print dialog
**HTML Requirements:**
* Should be a complete HTML document with proper structure
* Can include inline CSS styles or style tags
* External resources (images, stylesheets) should use absolute URLs
* Print-specific CSS can be added using
```typescript
import { Printer } from '@capgo/capacitor-printer';
// Simple HTML document
await Printer.printHtml({
name: 'Sales Report',
html: 'Q4 Sales Report Revenue: $125,000
',
});
// HTML with print-specific CSS
await Printer.printHtml({
name: 'Styled Invoice',
html: `
*
*
* Invoice #12345
* Amount: $299.99
*
*
* `;
* ```
*/
html: string;
}
````
### `PrintPdfOptions`
[Section titled âPrintPdfOptionsâ](#printpdfoptions)
Options for printing PDF documents.
```typescript
export interface PrintPdfOptions extends PrintOptions {
/**
* Path to the PDF document.
*
* **iOS Path Formats:**
* - `file://` URL: Full file URL path to PDF document
* - Relative path: Path relative to app's documents directory
* - Must be within app's accessible directories (documents, temporary, cache)
* - PDF must be valid and not password-protected
*
* **Android Path Formats:**
* - `content://` URI: Content provider URI (recommended for external PDFs)
* - `file://` path: Direct file system path to PDF
* - Must have read permission for the file
* - Supports both single-page and multi-page PDFs
*
* **Web Path Formats:**
* - Relative or absolute path accessible from web context
* - Must be a valid PDF file
*
* **Validation:**
* - File must exist at the specified path
* - File must be a valid PDF (checked by magic number/header)
* - File must be readable by the app
*
* **Common Sources:**
* - App documents: PDFs saved in app's document directory
* - Downloads: PDFs from system downloads (use content:// on Android)
* - Generated PDFs: Temporary PDFs created by the app
* - Network downloads: PDFs downloaded and saved locally
*
* @since 7.0.0
* @platform ios Supports file:// paths and relative paths
* @platform android Supports content:// URIs and file:// paths
* @platform web Supports accessible file paths
* @example 'content://com.android.providers.downloads.documents/document/123'
* @example 'file:///var/mobile/Containers/Data/Application/.../Documents/document.pdf'
* @example 'file:///storage/emulated/0/Download/report.pdf'
* @example 'Documents/invoice-2024.pdf'
*/
path: string;
}
```
### `PrintOptions`
[Section titled âPrintOptionsâ](#printoptions)
Base options for all print operations.
```typescript
export interface PrintOptions {
/**
* Name of the print job.
*
* **Usage:**
* - Displayed in the system print queue
* - Shown in print history/logs
* - May appear in printer status displays
* - Used as default filename for "Save as PDF" option
*
* **Platform Behavior:**
* - **iOS**: Shown in print preview header and activity view
* - **Android**: Displayed in print job notification and print queue
* - **Web**: Used as document title in print dialog
*
* **Best Practices:**
* - Use descriptive names (e.g., "Invoice #12345", "Q4 Report")
* - Keep under 50 characters for better display
* - Avoid special characters that may cause issues in filenames
* - Include relevant identifiers (order numbers, dates, etc.)
*
* **Examples:**
* - "Invoice #12345"
* - "Sales Report - 2024 Q4"
* - "Customer Receipt - John Doe"
* - "Product Photo - SKU-ABC123"
*
* @since 7.0.0
* @default 'Document'
* @platform ios Shown in print preview and activity view
* @platform android Shown in print queue and notifications
* @platform web Used as document title in print dialog
* @example 'Invoice #12345'
* @example 'Annual Report 2024'
* @example 'Receipt - Order #789'
*/
name?: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-printer](/plugins/capacitor-printer/) for the native capability in Using @capgo/capacitor-printer, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-privacy-screen
> Capacitor API for protecting app content from the app switcher preview.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor API for protecting app content from the app switcher preview.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `enable` - Enables the privacy screen.
* `disable` - Disables the privacy screen.
* `isEnabled` - Returns the current enabled state.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------- |
| `enable` | Enables the privacy screen. |
| `disable` | Disables the privacy screen. |
| `isEnabled` | Returns the current enabled state. |
| `getPluginVersion` | Returns the native implementation version marker. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-privacy-screen](https://github.com/Cap-go/capacitor-privacy-screen/).
## Keep going from @capgo/capacitor-privacy-screen
[Section titled âKeep going from @capgo/capacitor-privacy-screenâ](#keep-going-from-capgocapacitor-privacy-screen)
If you are using **@capgo/capacitor-privacy-screen** to plan security and compliance, connect it with [Using @capgo/capacitor-privacy-screen](/plugins/capacitor-privacy-screen/) for the native capability in Using @capgo/capacitor-privacy-screen, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# Android Behavior
> Understand how Privacy Screen blocks screenshots, recordings, and recents previews on Android.
## How Android protection works
[Section titled âHow Android protection worksâ](#how-android-protection-works)
On Android, the plugin applies `WindowManager.LayoutParams.FLAG_SECURE` to the activity window.
That protects your app from:
* screenshots
* screen recordings in most normal capture paths
* the recent apps thumbnail preview
## Setup
[Section titled âSetupâ](#setup)
No extra Android manifest or Gradle configuration is required after installation and `cap sync`.
## Runtime control
[Section titled âRuntime controlâ](#runtime-control)
```typescript
import { PrivacyScreen } from '@capgo/capacitor-privacy-screen';
await PrivacyScreen.disable();
// Allow a temporary flow where capture is acceptable.
await PrivacyScreen.enable();
```
## Important note
[Section titled âImportant noteâ](#important-note)
`FLAG_SECURE` is an Android platform feature. If you disable the plugin, users and other apps can capture your content again until you re-enable it.
## Keep going from Android Behavior
[Section titled âKeep going from Android Behaviorâ](#keep-going-from-android-behavior)
If you are using **Android Behavior** to plan security and compliance, connect it with [Using @capgo/capacitor-privacy-screen](/plugins/capacitor-privacy-screen/) for the native capability in Using @capgo/capacitor-privacy-screen, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# Getting Started
> Install and control the Privacy Screen plugin for Capacitor apps on iOS and Android.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-privacy-screen` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the plugin**
```sh
bun add @capgo/capacitor-privacy-screen
```
2. **Sync native platforms**
```sh
bunx cap sync
```
3. **Review platform behavior**
* Read the [iOS notes](/docs/plugins/privacy-screen/ios/) for app switcher behavior.
* Read the [Android notes](/docs/plugins/privacy-screen/android/) for screenshot and recording behavior.
## Default behavior
[Section titled âDefault behaviorâ](#default-behavior)
The plugin enables privacy protection automatically when the native implementation loads.
* On Android, secure mode blocks screenshots, screen recording capture, and the recent apps preview.
* On iOS, the plugin hides your app during app switcher snapshot generation.
* On Web, the plugin keeps an in-memory enabled flag only for API parity.
## Basic usage
[Section titled âBasic usageâ](#basic-usage)
```typescript
import { PrivacyScreen } from '@capgo/capacitor-privacy-screen';
await PrivacyScreen.disable();
// Run a flow where screenshots or previews are temporarily allowed.
await PrivacyScreen.enable();
const { enabled } = await PrivacyScreen.isEnabled();
console.log('Privacy screen enabled:', enabled);
```
## When to disable it temporarily
[Section titled âWhen to disable it temporarilyâ](#when-to-disable-it-temporarily)
Use `disable()` only when the current screen should remain visible in system previews or be capturable by the user, for example:
* account verification steps that require screenshots for support
* payment or identity-provider flows that need a visible app switcher preview
* controlled debugging sessions on trusted devices
Restore protection immediately afterward with `enable()`.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan security and compliance, connect it with [Using @capgo/capacitor-privacy-screen](/plugins/capacitor-privacy-screen/) for the native capability in Using @capgo/capacitor-privacy-screen, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# iOS Behavior
> Understand how Privacy Screen protects your Capacitor app in the iOS app switcher.
## How iOS protection works
[Section titled âHow iOS protection worksâ](#how-ios-protection-works)
On iOS, the plugin adds a temporary native overlay while the app resigns active. That overlay is what appears in the app switcher snapshot instead of your real interface.
This means the plugin protects:
* the app switcher preview
* the snapshot iOS keeps when your app moves to the background
## What it does not do
[Section titled âWhat it does not doâ](#what-it-does-not-do)
iOS does not offer the same screenshot blocking API as Android. The plugin cannot prevent a user from taking a screenshot while actively using the app.
If you need stronger policy controls on iOS, combine this plugin with app-level choices such as:
* masking especially sensitive UI before presenting it
* minimizing sensitive data retention on screen
* clearing temporary values when the app backgrounds
## Setup
[Section titled âSetupâ](#setup)
No extra iOS configuration is required after installation and `cap sync`.
## Example flow
[Section titled âExample flowâ](#example-flow)
```typescript
import { PrivacyScreen } from '@capgo/capacitor-privacy-screen';
await PrivacyScreen.enable();
```
For most apps, even this explicit call is optional because the plugin starts enabled by default.
## Keep going from iOS Behavior
[Section titled âKeep going from iOS Behaviorâ](#keep-going-from-ios-behavior)
If you are using **iOS Behavior** to plan security and compliance, connect it with [Using @capgo/capacitor-privacy-screen](/plugins/capacitor-privacy-screen/) for the native capability in Using @capgo/capacitor-privacy-screen, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# @capgo/capacitor-proximity
> Enable native proximity monitoring so your app can react when the device is held to the ear, covered by a hand, or placed face down.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for enabling proximity monitoring in mobile apps.
Simple Control
Turn proximity monitoring on when a flow starts and disable it cleanly when it ends.
Native iOS Behavior
Uses `UIDevice.isProximityMonitoringEnabled` so iOS handles the screen behavior natively.
Android Sensor Support
Listens to `Sensor.TYPE_PROXIMITY` and dims the current app window while the sensor is covered.
Availability Checks
Verify whether the current device exposes a usable proximity sensor before enabling the feature.
Version Reporting
Read the native plugin version at runtime for debugging, support, and diagnostics.
Comprehensive Documentation
Check the [Getting Started](/docs/plugins/proximity/getting-started/) guide to install and integrate the plugin quickly.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `enable` - Enable proximity monitoring.
* `disable` - Disable proximity monitoring.
* `getStatus` - Get the current sensor availability and plugin enabled state.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------------- |
| `enable` | Enable proximity monitoring. |
| `disable` | Disable proximity monitoring. |
| `getStatus` | Get the current sensor availability and plugin enabled state. |
| `getPluginVersion` | Get the current native plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-proximity](https://github.com/Cap-go/capacitor-proximity/).
## Keep going from @capgo/capacitor-proximity
[Section titled âKeep going from @capgo/capacitor-proximityâ](#keep-going-from-capgocapacitor-proximity)
If you are using **@capgo/capacitor-proximity** to plan native plugin work, connect it with [Using @capgo/capacitor-proximity](/plugins/capacitor-proximity/) for the native capability in Using @capgo/capacitor-proximity, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-proximity and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-proximity` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-proximity
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorProximity } from '@capgo/capacitor-proximity';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `enable`
[Section titled âenableâ](#enable)
Enable proximity monitoring.
On iOS this enables `UIDevice.isProximityMonitoringEnabled`. On Android this starts listening to `TYPE_PROXIMITY` and dims the current app window while the sensor is covered.
```typescript
import { CapacitorProximity } from '@capgo/capacitor-proximity';
await CapacitorProximity.enable();
```
### `disable`
[Section titled âdisableâ](#disable)
Disable proximity monitoring.
This restores the default app window behavior and stops sensor monitoring.
```typescript
import { CapacitorProximity } from '@capgo/capacitor-proximity';
await CapacitorProximity.disable();
```
### `getStatus`
[Section titled âgetStatusâ](#getstatus)
Get the current sensor availability and plugin enabled state.
```typescript
import { CapacitorProximity } from '@capgo/capacitor-proximity';
const status = await CapacitorProximity.getStatus();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `ProximityStatusResult`
[Section titled âProximityStatusResultâ](#proximitystatusresult)
Result returned by `getStatus()`.
```typescript
export interface ProximityStatusResult {
/**
* Whether the current device exposes a usable proximity sensor.
*
* @since 0.0.1
*/
available: boolean;
/**
* Whether proximity monitoring is currently enabled by the plugin.
*
* @since 0.0.1
*/
enabled: boolean;
/**
* Platform label returned by the native or web implementation.
*
* @since 0.0.1
*/
platform: 'ios' | 'android' | 'web';
}
```
### `PluginVersionResult`
[Section titled âPluginVersionResultâ](#pluginversionresult)
Result returned when requesting the plugin version.
```typescript
export interface PluginVersionResult {
/**
* Native plugin version string.
*
* @since 0.0.1
*/
version: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-proximity](/plugins/capacitor-proximity/) for the native capability in Using @capgo/capacitor-proximity, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-realtimekit
> Capacitor RealtimeKit Plugin for Cloudflare Calls integration.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor RealtimeKit Plugin for Cloudflare Calls integration.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Initializes the RealtimeKit plugin before using other methods.
* `startMeeting` - Start a meeting using the built-in UI. Only available on Android and iOS.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------------------------- |
| `initialize` | Initializes the RealtimeKit plugin before using other methods. |
| `startMeeting` | Start a meeting using the built-in UI. Only available on Android and iOS. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-realtimekit](https://github.com/Cap-go/capacitor-realtimekit/).
## Keep going from @capgo/capacitor-realtimekit
[Section titled âKeep going from @capgo/capacitor-realtimekitâ](#keep-going-from-capgocapacitor-realtimekit)
If you are using **@capgo/capacitor-realtimekit** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-realtimekit](/plugins/capacitor-realtimekit/) for the native capability in Using @capgo/capacitor-realtimekit, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-realtimekit and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-realtimekit` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-realtimekit
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `initialize`
[Section titled âinitializeâ](#initialize)
Initializes the RealtimeKit plugin before using other methods.
```typescript
import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';
await CapacitorRealtimekit.initialize();
```
### `startMeeting`
[Section titled âstartMeetingâ](#startmeeting)
Start a meeting using the built-in UI. Only available on Android and iOS.
```typescript
import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';
await CapacitorRealtimekit.startMeeting({
authToken: 'your-auth-token',
enableAudio: true,
enableVideo: true,
});
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `StartMeetingOptions`
[Section titled âStartMeetingOptionsâ](#startmeetingoptions)
Configuration options for starting a meeting.
```typescript
export interface StartMeetingOptions {
/**
* Authentication token for the participant.
* This token is required to join the Cloudflare Calls meeting.
*
* @since 7.0.0
*/
authToken: string;
/**
* Whether to join with audio enabled.
* Default is true.
*
* @default true
* @since 7.0.0
*/
enableAudio?: boolean;
/**
* Whether to join with video enabled.
* Default is true.
*
* @default true
* @since 7.0.0
*/
enableVideo?: boolean;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-realtimekit](/plugins/capacitor-realtimekit/) for the native capability in Using @capgo/capacitor-realtimekit, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-recaptcha
> Generate reCAPTCHA and reCAPTCHA Enterprise tokens on Web, Android, and iOS before sensitive backend requests.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-recaptcha` loads Google reCAPTCHA on each Capacitor platform and returns a token for a named action.
Use it before sensitive requests such as login, signup, checkout, password reset, or abuse-prone form submissions. Send the token to your backend and create a reCAPTCHA assessment there.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `load` - Loads and caches the platform reCAPTCHA client.
* `execute` - Runs reCAPTCHA for an action and returns a token.
* `getPluginVersion` - Returns the native implementation version marker.
## Platform Support
[Section titled âPlatform Supportâ](#platform-support)
| Platform | Support |
| -------- | ---------------------------------------------------------------------------------------------------------------------- |
| Web | reCAPTCHA v3 with `api.js`, or reCAPTCHA Enterprise with `enterprise.js` |
| Android | Google mobile reCAPTCHA SDK. Native regular reCAPTCHA v3 is not a separate mode; `enterprise: false` is rejected. |
| iOS | Google `RecaptchaEnterprise` SDK. Native regular reCAPTCHA v3 is not a separate mode; `enterprise: false` is rejected. |
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ---------------------------------------------------------------------------- |
| `load` | Loads and caches the reCAPTCHA client for the current platform. |
| `execute` | Executes reCAPTCHA for an action and returns a token for backend assessment. |
| `getPluginVersion` | Returns the platform implementation version marker. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-recaptcha](https://github.com/Cap-go/capacitor-recaptcha/).
## Keep going from @capgo/capacitor-recaptcha
[Section titled âKeep going from @capgo/capacitor-recaptchaâ](#keep-going-from-capgocapacitor-recaptcha)
If you are using **@capgo/capacitor-recaptcha** to plan authentication and account flows, connect it with [Using @capgo/capacitor-recaptcha](/plugins/capacitor-recaptcha/) for the native capability in Using @capgo/capacitor-recaptcha, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Android Setup
> Android-specific setup for @capgo/capacitor-recaptcha.
## Site Key
[Section titled âSite Keyâ](#site-key)
Create an Android mobile application key in Google Cloud reCAPTCHA. Register the package name used by your Capacitor app, then set the key in `capacitor.config.ts`.
Android uses Googleâs mobile reCAPTCHA SDK. Regular, non-Enterprise reCAPTCHA v3 is only available on Web in this plugin; `enterprise: false` is rejected on Android.
```ts
import type { CapacitorConfig } from '@capacitor/cli';
import '@capgo/capacitor-recaptcha';
const config: CapacitorConfig = {
plugins: {
Recaptcha: {
androidSiteKey: 'ANDROID_SITE_KEY',
},
},
};
export default config;
```
## Dependency
[Section titled âDependencyâ](#dependency)
The plugin includes the Google Android reCAPTCHA dependency:
```txt
com.google.android.recaptcha:recaptcha:18.8.0
```
Googleâs Android reCAPTCHA SDK requires core library desugaring in the consuming app. The plugin enables it automatically during `npx cap sync android` and adds:
```txt
com.android.tools:desugar_jdk_libs:2.1.5
```
You can override the dependency version from the app Gradle config with `recaptchaVersion` when you need to pin a newer Google SDK release. You can override the desugaring dependency with `desugarJdkLibsVersion`.
## Execute
[Section titled âExecuteâ](#execute)
```ts
import { Recaptcha } from '@capgo/capacitor-recaptcha';
const { token } = await Recaptcha.execute({
action: 'checkout',
timeout: 10000,
});
```
Send the token to your backend immediately and create a reCAPTCHA assessment before accepting the protected request.
## Keep going from Android Setup
[Section titled âKeep going from Android Setupâ](#keep-going-from-android-setup)
If you are using **Android Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-recaptcha](/plugins/capacitor-recaptcha/) for the native capability in Using @capgo/capacitor-recaptcha, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Getting Started
> Install @capgo/capacitor-recaptcha and generate reCAPTCHA tokens in a Capacitor app.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-recaptcha` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-recaptcha
npx cap sync
```
## Configure Site Keys
[Section titled âConfigure Site Keysâ](#configure-site-keys)
Create platform keys in Google Cloud reCAPTCHA, then add them to `capacitor.config.ts`.
```ts
import type { CapacitorConfig } from '@capacitor/cli';
import '@capgo/capacitor-recaptcha';
const config: CapacitorConfig = {
appId: 'com.example.app',
appName: 'Example',
webDir: 'dist',
plugins: {
Recaptcha: {
androidSiteKey: 'ANDROID_SITE_KEY',
iosSiteKey: 'IOS_SITE_KEY',
webSiteKey: 'WEB_SITE_KEY',
enterprise: true,
},
},
};
export default config;
```
`androidSiteKey`, `iosSiteKey`, and `webSiteKey` override the shared `siteKey`. You can also pass a `siteKey` directly to `load()` or `execute()` when the key depends on your environment.
## Generate A Token
[Section titled âGenerate A Tokenâ](#generate-a-token)
```ts
import { Recaptcha } from '@capgo/capacitor-recaptcha';
const { token } = await Recaptcha.execute({
action: 'login',
});
await fetch('/api/recaptcha-assessment', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ token, action: 'login' }),
});
```
`execute()` calls `load()` automatically when the client is not ready, so an explicit preload step is optional.
## Web Standard reCAPTCHA v3
[Section titled âWeb Standard reCAPTCHA v3â](#web-standard-recaptcha-v3)
Set `enterprise: false` to load Googleâs standard Web reCAPTCHA v3 script.
```ts
const { token } = await Recaptcha.execute({
siteKey: 'WEB_V3_SITE_KEY',
enterprise: false,
action: 'signup',
});
```
On Android and iOS, Googleâs native mobile SDK path is Enterprise/mobile only. Passing `enterprise: false` on native platforms is rejected so a standard Web v3 key is not used accidentally.
## Migration Notes
[Section titled âMigration Notesâ](#migration-notes)
The plugin accepts the old Cordova option aliases `sitekeyAndroid` and `sitekeyWeb` in call options and Capacitor config. It also accepts `sitekeyIos` and `sitekeyIOS` as iOS migration aliases. Prefer the Capacitor config names for new code.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan authentication and account flows, connect it with [Using @capgo/capacitor-recaptcha](/plugins/capacitor-recaptcha/) for the native capability in Using @capgo/capacitor-recaptcha, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# iOS Setup
> iOS-specific setup for @capgo/capacitor-recaptcha.
## Site Key
[Section titled âSite Keyâ](#site-key)
Create an iOS mobile application key in Google Cloud reCAPTCHA. Register the bundle identifier used by your Capacitor app, then set the key in `capacitor.config.ts`.
iOS uses Googleâs `RecaptchaEnterprise` mobile SDK. Regular, non-Enterprise reCAPTCHA v3 is only available on Web in this plugin; `enterprise: false` is rejected on iOS.
```ts
import type { CapacitorConfig } from '@capacitor/cli';
import '@capgo/capacitor-recaptcha';
const config: CapacitorConfig = {
plugins: {
Recaptcha: {
iosSiteKey: 'IOS_SITE_KEY',
},
},
};
export default config;
```
## Dependency
[Section titled âDependencyâ](#dependency)
The plugin ships CocoaPods and Swift Package Manager metadata for Googleâs `RecaptchaEnterprise` iOS SDK. Running `npx cap sync ios` installs the native dependency through your selected Capacitor iOS workflow.
## Execute
[Section titled âExecuteâ](#execute)
```ts
import { Recaptcha } from '@capgo/capacitor-recaptcha';
const { token } = await Recaptcha.execute({
action: 'password_reset',
timeout: 10000,
});
```
Send the token to your backend immediately and create a reCAPTCHA assessment before accepting the protected request.
## Keep going from iOS Setup
[Section titled âKeep going from iOS Setupâ](#keep-going-from-ios-setup)
If you are using **iOS Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-recaptcha](/plugins/capacitor-recaptcha/) for the native capability in Using @capgo/capacitor-recaptcha, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# @capgo/capacitor-ricoh360
> Provides an SDK for the Ricoh360 cameras for Capacitor.
## Overview
[Section titled âOverviewâ](#overview)
Provides an SDK for the Ricoh360 cameras for Capacitor.
Package name changed.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Initializes the SDK with camera URL.
* `getCameraAsset` - Retrieves a camera asset from a URL and returns it as base64.
* `listFiles` - Lists files stored on the camera.
* `capturePicture` - Captures a picture.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------------- |
| `initialize` | Initializes the SDK with camera URL. |
| `getCameraAsset` | Retrieves a camera asset from a URL and returns it as base64. |
| `listFiles` | Lists files stored on the camera. |
| `capturePicture` | Captures a picture. |
| `captureVideo` | Captures a video. |
| `livePreview` | Starts live preview. |
| `stopLivePreview` | Stops live preview. |
| `readSettings` | Reads camera settings. |
| `setSettings` | Sets camera settings. |
| `sendCommand` | Send raw command to camera. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-ricoh360-camera-plugin](https://github.com/Cap-go/capacitor-ricoh360-camera-plugin/).
## Keep going from @capgo/capacitor-ricoh360
[Section titled âKeep going from @capgo/capacitor-ricoh360â](#keep-going-from-capgocapacitor-ricoh360)
If you are using **@capgo/capacitor-ricoh360** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player, and [Using @capgo/capacitor-native-navigation](/plugins/capacitor-native-navigation/) for the native capability in Using @capgo/capacitor-native-navigation.
# Getting Started
> Install @capgo/capacitor-ricoh360 and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-ricoh360` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-ricoh360
npx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
Each example repeats the import so the snippet can be copied alone.
### `initialize`
[Section titled âinitializeâ](#initialize)
Initializes the SDK with camera URL
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.initialize({} as InitializeOptions);
```
### `getCameraAsset`
[Section titled âgetCameraAssetâ](#getcameraasset)
Retrieves a camera asset from a URL and returns it as base64
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.getCameraAsset({} as GetCameraAssetOptions);
```
### `listFiles`
[Section titled âlistFilesâ](#listfiles)
Lists files stored on the camera
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.listFiles();
```
### `capturePicture`
[Section titled âcapturePictureâ](#capturepicture)
Captures a picture
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.capturePicture();
```
### `captureVideo`
[Section titled âcaptureVideoâ](#capturevideo)
Captures a video
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.captureVideo({} as VideoCaptureOptions);
```
### `livePreview`
[Section titled âlivePreviewâ](#livepreview)
Starts live preview
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.livePreview({} as LivePreviewOptions);
```
### `stopLivePreview`
[Section titled âstopLivePreviewâ](#stoplivepreview)
Stops live preview
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.stopLivePreview();
```
### `readSettings`
[Section titled âreadSettingsâ](#readsettings)
Reads camera settings
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.readSettings({} as { options: string[] });
```
### `setSettings`
[Section titled âsetSettingsâ](#setsettings)
Sets camera settings
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.setSettings({} as { options: Record });
```
### `sendCommand`
[Section titled âsendCommandâ](#sendcommand)
Send raw command to camera
```typescript
import { Ricoh360Camera } from '@capgo/capacitor-ricoh360';
await Ricoh360Camera.sendCommand({} as { endpoint: string; payload: Record });
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `InitializeOptions`
[Section titled âInitializeOptionsâ](#initializeoptions)
```typescript
export interface InitializeOptions {
url: string;
}
```
### `CommandResponse`
[Section titled âCommandResponseâ](#commandresponse)
```typescript
export interface CommandResponse {
session?: string;
info?: string;
preview?: string;
picture?: string;
settings?: string;
}
```
### `GetCameraAssetOptions`
[Section titled âGetCameraAssetOptionsâ](#getcameraassetoptions)
```typescript
export interface GetCameraAssetOptions {
url: string;
saveToFile?: boolean;
}
```
### `GetCameraAssetResponse`
[Section titled âGetCameraAssetResponseâ](#getcameraassetresponse)
```typescript
export interface GetCameraAssetResponse {
statusCode: number;
data: string; // base64 encoded data
filePath?: string;
}
```
### `ListFilesOptions`
[Section titled âListFilesOptionsâ](#listfilesoptions)
```typescript
export interface ListFilesOptions {
fileType?: 'all' | 'image' | 'video';
startPosition?: number;
entryCount?: number;
maxThumbSize?: number;
_detail?: boolean;
}
```
### `ListFilesResponse`
[Section titled âListFilesResponseâ](#listfilesresponse)
```typescript
export interface ListFilesResponse {
results: {
entries: {
name: string;
fileUrl: string;
size: number;
dateTimeZone: string;
width?: number;
height?: number;
previewUrl?: string;
_projectionType?: string;
isProcessed?: boolean;
_thumbSize?: number;
}[];
totalEntries: number;
};
}
```
### `VideoCaptureOptions`
[Section titled âVideoCaptureOptionsâ](#videocaptureoptions)
```typescript
export interface VideoCaptureOptions {
// Define any specific options needed for capturing a video
resolution?: '4K' | '2K';
frameRate?: number;
bitrate?: number;
}
```
### `LivePreviewOptions`
[Section titled âLivePreviewOptionsâ](#livepreviewoptions)
```typescript
export interface LivePreviewOptions {
displayInFront?: boolean;
cropPreview?: boolean;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player, and [Using @capgo/capacitor-native-navigation](/plugins/capacitor-native-navigation/) for the native capability in Using @capgo/capacitor-native-navigation.
# @capgo/capacitor-rudderstack
> Capacitor API that mirrors the public surface of `rudder-sdk-cordova`.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor API that mirrors the public surface of `rudder-sdk-cordova`.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Initializes the RudderStack client.
* `identify` - Sends an identify call for the provided user id.
* `group` - Sends a group call for the provided group id.
* `track` - Sends a track call for the provided event name.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `initialize` | Initializes the RudderStack client. |
| `identify` | Sends an identify call for the provided user id. |
| `group` | Sends a group call for the provided group id. |
| `track` | Sends a track call for the provided event name. |
| `screen` | Sends a screen call for the provided screen name. |
| `alias` | Aliases the current user to a new identifier. |
| `reset` | Resets the current RudderStack identity state. |
| `flush` | Flushes queued events immediately. |
| `putDeviceToken` | Sets the push token that RudderStack forwards to supported destinations. |
| `setAdvertisingId` | See the source definitions for current behavior. |
| `putAdvertisingId` | Sets a custom advertising id value. |
| `setAnonymousId` | See the source definitions for current behavior. |
| `putAnonymousId` | Sets a custom anonymous id value. |
| `optOut` | Toggles RudderStack tracking opt-out. |
| `getPluginVersion` | Returns the plugin version marker from the native implementation. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-rudderstack](https://github.com/Cap-go/capacitor-rudderstack/).
## Keep going from @capgo/capacitor-rudderstack
[Section titled âKeep going from @capgo/capacitor-rudderstackâ](#keep-going-from-capgocapacitor-rudderstack)
If you are using **@capgo/capacitor-rudderstack** to plan native plugin work, connect it with [Using @capgo/capacitor-rudderstack](/plugins/capacitor-rudderstack/) for the native capability in Using @capgo/capacitor-rudderstack, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-rudderstack and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-rudderstack` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-rudderstack
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `initialize`
[Section titled âinitializeâ](#initialize)
Initializes the RudderStack client.
The method keeps the Cordova signature, so the second argument may be either a config object or a Rudder options object.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.initialize('value');
```
### `identify`
[Section titled âidentifyâ](#identify)
Sends an identify call for the provided user id.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.identify('value');
```
### `group`
[Section titled âgroupâ](#group)
Sends a group call for the provided group id.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.group('value');
```
### `track`
[Section titled âtrackâ](#track)
Sends a track call for the provided event name.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.track('value');
```
### `screen`
[Section titled âscreenâ](#screen)
Sends a screen call for the provided screen name.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.screen('value');
```
### `alias`
[Section titled âaliasâ](#alias)
Aliases the current user to a new identifier.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.alias('value');
```
### `reset`
[Section titled âresetâ](#reset)
Resets the current RudderStack identity state.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.reset();
```
### `flush`
[Section titled âflushâ](#flush)
Flushes queued events immediately.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.flush();
```
### `putDeviceToken`
[Section titled âputDeviceTokenâ](#putdevicetoken)
Sets the push token that RudderStack forwards to supported destinations.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.putDeviceToken('value');
```
### `setAdvertisingId`
[Section titled âsetAdvertisingIdâ](#setadvertisingid)
See the source definitions for the current contract.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.setAdvertisingId('value');
```
### `putAdvertisingId`
[Section titled âputAdvertisingIdâ](#putadvertisingid)
Sets a custom advertising id value.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.putAdvertisingId('value');
```
### `setAnonymousId`
[Section titled âsetAnonymousIdâ](#setanonymousid)
See the source definitions for the current contract.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.setAnonymousId('value');
```
### `putAnonymousId`
[Section titled âputAnonymousIdâ](#putanonymousid)
Sets a custom anonymous id value.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.putAnonymousId('value');
```
### `optOut`
[Section titled âoptOutâ](#optout)
Toggles RudderStack tracking opt-out.
```typescript
import { nativePlugin } from '@capgo/capacitor-rudderstack';
await nativePlugin.optOut(true);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `RudderConfiguration`
[Section titled âRudderConfigurationâ](#rudderconfiguration)
Supported configuration keys for the underlying RudderStack native SDKs.
```typescript
export interface RudderConfiguration {
/**
* RudderStack data plane URL.
*/
dataPlaneUrl?: string;
/**
* Number of events to batch before a flush.
*/
flushQueueSize?: number;
/**
* Database row threshold that triggers pruning on Android and iOS.
*/
dbCountThreshold?: number;
/**
* Server config refresh interval in hours.
*/
configRefreshInterval?: number;
/**
* RudderStack log verbosity.
*/
logLevel?: RudderLogLevelValue;
/**
* Sleep timeout / sleep count used by the native SDK.
*/
sleepTimeOut?: number;
/**
* Android only. Lets the native SDK collect the advertising identifier automatically.
*/
autoCollectAdvertId?: boolean;
/**
* Tracks `Application Installed`, `Application Updated`, and `Application Opened` automatically.
*/
trackLifecycleEvents?: boolean;
/**
* RudderStack control plane URL.
*/
controlPlaneUrl?: string;
/**
* Enables automatic screen tracking where supported by the native SDK.
*/
recordScreenViews?: boolean;
/**
* Ignored in this Capacitor port.
*
* The Cordova SDK uses this field to bootstrap native destination factories from companion plugins.
* Those extension packages are not implemented in this first Capacitor release.
*/
factories?: any[];
}
```
### `RudderOptions`
[Section titled âRudderOptionsâ](#rudderoptions)
RudderStack per-call options.
```typescript
export interface RudderOptions {
/**
* External identifiers forwarded with the event.
*/
externalIds?: Record;
/**
* Destination enablement flags keyed by integration name.
*/
integrations?: Record;
}
```
### `RudderTraits`
[Section titled âRudderTraitsâ](#ruddertraits)
Traits payload accepted by `identify` and `group`.
```typescript
export type RudderTraits = Record;
```
### `RudderProperties`
[Section titled âRudderPropertiesâ](#rudderproperties)
Properties payload accepted by `track` and `screen`.
```typescript
export type RudderProperties = Record;
```
### `PluginVersionResult`
[Section titled âPluginVersionResultâ](#pluginversionresult)
Plugin version payload.
```typescript
export interface PluginVersionResult {
/**
* Version identifier returned by the platform implementation.
*/
version: string;
}
```
### `RudderLogLevelValue`
[Section titled âRudderLogLevelValueâ](#rudderloglevelvalue)
RudderStack log level values exposed for migration convenience.
```typescript
export type RudderLogLevelValue = 0 | 1 | 2 | 3 | 4 | 5;
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-rudderstack](/plugins/capacitor-rudderstack/) for the native capability in Using @capgo/capacitor-rudderstack, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-screen-orientation
> Capacitor Screen Orientation Plugin interface.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Screen Orientation Plugin interface.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `orientation` - Get the current screen orientation.
* `lock` - Lock the screen orientation to a specific type.
* `unlock` - Unlock the screen orientation.
* `startOrientationTracking` - Start tracking device orientation using motion sensors.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| -------------------------- | ------------------------------------------------------- |
| `orientation` | Get the current screen orientation. |
| `lock` | Lock the screen orientation to a specific type. |
| `unlock` | Unlock the screen orientation. |
| `startOrientationTracking` | Start tracking device orientation using motion sensors. |
| `stopOrientationTracking` | Stop tracking device orientation using motion sensors. |
| `isOrientationLocked` | Check if device orientation lock is currently enabled. |
| `addListener` | Listen for screen orientation changes. |
| `removeAllListeners` | Remove all listeners for this plugin. |
| `getPluginVersion` | Get the native plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-screen-orientation](https://github.com/Cap-go/capacitor-screen-orientation/).
## Keep going from @capgo/capacitor-screen-orientation
[Section titled âKeep going from @capgo/capacitor-screen-orientationâ](#keep-going-from-capgocapacitor-screen-orientation)
If you are using **@capgo/capacitor-screen-orientation** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-screen-orientation](/plugins/capacitor-screen-orientation/) for the native capability in Using @capgo/capacitor-screen-orientation, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-screen-orientation and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-screen-orientation` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-screen-orientation
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `orientation`
[Section titled âorientationâ](#orientation)
Get the current screen orientation.
Returns the current orientation of the device screen.
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
const result = await ScreenOrientation.orientation();
console.log('Current orientation:', result.type);
```
### `lock`
[Section titled âlockâ](#lock)
Lock the screen orientation to a specific type.
Locks the screen to the specified orientation. On iOS, if bypassOrientationLock is true, it will also start tracking physical device orientation using motion sensors.
Note: The UI will still respect the userâs orientation lock setting. Motion tracking allows you to detect how the device is physically held even when the UI doesnât rotate.
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
// Standard lock
await ScreenOrientation.lock({ orientation: 'landscape' });
// Lock with motion tracking on iOS
await ScreenOrientation.lock({
orientation: 'portrait',
bypassOrientationLock: true
});
```
### `unlock`
[Section titled âunlockâ](#unlock)
Unlock the screen orientation.
Allows the screen to rotate freely based on device position. Also stops any motion-based orientation tracking if it was enabled.
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
await ScreenOrientation.unlock();
```
### `startOrientationTracking`
[Section titled âstartOrientationTrackingâ](#startorientationtracking)
Start tracking device orientation using motion sensors.
This method is useful when you want to track the deviceâs physical orientation independently from the screen orientation lock. It uses Core Motion on iOS to detect orientation changes.
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
await ScreenOrientation.startOrientationTracking({
bypassOrientationLock: true
});
// Listen for changes
ScreenOrientation.addListener('screenOrientationChange', (result) => {
console.log('Orientation changed:', result.type);
});
```
### `stopOrientationTracking`
[Section titled âstopOrientationTrackingâ](#stoporientationtracking)
Stop tracking device orientation using motion sensors.
Stops the motion-based orientation tracking if it was started.
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
await ScreenOrientation.stopOrientationTracking();
```
### `isOrientationLocked`
[Section titled âisOrientationLockedâ](#isorientationlocked)
Check if device orientation lock is currently enabled.
This method compares the physical device orientation (from motion sensors) with the UI orientation. If they differ, orientation lock is enabled.
Note: This requires motion tracking to be active via startOrientationTracking() or lock() with bypassOrientationLock: true. Works on both iOS (Core Motion) and Android (Accelerometer).
```typescript
import { ScreenOrientation } from '@capgo/capacitor-screen-orientation';
// Start motion tracking first
await ScreenOrientation.startOrientationTracking({
bypassOrientationLock: true
});
// Check lock status
const status = await ScreenOrientation.isOrientationLocked();
if (status.locked) {
console.log('Orientation lock is ON');
console.log('Physical:', status.physicalOrientation);
console.log('UI:', status.uiOrientation);
}
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `ScreenOrientationResult`
[Section titled âScreenOrientationResultâ](#screenorientationresult)
Result returned by the orientation() method.
```typescript
export interface ScreenOrientationResult {
/**
* The current orientation type.
*
* @since 1.0.0
*/
type: OrientationType;
}
```
### `OrientationLockOptions`
[Section titled âOrientationLockOptionsâ](#orientationlockoptions)
Options for locking the screen orientation.
```typescript
export interface OrientationLockOptions {
/**
* The orientation type to lock to.
*
* @since 1.0.0
*/
orientation: OrientationLockType;
/**
* Whether to track physical device orientation using motion sensors.
* When true, uses device motion sensors to detect the true physical
* orientation of the device, even when the device orientation lock is enabled.
*
* **Important:** This does NOT bypass the UI orientation lock.
* The screen will still respect the user's orientation lock setting.
* This option only affects orientation detection/tracking - you'll receive
* orientation change events based on how the device is physically held,
* but the UI will not rotate if orientation lock is enabled.
*
* Supported on iOS (Core Motion) and Android (Accelerometer).
*
* @default false
* @since 1.0.0
*/
bypassOrientationLock?: boolean;
}
```
### `StartOrientationTrackingOptions`
[Section titled âStartOrientationTrackingOptionsâ](#startorientationtrackingoptions)
Options for starting orientation tracking using motion sensors.
```typescript
export interface StartOrientationTrackingOptions {
/**
* Whether to track physical device orientation using motion sensors.
* When true, uses device motion sensors to detect the true physical
* orientation of the device, even when the device orientation lock is enabled.
*
* **Important:** This does NOT bypass the UI orientation lock.
* This only enables detection of the physical orientation.
*
* Supported on iOS (Core Motion) and Android (Accelerometer).
*
* @default false
* @since 1.0.0
*/
bypassOrientationLock?: boolean;
}
```
### `OrientationLockStatusResult`
[Section titled âOrientationLockStatusResultâ](#orientationlockstatusresult)
Result returned by the isOrientationLocked() method.
```typescript
export interface OrientationLockStatusResult {
/**
* Whether the device orientation lock is currently enabled.
*
* This is determined by comparing the physical device orientation
* (from motion sensors) with the UI orientation. If they differ,
* orientation lock is enabled.
*
* Available on iOS (Core Motion) and Android (Accelerometer) when motion tracking is active.
*
* @since 1.0.0
*/
locked: boolean;
/**
* The physical orientation of the device from motion sensors.
* Available when motion tracking is active (iOS and Android).
*
* @since 1.0.0
*/
physicalOrientation?: OrientationType;
/**
* The current UI orientation reported by the system.
*
* @since 1.0.0
*/
uiOrientation: OrientationType;
}
```
### `OrientationType`
[Section titled âOrientationTypeâ](#orientationtype)
Orientation type that describes the orientation state of the device.
```typescript
export type OrientationType = 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary';
```
### `OrientationLockType`
[Section titled âOrientationLockTypeâ](#orientationlocktype)
Orientation lock type that can be used to lock the device orientation.
```typescript
export type OrientationLockType =
| 'any'
| 'natural'
| 'landscape'
| 'portrait'
| 'portrait-primary'
| 'portrait-secondary'
| 'landscape-primary'
| 'landscape-secondary';
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-screen-orientation](/plugins/capacitor-screen-orientation/) for the native capability in Using @capgo/capacitor-screen-orientation, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# @capgo/capacitor-screen-recorder
> Capacitor Screen Recorder Plugin for recording the device screen. Allows you to capture video recordings of the screen with optional audio.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Screen Recorder Plugin for recording the device screen. Allows you to capture video recordings of the screen with optional audio.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `start` - Start recording the device screen.
* `stop` - Stop the current screen recording.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ---------------------------------------- |
| `start` | Start recording the device screen. |
| `stop` | Stop the current screen recording. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-screen-recorder](https://github.com/Cap-go/capacitor-screen-recorder/).
## Keep going from @capgo/capacitor-screen-recorder
[Section titled âKeep going from @capgo/capacitor-screen-recorderâ](#keep-going-from-capgocapacitor-screen-recorder)
If you are using **@capgo/capacitor-screen-recorder** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-screen-recorder](/plugins/capacitor-screen-recorder/) for the native capability in Using @capgo/capacitor-screen-recorder, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-screen-recorder and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-screen-recorder` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-screen-recorder
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { ScreenRecorder } from '@capgo/capacitor-screen-recorder';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `start`
[Section titled âstartâ](#start)
Start recording the device screen.
Initiates screen recording with optional audio capture. The user will be prompted to grant screen recording permissions if not already granted. On iOS, the system recording UI will be displayed. On Android, the recording starts immediately after permission is granted.
```typescript
import { ScreenRecorder } from '@capgo/capacitor-screen-recorder';
// Start recording without audio
await ScreenRecorder.start();
// Start recording with audio
await ScreenRecorder.start({ recordAudio: true });
```
### `stop`
[Section titled âstopâ](#stop)
Stop the current screen recording.
Stops the active screen recording and saves the video to the deviceâs camera roll or gallery. On iOS, the system will show a preview of the recording. On Android, the video is saved directly to the gallery.
```typescript
import { ScreenRecorder } from '@capgo/capacitor-screen-recorder';
await ScreenRecorder.stop();
console.log('Recording saved to gallery');
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-screen-recorder](/plugins/capacitor-screen-recorder/) for the native capability in Using @capgo/capacitor-screen-recorder, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# @capgo/capacitor-shake
> Capacitor Shake Plugin interface for detecting shake gestures on mobile devices. This plugin allows you to listen for shake events and get plugin version information.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Shake Plugin interface for detecting shake gestures on mobile devices. This plugin allows you to listen for shake events and get plugin version information.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `addListener` - Listen for shake event on the device.
* `getPluginVersion` - Get the native Capacitor plugin version.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ---------------------------------------- |
| `addListener` | Listen for shake event on the device. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-shake](https://github.com/Cap-go/capacitor-shake/).
## Keep going from @capgo/capacitor-shake
[Section titled âKeep going from @capgo/capacitor-shakeâ](#keep-going-from-capgocapacitor-shake)
If you are using **@capgo/capacitor-shake** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-shake](/plugins/capacitor-shake/) for the native capability in Using @capgo/capacitor-shake, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# Getting Started
> Install @capgo/capacitor-shake and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-shake` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-shake
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorShake } from '@capgo/capacitor-shake';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `addListener`
[Section titled âaddListenerâ](#addlistener)
Listen for shake event on the device.
Registers a listener that will be called whenever a shake gesture is detected. The shake detection uses the deviceâs accelerometer to identify shake patterns.
```typescript
import { CapacitorShake } from '@capgo/capacitor-shake';
const listener = await CapacitorShake.addListener('shake', () => {
console.log('Shake detected!');
});
// To remove the listener:
await listener.remove();
```
### `getPluginVersion`
[Section titled âgetPluginVersionâ](#getpluginversion)
Get the native Capacitor plugin version.
Returns the current version of the native plugin implementation.
```typescript
import { CapacitorShake } from '@capgo/capacitor-shake';
const { version } = await CapacitorShake.getPluginVersion();
console.log('Plugin version:', version);
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-shake](/plugins/capacitor-shake/) for the native capability in Using @capgo/capacitor-shake, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-share-target
> Capacitor Share Target Plugin interface.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor Share Target Plugin interface.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `addListener` - Listen for shareReceived event.
* `removeAllListeners` - Remove all listeners for this plugin.
* `getPluginVersion` - Get the native Capacitor plugin version.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| -------------------- | ---------------------------------------- |
| `addListener` | Listen for shareReceived event. |
| `removeAllListeners` | Remove all listeners for this plugin. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-share-target](https://github.com/Cap-go/capacitor-share-target/).
## Keep going from @capgo/capacitor-share-target
[Section titled âKeep going from @capgo/capacitor-share-targetâ](#keep-going-from-capgocapacitor-share-target)
If you are using **@capgo/capacitor-share-target** to plan native plugin work, connect it with [Using @capgo/capacitor-share-target](/plugins/capacitor-share-target/) for the native capability in Using @capgo/capacitor-share-target, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-share-target and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-share-target` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-share-target
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorShareTarget } from '@capgo/capacitor-share-target';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `addListener`
[Section titled âaddListenerâ](#addlistener)
Listen for shareReceived event.
Registers a listener that will be called when content is shared to the application from another app. The callback receives event data containing title, texts, and files.
```typescript
import { CapacitorShareTarget } from '@capgo/capacitor-share-target';
const listener = await CapacitorShareTarget.addListener('shareReceived', (event) => {
console.log('Title:', event.title);
console.log('Texts:', event.texts);
event.files?.forEach(file => {
console.log(`File: ${file.name} (${file.mimeType})`);
});
});
// To remove the listener:
await listener.remove();
```
### `removeAllListeners`
[Section titled âremoveAllListenersâ](#removealllisteners)
Remove all listeners for this plugin.
```typescript
import { CapacitorShareTarget } from '@capgo/capacitor-share-target';
await CapacitorShareTarget.removeAllListeners();
```
### `getPluginVersion`
[Section titled âgetPluginVersionâ](#getpluginversion)
Get the native Capacitor plugin version.
Returns the current version of the native plugin implementation.
```typescript
import { CapacitorShareTarget } from '@capgo/capacitor-share-target';
const { version} = await CapacitorShareTarget.getPluginVersion();
console.log('Plugin version:', version);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `ShareReceivedEvent`
[Section titled âShareReceivedEventâ](#sharereceivedevent)
Event data received when content is shared to the application.
```typescript
export interface ShareReceivedEvent {
/**
* The title of the shared content.
*
* @since 0.1.0
*/
title: string;
/**
* Array of text content shared to the application.
*
* @since 0.1.0
*/
texts: string[];
/**
* Array of files shared to the application.
*
* @since 0.2.0
*/
files: SharedFile[];
}
```
### `SharedFile`
[Section titled âSharedFileâ](#sharedfile)
Represents a file shared to the application.
```typescript
export interface SharedFile {
/**
* The URI of the shared file. On Android/iOS this will be a file path or data URL.
* On web this will be a cached URL accessible via fetch.
*
* @since 0.1.0
*/
uri: string;
/**
* The name of the shared file, with or without extension.
*
* @since 0.1.0
*/
name: string;
/**
* The MIME type of the shared file.
*
* @since 0.1.0
*/
mimeType: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-share-target](/plugins/capacitor-share-target/) for the native capability in Using @capgo/capacitor-share-target, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-sheets
> Build Silk-style sheets, drawers, dialogs, toasts, lightboxes, cards, and page overlays in any Capacitor frontend.
Framework agnostic
Use standards-based custom elements directly or use setup helpers for React, Vue, Angular, Svelte, and Solid.
Capacitor ready
Respect safe areas, keyboard movement, native edge gestures, and WebView theme-color dimming from the web layer.
Every sheet shape
Compose bottom sheets, side drawers, top sheets, centered dialogs, toasts, detached sheets, cards, pages, and lightboxes.
Modern CSS
Author detents with `em`, `rem`, viewport units, `calc()`, and CSS variables while the default sizing stays `em` based.
## When To Use It
[Section titled âWhen To Use Itâ](#when-to-use-it)
`@capgo/capacitor-sheets` is for Capacitor apps that need high-quality mobile overlays without adopting a specific UI framework. It ships as custom elements, so the core package has no React, Vue, Angular, Svelte, or Solid runtime dependency.
Use it when you need:
* bottom sheets with one or more detents
* left or right sidebars and top sheets
* centered dialogs, lightboxes, cards, and toast-like overlays
* persistent sheets that keep the app behind them interactive
* full-page overlays that enter from an edge
* stack, depth, and parallax effects driven by sheet progress
* scroll helpers that expose progress and distance values
* Capacitor-safe layout for notches, home indicators, Android cutouts, and software keyboards
Note
This package is inspired by the public Silk-style overlay feature surface, but it is not a Silk wrapper and does not include Silk source code.
## Usecase Coverage
[Section titled âUsecase Coverageâ](#usecase-coverage)
| Usecase | Capgo Sheets pattern |
| ---------------------------- | ----------------------------------------------------------------------------- |
| Long Sheet | `cap-sheet` with natural content scroll or `cap-scroll` |
| Sheet with Detent | `detents="18em 32em"` plus `stepTo()` or a step trigger |
| Sidebar | `content-placement="left"` or `content-placement="right"` |
| Bottom Sheet | default `content-placement="bottom"` |
| Sheet with Keyboard | `native-focus-scroll-prevention` and visual viewport offset handling |
| Toast | `inert-outside="false"`, `focus-trap="false"`, and no outside-click dismissal |
| Detached Sheet | `cap-sheet-special-wrapper` plus custom margins and radius |
| Page from Bottom | full-height bottom sheet content |
| Top Sheet | `content-placement="top"` |
| Sheet with Stacking | `cap-sheet-stack` with depth variables |
| Sheet with Depth | `cap-sheet-outlet` and progress-driven transforms |
| Parallax Page | `cap-sheet-outlet` with `cap-scroll` progress |
| Page | full-viewport content entering from an edge |
| Lightbox | `content-placement="center"` with backdrop and media content |
| Persistent Sheet with Detent | `default-presented`, `swipe-dismissal="false"`, and `inert-outside="false"` |
| Card | compact centered sheet content |
## Core API
[Section titled âCore APIâ](#core-api)
* `` owns presentation state, detents, placement, gestures, accessibility, and events.
* `` declares present, dismiss, toggle, and detent-step actions.
* `` positions the overlay and applies safe-area and keyboard offsets.
* `` renders a progress-synced backdrop.
* `` renders the sheet surface.
* `` provides drag and keyboard detent controls.
* `` coordinates stacked sheets.
* `` exposes sheet progress to page, depth, and parallax effects.
* `` and `` expose scroll progress and distance helpers.
* `setupSheet(element, options?)` configures a sheet from framework effects or lifecycle hooks.
## Capacitor Layout Model
[Section titled âCapacitor Layout Modelâ](#capacitor-layout-model)
The default sheet viewport reads `env(safe-area-inset-*)` and Capacitor-style `--safe-area-inset-*` fallback variables, then applies the selected edges as padding around the overlay. `safe-area="auto"` protects all edges; use `safe-area="bottom left right"` or `safe-area="none"` when a usecase needs different behavior.
Keyboard handling is enabled by default. The sheet listens to `visualViewport` resize and scroll events, adds a keyboard offset to the viewport, and focuses controls with `preventScroll` so inputs stay visible above the software keyboard.
The package runs in the web layer. It does not render native UIKit or Android bottom sheets, so your app keeps full styling control while still respecting the WebView constraints that matter in Capacitor.
## Demos And Playgrounds
[Section titled âDemos And Playgroundsâ](#demos-and-playgrounds)
Animated WebP demos for every supported usecase ship in the repository README. The examples are also available as StackBlitz playgrounds:
| Long Sheet | Detents |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|  |  |
| Sidebar | Lightbox |
| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|  |  |
* [Full demo grid](/plugins/capacitor-sheets/)
* [React playground](https://stackblitz.com/github/Cap-go/capacitor-sheets?file=examples/react-app/src/main.tsx\&startScript=stackblitz-react)
* [Vue playground](https://stackblitz.com/github/Cap-go/capacitor-sheets?file=examples/vue-app/src/App.vue\&startScript=stackblitz-vue)
* [Angular playground](https://stackblitz.com/github/Cap-go/capacitor-sheets?file=examples/angular-app/src/app/app.component.ts\&startScript=stackblitz-angular)
* [Svelte playground](https://stackblitz.com/github/Cap-go/capacitor-sheets?file=examples/svelte-app/src/App.svelte\&startScript=stackblitz-svelte)
* [Solid playground](https://stackblitz.com/github/Cap-go/capacitor-sheets?file=examples/solid-app/src/main.tsx\&startScript=stackblitz-solid)
## Keep going from @capgo/capacitor-sheets
[Section titled âKeep going from @capgo/capacitor-sheetsâ](#keep-going-from-capgocapacitor-sheets)
If you are using **@capgo/capacitor-sheets** to plan native plugin work, connect it with [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives, and [Capgo Native Builds](/native-build/) for the product workflow in Capgo Native Builds.
# Getting Started
> Install @capgo/capacitor-sheets and add framework-agnostic sheets to a Capacitor app.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-sheets` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```bash
npm install @capgo/capacitor-sheets
```
2. **Register the web components**
```ts
import '@capgo/capacitor-sheets';
```
3. **Add the viewport setting for safe areas**
```html
```
4. **Render a sheet**
```html
Open route
Evening route
Choose a route and confirm pickup.
Done
```
```css
.route-sheet {
width: min(100%, 34em);
padding: 0 1.25em 1.25em;
}
```
Note
There is no native plugin registration step. Run `npx cap sync` only as part of your normal Capacitor workflow when you need to sync web assets or native project changes.
## Capacitor Safe Areas
[Section titled âCapacitor Safe Areasâ](#capacitor-safe-areas)
Safe areas are enabled by default through `safe-area="auto"`. The sheet viewport reads both browser environment values and Capacitor fallback variables:
```css
env(safe-area-inset-top)
env(safe-area-inset-bottom)
env(safe-area-inset-left)
env(safe-area-inset-right)
var(--safe-area-inset-top)
var(--safe-area-inset-bottom)
var(--safe-area-inset-left)
var(--safe-area-inset-right)
```
Choose the protected edges per sheet:
```html
```
For apps with overlay status bars or system bars, keep the native plugins responsible for exposing correct inset values:
```ts
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
plugins: {
StatusBar: {
overlaysWebView: true,
},
Keyboard: {
resize: 'body',
resizeOnFullScreen: true,
},
SystemBars: {
insetsHandling: 'css',
},
},
};
export default config;
```
Keyboard handling is controlled by `native-focus-scroll-prevention`, which defaults to `true`. Disable it only when your app already owns keyboard avoidance:
```html
```
## Imperative Control
[Section titled âImperative Controlâ](#imperative-control)
All framework helpers configure the same underlying custom element. You can also control a sheet directly:
```ts
const sheet = document.querySelector('cap-sheet');
await sheet?.present();
await sheet?.stepTo(2);
await sheet?.step('down');
await sheet?.dismiss();
```
Listen to events for controlled state, analytics, or coordinated animation:
```ts
sheet?.addEventListener('cap-sheet-presented-change', (event) => {
console.log(event.detail.presented);
});
sheet?.addEventListener('cap-sheet-active-detent-change', (event) => {
console.log(event.detail.activeDetent);
});
sheet?.addEventListener('cap-sheet-travel', (event) => {
console.log(event.detail.progress);
});
```
## React
[Section titled âReactâ](#react)
```tsx
import { useEffect, useRef } from 'react';
import { setupSheet } from '@capgo/capacitor-sheets/react';
import '@capgo/capacitor-sheets';
export function BookingSheet() {
const sheetRef = useRef(null);
useEffect(() => {
if (!sheetRef.current) return;
return setupSheet(sheetRef.current, {
detents: ['18em', '32em'],
contentPlacement: 'bottom',
onPresentedChange: ({ presented }) => console.log({ presented }),
});
}, []);
return (
Open
React sheet
);
}
```
Importing from `@capgo/capacitor-sheets/react` also registers JSX typings for the custom elements. If TypeScript still reports unknown tags, add a declaration file inside your source tree:
src/capgo-sheets.d.ts
```ts
import '@capgo/capacitor-sheets/react';
```
## Vue
[Section titled âVueâ](#vue)
```vue
Open
Vue sheet
```
## Angular
[Section titled âAngularâ](#angular)
```ts
import { AfterViewInit, Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, ViewChild } from '@angular/core';
import { setupSheet } from '@capgo/capacitor-sheets/angular';
import '@capgo/capacitor-sheets';
@Component({
selector: 'app-root',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
Open
Angular sheet
`,
})
export class AppComponent implements AfterViewInit {
@ViewChild('sheet', { static: true }) sheet?: ElementRef;
ngAfterViewInit(): void {
if (this.sheet?.nativeElement) {
setupSheet(this.sheet.nativeElement, {
detents: ['18em', '32em'],
contentPlacement: 'bottom',
});
}
}
}
```
## Svelte
[Section titled âSvelteâ](#svelte)
```svelte
Open
Svelte sheet
```
## Solid
[Section titled âSolidâ](#solid)
```tsx
import { onCleanup, onMount } from 'solid-js';
import { setupSheet } from '@capgo/capacitor-sheets/solid';
import '@capgo/capacitor-sheets';
export function BookingSheet() {
let sheetEl!: HTMLElement;
onMount(() => {
const cleanup = setupSheet(sheetEl, {
detents: ['18em', '32em'],
contentPlacement: 'bottom',
});
onCleanup(cleanup);
});
return (
Open
Solid sheet
);
}
```
## Components
[Section titled âComponentsâ](#components)
| Element | Purpose |
| ------------------------------- | ----------------------------------------------------------- |
| `cap-sheet` | Sheet state, detents, gestures, modal behavior, and events |
| `cap-sheet-trigger` | Declarative present, dismiss, toggle, and step actions |
| `cap-sheet-portal` | Optional body portal for overlay layering |
| `cap-sheet-view` | Fixed viewport host with safe-area and keyboard padding |
| `cap-sheet-backdrop` | Progress-synced backdrop |
| `cap-sheet-content` | Accessible sheet surface |
| `cap-sheet-bleeding-background` | Background extension for rounded edge sheets |
| `cap-sheet-handle` | Draggable and keyboard-accessible detent handle |
| `cap-sheet-title` | Accessible title |
| `cap-sheet-description` | Accessible description |
| `cap-sheet-special-wrapper` | Composition hook for detached sheets, cards, and lightboxes |
| `cap-sheet-stack` | Stacked sheet grouping |
| `cap-sheet-outlet` | Progress outlet for depth, parallax, and page effects |
| `cap-scroll` | Scroll progress helper |
| `cap-fixed` | Fixed layer helper |
| `cap-island` | Related floating island content |
| `cap-external-overlay` | Overlay content managed outside the sheet tree |
## Main Options
[Section titled âMain Optionsâ](#main-options)
| Option | Attribute | Default | Description |
| ----------------------------- | -------------------------------- | -------- | --------------------------------------------------- |
| `contentPlacement` | `content-placement` | `bottom` | `top`, `bottom`, `left`, `right`, or `center` |
| `detents` | `detents` | none | Space-separated CSS lengths such as `18em 32em` |
| `safeArea` | `safe-area` | `auto` | Protected safe-area edges |
| `swipe` | `swipe` | `true` | Enable pointer, touch, trackpad, and wheel gestures |
| `swipeDismissal` | `swipe-dismissal` | `true` | Allow gestures to dismiss to detent `0` |
| `inertOutside` | `inert-outside` | `true` | Prevent interaction behind modal sheets |
| `focusTrap` | `focus-trap` | `true` | Keep keyboard focus inside the sheet |
| `closeOnOutsideClick` | `close-on-outside-click` | `true` | Dismiss when clicking the backdrop or view |
| `closeOnEscape` | `close-on-escape` | `true` | Dismiss when pressing Escape |
| `nativeFocusScrollPrevention` | `native-focus-scroll-prevention` | `true` | Keep focused inputs visible above the keyboard |
| `themeColorDimming` | `theme-color-dimming` | `auto` | Dim the WebView theme color while modal |
## Available Entrypoints
[Section titled âAvailable Entrypointsâ](#available-entrypoints)
* `@capgo/capacitor-sheets`
* `@capgo/capacitor-sheets/react`
* `@capgo/capacitor-sheets/vue`
* `@capgo/capacitor-sheets/angular`
* `@capgo/capacitor-sheets/svelte`
* `@capgo/capacitor-sheets/solid`
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native plugin work, connect it with [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives, and [Capgo Native Builds](/native-build/) for the product workflow in Capgo Native Builds.
# @capgo/capacitor-sim
> Capacitor SIM Plugin for retrieving information from device SIM cards.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor SIM Plugin for retrieving information from device SIM cards.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `getSimCards` - Get information from the deviceâs SIM cards.
* `checkPermissions` - Check permission to access SIM card information.
* `requestPermissions` - Request permission to access SIM card information.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| -------------------- | -------------------------------------------------- |
| `getSimCards` | Get information from the deviceâs SIM cards. |
| `checkPermissions` | Check permission to access SIM card information. |
| `requestPermissions` | Request permission to access SIM card information. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-sim](https://github.com/Cap-go/capacitor-sim/).
## Keep going from @capgo/capacitor-sim
[Section titled âKeep going from @capgo/capacitor-simâ](#keep-going-from-capgocapacitor-sim)
If you are using **@capgo/capacitor-sim** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-sim](/plugins/capacitor-sim/) for the native capability in Using @capgo/capacitor-sim, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# Getting Started
> Install @capgo/capacitor-sim and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-sim` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-sim
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { Sim } from '@capgo/capacitor-sim';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `getSimCards`
[Section titled âgetSimCardsâ](#getsimcards)
Get information from the deviceâs SIM cards.
Retrieves details about all SIM cards installed in the device. On dual-SIM devices, returns information for both SIM cards. Requires READ\_PHONE\_STATE permission on Android.
```typescript
import { Sim } from '@capgo/capacitor-sim';
const { simCards } = await SimPlugin.getSimCards();
simCards.forEach((sim, index) => {
console.log(`SIM ${index + 1}:`);
console.log(` Carrier: ${sim.carrierName}`);
console.log(` Country: ${sim.isoCountryCode}`);
console.log(` MCC: ${sim.mobileCountryCode}`);
console.log(` MNC: ${sim.mobileNetworkCode}`);
});
```
### `checkPermissions`
[Section titled âcheckPermissionsâ](#checkpermissions)
Check permission to access SIM card information.
Checks if the app has permission to read SIM card data. On Android, checks READ\_PHONE\_STATE permission. On iOS, the status is always granted. On Web, the status is always denied.
```typescript
import { Sim } from '@capgo/capacitor-sim';
const status = await SimPlugin.checkPermissions();
if (status.readSimCard === 'granted') {
console.log('Permission granted');
} else {
console.log('Permission not granted');
}
```
### `requestPermissions`
[Section titled ârequestPermissionsâ](#requestpermissions)
Request permission to access SIM card information.
Prompts the user to grant permission to read SIM card data. On Android, requests READ\_PHONE\_STATE permission. On iOS, the status is always granted without prompting. On Web, the status remains denied.
```typescript
import { Sim } from '@capgo/capacitor-sim';
const status = await SimPlugin.requestPermissions();
if (status.readSimCard === 'granted') {
// Now you can call getSimCards()
const simCards = await SimPlugin.getSimCards();
}
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `GetSimCardsResult`
[Section titled âGetSimCardsResultâ](#getsimcardsresult)
Result returned by .
```typescript
export interface GetSimCardsResult {
simCards: SimCard[];
}
```
### `PermissionStatus`
[Section titled âPermissionStatusâ](#permissionstatus)
Result of a permission check or request.
```typescript
export interface PermissionStatus {
readSimCard: PermissionState;
}
```
### `SimCard`
[Section titled âSimCardâ](#simcard)
A SIM card description.
```typescript
export interface SimCard {
/**
* Android only: Phone number for this SIM slot, when available.
*
* @since 1.0.0
*/
number?: string;
/**
* Android only: Unique subscription identifier.
*
* @since 1.1.0
*/
subscriptionId?: string;
/**
* Android only: Physical SIM slot index for this subscription.
*
* @since 1.1.0
*/
simSlotIndex?: number;
/**
* iOS only: Indicates whether the carrier supports VoIP.
*
* @since 1.0.0
*/
allowsVOIP?: boolean;
/**
* Display name of the cellular service provider.
*
* On iOS 16.4+ the system may return placeholder values such as `--`.
* See https://github.com/jonz94/capacitor-sim/issues/8 for details.
*
* @since 1.0.0
*/
carrierName: string;
/**
* ISO 3166-1 alpha-2 country code of the service provider.
*
* On iOS 16.4+ the system may return an empty string or `--`.
* See https://github.com/jonz94/capacitor-sim/issues/8 for details.
*
* @since 1.0.0
*/
isoCountryCode: string;
/**
* Mobile Country Code (MCC) of the service provider.
*
* On iOS 16.4+ the system may return placeholder values such as `65535`.
* See https://github.com/jonz94/capacitor-sim/issues/8 for details.
*
* @since 1.0.0
*/
mobileCountryCode: string;
/**
* Mobile Network Code (MNC) of the service provider.
*
* On iOS 16.4+ the system may return placeholder values such as `65535`.
* See https://github.com/jonz94/capacitor-sim/issues/8 for details.
*
* @since 1.0.0
*/
mobileNetworkCode: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-sim](/plugins/capacitor-sim/) for the native capability in Using @capgo/capacitor-sim, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-social-login
> All social logins in one plugin.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-social-login` is the all-in-one social authentication plugin for Web, iOS, and Android.
It implements:
* Google Sign-In with Android Credential Manager
* Sign in with Apple, including OAuth on Android
* Facebook Login with the current Facebook SDK
* Twitter/X through OAuth 2.0
* Generic OAuth2 and OIDC providers, including GitHub, Microsoft Entra ID, Auth0, Okta, Keycloak, and other compliant servers
It is also the Capgo migration path for apps moving from Ionic Appflow Social Login or Ionic Auth Connect.
## Fork and migration notes
[Section titled âFork and migration notesâ](#fork-and-migration-notes)
This plugin started as a fork of `@codetrix-studio/capacitor-google-auth`. The original package is effectively archived, so Capgo maintains this plugin as the supported replacement.
If you are still using `@codetrix-studio/capacitor-google-auth`, follow the [legacy Google Auth migration guide](https://github.com/Cap-go/capacitor-social-login/blob/main/MIGRATION_CODETRIX.md).
If you are migrating from Ionic Auth Connect, use the built-in `SocialLoginAuthConnect` wrapper for the `auth0`, `azure`, `cognito`, `okta`, and `onelogin` provider names:
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [Auth Connect compatibility guide](https://github.com/Cap-go/capacitor-social-login/blob/main/docs/auth_connect_compatibility.md)
* [Keycloak setup](/docs/plugins/social-login/integrations/keycloak/)
## Compatibility
[Section titled âCompatibilityâ](#compatibility)
| Plugin version | Capacitor compatibility | Maintained |
| -------------- | ----------------------- | ---------- |
| v8.x | v8.x | Yes |
| v7.x | v7.x | On demand |
| v6.x | v6.x | No |
| v5.x | v5.x | No |
The major version of this plugin follows the major version of Capacitor. For example, use plugin v8 with Capacitor 8. Only the latest major version is actively maintained.
## Video Walkthrough
[Section titled âVideo Walkthroughâ](#video-walkthrough)
Watch a quick demo of the plugin setup and login flow in action.
[Capgo Social Login plugin overview](https://www.youtube-nocookie.com/embed/iTINYxvSJrE)
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Initialize the plugin.
* `login` - Login with the selected provider.
* `logout` - Logout.
* `isLoggedIn` - IsLoggedIn.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `initialize` | Initialize the plugin. |
| `login` | Login with the selected provider. |
| `logout` | Logout. |
| `isLoggedIn` | IsLoggedIn. |
| `getAuthorizationCode` | Get the current authorization code. |
| `refresh` | Refresh the access token. |
| `refreshToken` | OAuth2 refresh-token helper for the built-in OAuth2 provider. |
| `handleRedirectCallback` | Web-only: handle the OAuth redirect callback and return the parsed result. |
| `decodeIdToken` | Decode a JWT (typically an OIDC ID token) into its claims. |
| `getAccessTokenExpirationDate` | Convert an access token expiration timestamp (milliseconds since epoch) to an ISO date string. |
| `isAccessTokenAvailable` | Check if an access token is available (non-empty). |
| `isAccessTokenExpired` | Check if an access token is expired. |
| `isRefreshTokenAvailable` | Check if a refresh token is available (non-empty). |
| `providerSpecificCall` | Execute provider-specific calls. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
| `openSecureWindow` | Opens a secured window for OAuth2 authentication. For web, you should have the code in the redirected page to use a broadcast channel to send the redirected url to the app Something like: `html `For mobile, you should have a redirect uri that opens the app, something like: `myapp://oauth_callback/` And make sure to register it in the appâs info.plist: `xml CFBundleURLTypes CFBundleURLSchemes myapp `And in the AndroidManifest.xml file: `xml `. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-social-login](https://github.com/Cap-go/capacitor-social-login/).
## Keep going from @capgo/capacitor-social-login
[Section titled âKeep going from @capgo/capacitor-social-loginâ](#keep-going-from-capgocapacitor-social-login)
If you are using **@capgo/capacitor-social-login** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication, and [SSO (Enterprise)](/docs/webapp/enterprise-sso/) for the implementation detail in SSO (Enterprise).
# Apple login on Android
> This guide provides a comprehensive walkthrough on setting up Apple Login using Capacitor for iOS devices, detailing each step to ensure a smooth integration process.
Apple login on android is hacky. Apple has no official support for `Sign in with Apple` on Android, so the solution is slightly hacky.
Android currently uses a chrome tabs to display an OAuth2 website. This approach has the challenges:
* Difficult configuration
* A backend is required
## Understanding the flow on android.
[Section titled âUnderstanding the flow on android.â](#understanding-the-flow-on-android)
Let me use a diagram to explain the flow on android:
```
flowchart TD
A("await SocialLogin.login()") -->|Handled in the plugin|B(Generate the login URL)
B --> |Pass the link| C(Open the Chrome browser)
C --> D(Wait for the user to login)
D --> |Apple redirects to your backend|E(Handle the data returned from Apple)
E --> F(Redirect back to the app)
F --> G(Return to JS)
```
Now that you are aware of the challlanges and the flow, letâs begin the configuration.
## Creating the service ID
[Section titled âCreating the service IDâ](#creating-the-service-id)
1. Login into the [Apple Developer Portal](https://developer.apple.com).
2. Click on `Identifiers`.

You should see a screen that looks like this:

1. Ensure that this field says `App IDs`
2. Make sure that you can find your App ID.
Note
If you donât have configured Apple Login for IOS, you will have to create one. For me, I already have one created. The app ID I will use is `me.wcaleniewolny.test.ionic.vue`. If you donât have one, please create one using the [create app step](#creating-the-app).
3. Make sure that the `Sign in with Apple` capability is enabled for your app
1. Click on your app 
2. Ensure that the `Sign in with Apple` capability is enabled 
3. If it isnât enabled, enable it.
4. Go back to all `All Identifiers`

5. Click on `App Ids` and go to `Services IDs`

6. Creare a new identifier
1. Click on the plus button

2. Select `Servcice IDs` and click `Continue`

3. Enter a description and a identifiers and click `Continuie`.

Note
This `identifiers` will become the `clientId` that you will pass in the `initialize` function AND `ANDROID_SERVICE_ID` for the backend.
**Please save it!!!**
Note
Service ID doesnât have to match the App ID, but I recommend setting the service ID to `YOUR_APP_ID.service` . As a reminder, I am using `me.wcaleniewolny.test.ionic.vue` for my app ID but I am using `ee.forgr.io.ionic.service2` as the service ID.
4. Please verify the details and click `Register`

5. Click on the the newly created service

6. Enable the `Sign in with Apple` option

7. Configure the `Sign In with Apple`

8. Ensure that the `Primary App ID` is set to the App ID configured in the previous step

9. Add the domain that you are going to host you backend on.

Note
This backend **has** to be running on HTTPS. As for the `Return URLs`, you might want to come back to this after reading the next section of this tutorial and after configuring the backend. For the purposes of this tutorial, I will use `https://xyz.wcaleniewolny.me/login/callback` for the return URL and `xyz.wcaleniewolny.me` the domain. Press next.
10. Confirm the data and click `Done`

11. Click on `Continue`

12. Click on `Save`

## Creating the key
[Section titled âCreating the keyâ](#creating-the-key)
1. Go back to all `All Identifiers`

2. Click on `Keys`

3. Click on the plus icon

4. Name your key

Note
This name isnât important, you can put anything.
5. Select `Sign in with Apple` and click `Configure`

6. Select the primary App ID, and press `Save`

Note
This must be the same App ID as the ID in the previous steps.
7. Click on `Continue`

8. Click on `Register`

9. Copy the key ID and download the key.

Caution
**IMPORTANT:** Save this ID, in the backend it will be called `KEY_ID`. Download the key. Make sure to never share this key.
10. Find the downloaded key and save it in the backend folder.

## Getting the Team ID
[Section titled âGetting the Team IDâ](#getting-the-team-id)
In order to use `Login with Apple` on Android, you need to get the `Team ID`. It will be used in the backend.
1. Go to [this website](https://developer.apple.com/account/) and scroll down
2. Find the `Team ID`

## Configuring the app redirect
[Section titled âConfiguring the app redirectâ](#configuring-the-app-redirect)
As you saw in the diagram, the backend performs a step called `Redirect back to the app`. This requires manual changes to your app.
1. Modify the `AndroidManifest.xml`
1. Open the file, I will use `AndroidStudio`

2. Find the `MainActivity` and add the following Intent filter

```xml
```
2. Modify the `MainActivity`
1. Please open the `MainActivity`

2. Add the following code:

```java
@Override
protected void onNewIntent(Intent intent) {
String action = intent.getAction();
Uri data = intent.getData();
if (Intent.ACTION_VIEW.equals(action) && data != null) {
PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin");
if (pluginHandle == null) {
Log.i("Apple Login Intent", "SocialLogin login handle is null");
return;
}
Plugin plugin = pluginHandle.getInstance();
if (!(plugin instanceof SocialLoginPlugin)) {
Log.i("Apple Login Intent", "SocialLogin plugin instance is not SocialLoginPlugin");
return;
}
((SocialLoginPlugin) plugin).handleAppleLoginIntent(intent);
return;
}
super.onNewIntent(intent);
}
```
Caution
This example assumes that you donât have any deep links configured. If you do, please adjust the code
Note
You have just added a new deep link into your app. The deep link will look something like this: `capgo-demo-app://path`. You can change the `android:scheme` and the `android:host` to modify how this deep link looks. **Important:** In the backend configuration, this deep link will become `BASE_REDIRECT_URL`
## Backend configuration
[Section titled âBackend configurationâ](#backend-configuration)
A backend is required for Android, but configuring a backend will also impact IOS. An example backend is provided [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/main/index.ts)
This example provides the following:
* A simple JSON database
* A way to request the JWT from Appleâs servers
* A simple JWT verification
Note
I use `PM2` in order to host this example. An example `ecosystem.config.js` can be found [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/main/ecosystem.config.js.example)
Given everything that I said in this tutorial, here is how the `env` section would look:
* `ANDROID_SERVICE_ID` = Service ID
* `IOS_SERVICE_ID` = App ID
```js
env: {
PRIVATE_KEY_FILE: "AuthKey_U93M8LBQK3.p8",
KEY_ID: "U93M8LBQK3",
TEAM_ID: "UVTJ336J2D",
ANDROID_SERVICE_ID: "ee.forgr.io.ionic.starter.service2",
IOS_SERVICE_ID: "me.wcaleniewolny.test.ionic.vue",
PORT: 3000,
REDIRECT_URI: "https://xyz.wcaleniewolny.me/login/callback",
BASE_REDIRECT_URL: "capgo-demo-app://path"
}
```
#### Using the plugin
[Section titled âUsing the pluginâ](#using-the-plugin)
The usage of the `login` function doesnât change, itâs the same as IOS. Please take a look at that section for more info. **HOWEVER**, the `initialize` method changes a bit.
```typescript
await SocialLogin.initialize({
apple: {
clientId: 'ee.forgr.io.ionic.starter.service2',
redirectUrl: 'https://appleloginvps.wcaleniewolny.me/login/callback'
}
})
```
Danger
Note, that adding `redirectUrl` **WILL** affect IOS !!!!!
## Creating the app
[Section titled âCreating the appâ](#creating-the-app)
Note
If you already have an App ID, you can skip this step. Donât follow this step if you have configured Apple Login for IOS.
1. If you donât already have an App ID, click on the plus button

2. Select `App IDs` and click continue

3. Click on type `App` and click `Continue`

4. Enter the description and the app ID

5. Enable `Sign with Apple` capability

6. Click `Continue`

7. Confirm the details and click `Register`

## Keep going from Apple login on Android
[Section titled âKeep going from Apple login on Androidâ](#keep-going-from-apple-login-on-android)
If you are using **Apple login on Android** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Apple Login Setup
> This guide provides detailed instructions on setting up Apple Login using Capacitor, covering all necessary steps and requirements for a successful integration.
### Introduction
[Section titled âIntroductionâ](#introduction)
In this guide, you are going to learn how to configure Apple Login with Capacitor. In order to do this, you will need the following:
* an Apple Developer Account
* A computer running macOS (IOS only)
* Xcode installed (IOS only)
* A custom backend (Android only)
## Keep going from Apple Login Setup
[Section titled âKeep going from Apple Login Setupâ](#keep-going-from-apple-login-setup)
If you are using **Apple Login Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Apple login on IOS
> This comprehensive guide will walk you through the process of setting up Apple Login using Capacitor on iOS devices, ensuring a seamless integration by covering all necessary steps and configurations.
Letâs break down what you are going to need in order to setup Apple login on IOS.
1. Configure the capabilities of your app.
In order to do this, please open Xcode, click on `App` 
2. Make sure that you select the right target.

3. Please make sure that you add the `Sign in with Apple` capability.


Caution
If you donât see the `Sign in with Apple` capability, configure the [Account & Organizational Data Sharing](https://developer.apple.com/account/resources/services/cwa/configure/)
4. Initialize the Apple Login in your app.
Note
I am using Vue as my framework, the exact implementation will vary depending on the framework of your choice
```ts
// onMounted is vue specific
onMounted(() => {
SocialLogin.initialize({
apple: {}
})
});
```
5. Create a button that will begin the login process.
Said button should call the following function:
```ts
async function loginApple() {
const res = await SocialLogin.login({
provider: 'apple',
options: {}
})
```
6. Run your app on a ***PHYSICAL*** device and test it.
If you followed the steps closely you will see the following screen after clicking your button.

Thatâs it! You are all set.
## Initialize Apple login
[Section titled âInitialize Apple loginâ](#initialize-apple-login)
Call `initialize` with the `apple` provider before login. On iOS, `clientId` is not used by the operating system directly; the plugin uses it to know which provider to initialize.
```typescript
await SocialLogin.initialize({
apple: {
clientId: 'your-client-id',
},
});
const res = await SocialLogin.login({
provider: 'apple',
options: {
scopes: ['email', 'name'],
},
});
```
## Keep going from Apple login on IOS
[Section titled âKeep going from Apple login on IOSâ](#keep-going-from-apple-login-on-ios)
If you are using **Apple login on IOS** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Apple login for web browsers
> This guide provides a detailed walkthrough on setting up Apple Login using Capacitor for web applications, utilizing the @capgo/capacitor-social-login plugin to ensure a seamless integration process.
Configuring the web login is not trivial. Itâs more difficult than setting up `Sign in with Apple` on iOS but more difficult than setting up `Sign in with Apple` on Android.
## Generating the service
[Section titled âGenerating the serviceâ](#generating-the-service)
Note
This step is redundant if you have already configured `Sign in with Apple` on Android.
Please follow the guide [here](/docs/plugins/social-login/apple/android/#creating-the-service-id/) to generate the service.
## Configuring the `Return URLs`
[Section titled âConfiguring the Return URLsâ](#configuring-the-return-urls)
1. **Go to your Service ID configuration**
In the [Apple Developer Portal](https://developer.apple.com), navigate to `Identifiers` > `Services IDs` and click on your service ID.
2. **Configure Sign in with Apple**
Click on `Configure` next to `Sign in with Apple`.

3. **Add the Return URLs**
Click on the `+` button to add a new return URL.

4. **Add the Return URLs**
Add your domain for your web application in `Domains and Subdomains`.
Caution
You **CANNOT** add `localhost` or `127.0.0.1` as a domain here.
Then, add your domain with the `https://` prefix and the path from which you will call Apple Login. For example, if your domain is `https://my-app.com` and you will call Apple Login from `/login`, you should add `https://my-app.com/login` as the return URL.
Caution
You **MUST** add both the domain with a trailing slash and without a trailing slash.

5. **Save the changes**
1. Click on the `Next` button to save the changes.
2. Click on the `Save` button to save the changes.
6. You should be ready to test the login for JavaScript. Please note that you cannot test from localhost.
## Keep going from Apple login for web browsers
[Section titled âKeep going from Apple login for web browsersâ](#keep-going-from-apple-login-for-web-browsers)
If you are using **Apple login for web browsers** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Better Auth Integration
> Use @capgo/capacitor-social-login with Better Auth for native Google, Apple, and Facebook sign-in, plus Generic OAuth providers.
## Overview
[Section titled âOverviewâ](#overview)
Better Auth works well with `@capgo/capacitor-social-login` when you want native sign-in on the device but still want Better Auth to create and manage the session on your backend.
This page focuses on the two integration patterns that fit best:
* Native token handoff for Google, Apple, and Facebook
* Better Auth Generic OAuth for providers like Auth0, Okta, Keycloak, and custom OIDC servers
## Which pattern to use
[Section titled âWhich pattern to useâ](#which-pattern-to-use)
### Use native token handoff
[Section titled âUse native token handoffâ](#use-native-token-handoff)
Use `SocialLogin.login()` first, then send the returned token to Better Auth with `authClient.signIn.social()` when you use:
* Google
* Apple
* Facebook
### Use Better Auth Generic OAuth
[Section titled âUse Better Auth Generic OAuthâ](#use-better-auth-generic-oauth)
Let Better Auth own the full OAuth redirect flow when you use:
* Auth0
* Okta
* Keycloak
* GitHub
* OneLogin
* Any custom OAuth2 or OIDC provider
That keeps the session exchange on the Better Auth side and avoids duplicating redirect logic between two systems.
## Better Auth server setup
[Section titled âBetter Auth server setupâ](#better-auth-server-setup)
Start by configuring Better Auth with the social providers you want to support:
```typescript
import { betterAuth } from 'better-auth';
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL,
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
},
apple: {
clientId: process.env.APPLE_CLIENT_ID as string,
clientSecret: process.env.APPLE_CLIENT_SECRET as string,
appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER as string,
},
facebook: {
clientId: process.env.FACEBOOK_CLIENT_ID as string,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET as string,
},
},
trustedOrigins: ['https://appleid.apple.com'],
});
```
Note
For Apple on native iOS, Better Auth documents that you should provide `appBundleIdentifier` so the Apple ID token audience matches the bundle identifier used by iOS.
## Better Auth client setup
[Section titled âBetter Auth client setupâ](#better-auth-client-setup)
```typescript
import { createAuthClient } from 'better-auth/client';
export const authClient = createAuthClient({
baseURL: 'https://auth.example.com',
});
```
If you use React, use the Better Auth React client package your app already uses. The token handoff pattern stays the same.
## Google example
[Section titled âGoogle exampleâ](#google-example)
This is the cleanest integration path for native mobile Google sign-in:
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
import { authClient } from '@/lib/auth-client';
const googleResult = await SocialLogin.login({
provider: 'google',
options: {
scopes: ['profile', 'email'],
},
});
if (googleResult.result.responseType !== 'online' || !googleResult.result.idToken) {
throw new Error('Google online mode with idToken is required for Better Auth.');
}
await authClient.signIn.social({
provider: 'google',
idToken: {
token: googleResult.result.idToken,
accessToken: googleResult.result.accessToken?.token,
},
callbackURL: '/dashboard',
});
```
## Apple example
[Section titled âApple exampleâ](#apple-example)
For Apple, pass the same nonce to both the native login request and Better Auth:
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
import { authClient } from '@/lib/auth-client';
const nonce = crypto.randomUUID();
const appleResult = await SocialLogin.login({
provider: 'apple',
options: {
scopes: ['email', 'name'],
nonce,
},
});
if (!appleResult.result.idToken) {
throw new Error('Apple idToken is required for Better Auth.');
}
await authClient.signIn.social({
provider: 'apple',
idToken: {
token: appleResult.result.idToken,
nonce,
accessToken: appleResult.result.accessToken?.token,
},
callbackURL: '/dashboard',
});
```
## Facebook example
[Section titled âFacebook exampleâ](#facebook-example)
Better Auth documents two Facebook handoff modes:
* iOS Limited Login: pass the `idToken`
* Access-token flow: pass the access token as both `token` and `accessToken`
This works with the response shape from `@capgo/capacitor-social-login`:
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
import { authClient } from '@/lib/auth-client';
const facebookResult = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
},
});
const betterAuthToken = facebookResult.result.idToken
? {
token: facebookResult.result.idToken,
}
: facebookResult.result.accessToken?.token
? {
token: facebookResult.result.accessToken.token,
accessToken: facebookResult.result.accessToken.token,
}
: null;
if (!betterAuthToken) {
throw new Error('Facebook idToken or access token is required for Better Auth.');
}
await authClient.signIn.social({
provider: 'facebook',
idToken: betterAuthToken,
callbackURL: '/dashboard',
});
```
## Generic OAuth providers with Better Auth
[Section titled âGeneric OAuth providers with Better Authâ](#generic-oauth-providers-with-better-auth)
For Auth0, Okta, Keycloak, GitHub, Microsoft Entra ID, and similar providers, Better Authâs Generic OAuth plugin is usually the better fit than passing tokens from `SocialLogin.login({ provider: 'oauth2' })`.
### Better Auth server
[Section titled âBetter Auth serverâ](#better-auth-server)
```typescript
import { betterAuth } from 'better-auth';
import { genericOAuth } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
{
providerId: 'keycloak',
discoveryUrl: 'https://sso.example.com/realms/mobile/.well-known/openid-configuration',
clientId: process.env.KEYCLOAK_CLIENT_ID as string,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET as string,
},
],
}),
],
});
```
### Better Auth client
[Section titled âBetter Auth clientâ](#better-auth-client)
```typescript
import { createAuthClient } from 'better-auth/client';
import { genericOAuthClient } from 'better-auth/client/plugins';
export const authClient = createAuthClient({
baseURL: 'https://auth.example.com',
plugins: [genericOAuthClient()],
});
await authClient.signIn.oauth2({
providerId: 'keycloak',
callbackURL: '/dashboard',
});
```
## Provider examples for Better Auth Generic OAuth
[Section titled âProvider examples for Better Auth Generic OAuthâ](#provider-examples-for-better-auth-generic-oauth)
Better Auth ships pre-configured helpers for several providers. These are the closest match to the extra provider examples you see in the social-login plugin docs.
### Auth0
[Section titled âAuth0â](#auth0)
```typescript
import { betterAuth } from 'better-auth';
import { auth0, genericOAuth } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
auth0({
providerId: 'auth0',
domain: 'dev-example.eu.auth0.com',
clientId: process.env.AUTH0_CLIENT_ID as string,
clientSecret: process.env.AUTH0_CLIENT_SECRET as string,
scopes: ['openid', 'profile', 'email', 'offline_access'],
}),
],
}),
],
});
```
```typescript
await authClient.signIn.oauth2({
providerId: 'auth0',
callbackURL: '/dashboard',
});
```
### Microsoft Entra ID
[Section titled âMicrosoft Entra IDâ](#microsoft-entra-id)
```typescript
import { betterAuth } from 'better-auth';
import { genericOAuth, microsoftEntraId } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
microsoftEntraId({
providerId: 'entra',
tenantId: 'common',
clientId: process.env.AZURE_CLIENT_ID as string,
clientSecret: process.env.AZURE_CLIENT_SECRET as string,
scopes: ['openid', 'profile', 'email', 'User.Read'],
}),
],
}),
],
});
```
```typescript
await authClient.signIn.oauth2({
providerId: 'entra',
callbackURL: '/dashboard',
});
```
### Okta
[Section titled âOktaâ](#okta)
```typescript
import { betterAuth } from 'better-auth';
import { genericOAuth, okta } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
okta({
providerId: 'okta',
issuer: 'https://dev-12345.okta.com/oauth2/default',
clientId: process.env.OKTA_CLIENT_ID as string,
clientSecret: process.env.OKTA_CLIENT_SECRET as string,
scopes: ['openid', 'profile', 'email', 'offline_access'],
}),
],
}),
],
});
```
```typescript
await authClient.signIn.oauth2({
providerId: 'okta',
callbackURL: '/dashboard',
});
```
### Keycloak
[Section titled âKeycloakâ](#keycloak)
```typescript
import { betterAuth } from 'better-auth';
import { genericOAuth, keycloak } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
keycloak({
providerId: 'keycloak',
issuer: 'https://sso.example.com/realms/mobile',
clientId: process.env.KEYCLOAK_CLIENT_ID as string,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET as string,
scopes: ['openid', 'profile', 'email', 'offline_access'],
}),
],
}),
],
});
```
```typescript
await authClient.signIn.oauth2({
providerId: 'keycloak',
callbackURL: '/dashboard',
});
```
### GitHub with manual Generic OAuth config
[Section titled âGitHub with manual Generic OAuth configâ](#github-with-manual-generic-oauth-config)
GitHub does not have a Better Auth helper on the Generic OAuth page, so use manual configuration:
```typescript
import { betterAuth } from 'better-auth';
import { genericOAuth } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
{
providerId: 'github',
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
authorizationUrl: 'https://github.com/login/oauth/authorize',
tokenUrl: 'https://github.com/login/oauth/access_token',
userInfoUrl: 'https://api.github.com/user',
scopes: ['read:user', 'user:email'],
pkce: true,
},
],
}),
],
});
```
```typescript
await authClient.signIn.oauth2({
providerId: 'github',
callbackURL: '/dashboard',
});
```
## Notes and caveats
[Section titled âNotes and caveatsâ](#notes-and-caveats)
1. **Use Google online mode** Better Auth needs the `idToken`, so `google.mode: 'offline'` is not the right fit for this handoff flow.
2. **Reuse the Apple nonce** Generate it once, send it to Apple native login, then send the same value to Better Auth.
3. **Handle Facebook differently by platform** Limited Login on iOS gives you an ID token. Other flows may only give an access token.
4. **Do not mix Generic OAuth flows unless you have a reason** If Better Auth owns the OAuth provider configuration, let Better Auth own the redirect flow too.
## Further reading
[Section titled âFurther readingâ](#further-reading)
* [Better Auth Google provider docs](https://better-auth.com/docs/authentication/google)
* [Better Auth Apple provider docs](https://better-auth.com/docs/authentication/apple)
* [Better Auth Facebook provider docs](https://better-auth.com/docs/authentication/facebook)
* [Better Auth Generic OAuth plugin docs](https://better-auth.com/docs/plugins/generic-oauth)
* [Social Login OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
## Keep going from Better Auth Integration
[Section titled âKeep going from Better Auth Integrationâ](#keep-going-from-better-auth-integration)
If you are using **Better Auth Integration** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Facebook Login Setup
> This guide provides a comprehensive walkthrough on setting up Facebook Login using Capacitor, ensuring seamless integration and enhanced user authentication for your application.
## Introduction
[Section titled âIntroductionâ](#introduction)
In this guide, you will learn how to setup Facebook Login with Capgo Social Login. You will need the following:
* A Facebook Developer Account
* Your appâs package name/bundle ID
* Access to a terminal for generating key hashes (Android)
## General Setup
[Section titled âGeneral Setupâ](#general-setup)
If you donât already have a Facebook app created, follow these steps:
1. Create a Facebook App
Follow the tutorial to [Create an App](https://developers.facebook.com/docs/development/create-an-app/)
2. Add Facebook Login to your app
In your Facebook Developer Dashboard, add the Facebook Login product to your app
3. Before you can release your app to the public, follow this [tutorial](https://developers.facebook.com/docs/development/release/) to publish it
## Important Information
[Section titled âImportant Informationâ](#important-information)
Hereâs where to find the key information youâll need for integration:
1. `CLIENT_TOKEN`:

2. `APP_ID`:

3. `APP_NAME`:

## Facebook Business Login
[Section titled âFacebook Business Loginâ](#facebook-business-login)
This plugin supports Facebook Business Login for business-related features and permissions. Business accounts can request additional permissions beyond standard consumer login, including Instagram and Pages management.
Supported business permissions include:
* `instagram_basic` - Access to Instagram Basic Display API
* `instagram_manage_insights` - Access to Instagram Insights
* `pages_show_list` - List of Pages the person manages
* `pages_read_engagement` - Read engagement data from Pages
* `pages_manage_posts` - Manage posts on Pages
* `business_management` - Manage business assets
See the [Facebook Permissions Reference](https://developers.facebook.com/docs/permissions/reference) for the full permission list.
Configuration requirements:
1. Your Facebook app must be configured as a Business app in the Facebook Developer Console.
2. Business permissions may require Facebook App Review before production use.
3. Your app must comply with Facebookâs Business Use Case policies.
### Instagram Basic Access
[Section titled âInstagram Basic Accessâ](#instagram-basic-access)
```typescript
await SocialLogin.initialize({
facebook: {
appId: 'your-business-app-id',
clientToken: 'your-client-token',
},
});
const res = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: [
'email',
'public_profile',
'instagram_basic',
'pages_show_list',
'pages_read_engagement',
],
},
});
const profile = await SocialLogin.providerSpecificCall({
call: 'facebook#getProfile',
options: {
fields: ['id', 'name', 'email', 'instagram_business_account'],
},
});
```
### Pages Management
[Section titled âPages Managementâ](#pages-management)
```typescript
const res = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: [
'email',
'pages_show_list',
'pages_manage_posts',
'pages_read_engagement',
],
},
});
const profile = await SocialLogin.providerSpecificCall({
call: 'facebook#getProfile',
options: {
fields: ['id', 'name', 'accounts{id,name,instagram_business_account}'],
},
});
```
Important notes:
* You can test business permissions with test users and development apps before App Review.
* Most business permissions require Facebook App Review before production use.
* Business APIs have different rate limits. Review Facebookâs current platform documentation before launch.
* Follow the [Facebook Business Integration Guide](https://developers.facebook.com/docs/development/create-an-app/app-dashboard/business-integrations) when configuring the app.
## Android Setup
[Section titled âAndroid Setupâ](#android-setup)
1. Add internet permission to your `AndroidManifest.xml`
Ensure this line is present:
```xml
```
2. Generate your Android key hash
This is a crucial security step required by Facebook. Open your terminal and run:
```bash
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64 -A
```
When prompted for a password, use: `android`
Note
For release builds, youâll need to use your release keystore:
```bash
keytool -exportcert -alias your-key-name -keystore your-keystore-path | openssl sha1 -binary | openssl base64 -A
```
3. Add the key hash to your Facebook app
1. Go to your appâs dashboard on Facebook Developers
2. Navigate to Settings > Basic
3. Scroll down to âAndroidâ section
4. Click âAdd Platformâ if Android isnât added yet and fill in the details
5. Add the key hash you generated
6. For production, add both debug and release key hashes
4. Update your `AndroidManifest.xml` to include:
```xml
...
```
Caution
Make sure to replace `[APP_ID]` with your actual Facebook app ID in the `android:scheme` attribute
## iOS Setup
[Section titled âiOS Setupâ](#ios-setup)
1. Add the iOS platform in Facebook Developer Console
1. Go to your appâs dashboard on Facebook Developers
2. Navigate to Settings > Basic
3. Scroll down to very bottom of the page and click âAdd Platformâ
4. Select iOS and fill in the required details
2. Open your Xcode project and navigate to Info.plist
3. Add the following entries to your Info.plist:
```xml
FacebookAppID
[APP-ID]
FacebookClientToken
[CLIENT-TOKEN]
FacebookDisplayName
[APP-NAME]
LSApplicationQueriesSchemes
fbapi
fb-messenger-share-api
CFBundleURLTypes
CFBundleURLSchemes
fb[APP-ID]
```
Caution
Replace the following values:
* `[APP-ID]` with your Facebook app ID
* `[CLIENT-TOKEN]` with your client token
* `[APP-NAME]` with your appâs name
4. Modify the `AppDelegate.swift`
```swift
import FBSDKCoreKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Initialize Facebook SDK
FBSDKCoreKit.ApplicationDelegate.shared.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
if (FBSDKCoreKit.ApplicationDelegate.shared.application(
app,
open: url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation]
)) {
return true;
} else {
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
}
}
```
## Using Facebook Login in Your App
[Section titled âUsing Facebook Login in Your Appâ](#using-facebook-login-in-your-app)
Caution
**Before You Start**: Remember that with the new Facebook SDK, the token type you receive depends entirely on the userâs App Tracking choice, not on your code configuration. Always implement both access token and JWT token handling in your backend to ensure authentication works for all users.
1. Initialize the Facebook login in your app
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
// Initialize during app startup
await SocialLogin.initialize({
facebook: {
appId: 'APP_ID',
clientToken: 'CLIENT_TOKEN',
}
})
```
2. Implement the login function
```typescript
async function loginWithFacebook() {
try {
const result = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
limitedLogin: false // See Limited Login section below for important details
}
});
console.log('Facebook login result:', result);
// Handle successful login
} catch (error) {
console.error('Facebook login error:', error);
// Handle error
}
}
```
Note
**Limited Login (iOS Only)**: Set `limitedLogin` to true if you want to use Facebookâs Limited Login feature. This is an iOS-only feature that provides enhanced privacy by restricting the data shared during login.
**Important Limitations:**
* **iOS Only**: Limited Login only affects iOS devices and has no impact on Android
* **ATT Override**: Even if you set `limitedLogin: false`, Facebook will automatically force it to `true` if the user hasnât granted App Tracking Transparency (ATT) permission
* **Always Handle Both Cases**: Your app should always be prepared to handle both limited and full login scenarios
**Checking ATT Status:**
```typescript
// Check if user has granted tracking permission
const trackingStatus = await SocialLogin.providerSpecificCall({
call: 'facebook#requestTracking',
options: {}
});
console.log('Tracking status:', trackingStatus.status); // 'authorized', 'denied', 'notDetermined', or 'restricted'
```
**Recommended Implementation if access\_token is preferred:**
```typescript
async function loginWithFacebook() {
try {
// Check ATT status first
const trackingStatus = await SocialLogin.providerSpecificCall({
call: 'facebook#requestTracking',
options: {}
});
const result = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
limitedLogin: trackingStatus.status === 'denied' // Auto-adjust based on ATT
}
});
// Handle different response types based on limited login
if (result.result.accessToken) {
// Your app logic should work with both limited and full login
console.log('Login successful:', result);
}
} catch (error) {
console.error('Facebook login error:', error);
}
}
```
**What Happens in Limited Login:**
* **Reduced Data Access**: Some user data may not be available
* **Different Token Types**: Access tokens may have different capabilities
* **Privacy Compliance**: Helps comply with iOS privacy requirements
**Important**: Always test your app with both limited and full login scenarios to ensure your app works correctly in both cases. You can learn more about Limited Login [here](https://developers.facebook.com/docs/facebook-login/limited-login/).
3. **Get User Profile Data**
After successful login, you can retrieve additional profile information:
```typescript
async function getFacebookProfile() {
try {
const profileResponse = await SocialLogin.providerSpecificCall({
call: 'facebook#getProfile',
options: {
fields: ['id', 'name', 'email', 'first_name', 'last_name', 'picture']
}
});
console.log('Facebook profile:', profileResponse.profile);
return profileResponse.profile;
} catch (error) {
console.error('Failed to get Facebook profile:', error);
return null;
}
}
// Example usage after login
async function loginAndGetProfile() {
const loginResult = await loginWithFacebook();
if (loginResult) {
const profile = await getFacebookProfile();
if (profile) {
console.log('User ID:', profile.id);
console.log('Name:', profile.name);
console.log('Email:', profile.email);
console.log('Profile Picture:', profile.picture?.data?.url);
}
}
}
```
Tip
**Available Profile Fields**: You can request any fields available in Facebookâs Graph API. Common fields include: `id`, `name`, `email`, `first_name`, `last_name`, `picture`, `birthday`, `gender`, `location`, `hometown`. Note that some fields may require additional permissions.
**Token Type Limitation**: The `getProfile` call only works when you have an **access token** (standard login with tracking allowed). If the user denied tracking or youâre using limited login (JWT token only), this call will fail. In that case, use the profile data provided in the initial login response.
## â ïž Critical: Backend Token Handling
[Section titled ââ ïž Critical: Backend Token Handlingâ](#ïž-critical-backend-token-handling)
Danger
**CRITICAL iOS BEHAVIOR**: Limited Login and App Tracking Transparency (ATT) are **iOS-ONLY** features. On Android, you will always receive access tokens regardless of the `limitedLogin` setting.
**iOS Token Behavior**:
* **`limitedLogin: true`** â Always JWT token (iOS only)
* **`limitedLogin: false` + User ALLOWS tracking** â Access token
* **`limitedLogin: false` + User DENIES tracking** â JWT token (iOS automatically overrides your setting)
**Android Token Behavior**: Always access token, `limitedLogin` setting is ignored.
Your backend must handle **two different token types** because iOS users can receive either access tokens or JWT tokens depending on their App Tracking Transparency choice, while Android users always receive access tokens.
### Token Types by Platform
[Section titled âToken Types by Platformâ](#token-types-by-platform)
| Platform | limitedLogin Setting | User ATT Choice | Result Token Type |
| ----------- | -------------------- | --------------- | ------------------------- |
| **iOS** | `true` | Any | JWT Token |
| **iOS** | `false` | Allows tracking | Access Token |
| **iOS** | `false` | Denies tracking | JWT Token (auto-override) |
| **Android** | Any | N/A | Access Token (always) |
### Backend Implementation
[Section titled âBackend Implementationâ](#backend-implementation)
1. **Detect Token Type and Handle Accordingly**
```typescript
async function loginWithFacebook() {
try {
const loginResult = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
limitedLogin: false // iOS: depends on ATT, Android: ignored
}
});
if (loginResult.accessToken) {
// Access token (Android always, iOS when tracking allowed)
return handleAccessToken(loginResult.accessToken.token);
} else if (loginResult.idToken) {
// JWT token (iOS only when tracking denied or limitedLogin: true)
return handleJWTToken(loginResult.idToken);
}
} catch (error) {
console.error('Facebook login error:', error);
}
}
```
2. **Firebase Integration Example**
```typescript
import { OAuthProvider, FacebookAuthProvider, signInWithCredential } from 'firebase/auth';
async function handleAccessToken(accessToken: string, nonce: string) {
// For access tokens, use OAuthProvider (new method)
const fbOAuth = new OAuthProvider("facebook.com");
const credential = fbOAuth.credential({
idToken: accessToken,
rawNonce: nonce
});
try {
const userResponse = await signInWithCredential(auth, credential);
return userResponse;
} catch (error) {
console.error('Firebase OAuth error:', error);
return false;
}
}
async function handleJWTToken(jwtToken: string) {
// For JWT tokens, send to your backend for validation
try {
const response = await fetch('/api/auth/facebook-jwt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ jwtToken })
});
const result = await response.json();
return result;
} catch (error) {
console.error('JWT validation error:', error);
return false;
}
}
```
3. **Backend JWT Validation**
```typescript
// Backend: Validate JWT token from Facebook
import jwt from 'jsonwebtoken';
import { Request, Response } from 'express';
app.post('/api/auth/facebook-jwt', async (req: Request, res: Response) => {
const { jwtToken } = req.body;
try {
// Verify JWT token with Facebook's public key
// See: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims
const decoded = jwt.verify(jwtToken, getFacebookPublicKey(), {
algorithms: ['RS256'],
audience: process.env.FACEBOOK_APP_ID,
issuer: 'https://www.facebook.com' // From: https://www.facebook.com/.well-known/openid-configuration/?_rdr
});
// Extract user info from JWT
const userInfo = {
id: decoded.sub,
email: decoded.email,
name: decoded.name,
isJWTAuth: true
};
// Create your app's session/token
const sessionToken = createUserSession(userInfo);
res.json({
success: true,
token: sessionToken,
user: userInfo
});
} catch (error) {
console.error('JWT validation failed:', error);
res.status(401).json({ success: false, error: 'Invalid token' });
}
});
```
4. **Generic Backend Token Handler**
```typescript
// Handle both token types in your backend
async function authenticateFacebookUser(tokenData: any) {
if (tokenData.accessToken) {
// Handle access token - validate with Facebook Graph API
const response = await fetch(`https://graph.facebook.com/me?access_token=${tokenData.accessToken}&fields=id,name,email`);
const userInfo = await response.json();
return {
user: userInfo,
tokenType: 'access_token',
expiresIn: tokenData.expiresIn || 3600
};
} else if (tokenData.jwtToken) {
// Handle JWT token - decode and validate
// See: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims
const decoded = jwt.verify(tokenData.jwtToken, getFacebookPublicKey());
return {
user: {
id: decoded.sub,
name: decoded.name,
email: decoded.email
},
tokenType: 'jwt',
expiresIn: decoded.exp - Math.floor(Date.now() / 1000)
};
} else {
throw new Error('No valid token provided');
}
}
```
### Key Considerations
[Section titled âKey Considerationsâ](#key-considerations)
Danger
**Critical iOS-Only Understanding**:
* **iOS**: Userâs App Tracking choice determines token type, NOT your code settings (even when `limitedLogin: false`)
* **Android**: Always receives access tokens, regardless of `limitedLogin` setting
* **Limited Login is iOS-ONLY** - Android ignores this setting completely
**Access Token (Standard Login)**:
* â
**Android**: Always available (iOS-only restrictions donât apply)
* â
**iOS**: Only when user explicitly allows app tracking
* â
Can be used to access Facebook Graph API
* â
Longer expiration times
* â
More user data available
* â **Becoming less common on iOS** as users increasingly deny tracking
**JWT Token (iOS-Only Privacy Mode)**:
* â **Android**: Never occurs (not supported)
* â
**iOS**: When tracking denied or `limitedLogin: true`
* â
Respects iOS user privacy preferences
* â Contains basic user info only
* â Shorter expiration times
* â No access to Facebook Graph API
* â ïž **Now the most common scenario for iOS users**
**Platform-Specific Behavior**:
* **iOS apps**: Must handle both access tokens AND JWT tokens
* **Android apps**: Only need to handle access tokens
* **Cross-platform apps**: Must implement both token handling methods
Tip
**Essential for iOS**: You MUST implement both token handling methods. Many iOS developers assume theyâll always get access tokens and their apps break when users deny tracking.
## Secure Context Requirements (Web/Capacitor)
[Section titled âSecure Context Requirements (Web/Capacitor)â](#secure-context-requirements-webcapacitor)
### Crypto API Limitations
[Section titled âCrypto API Limitationsâ](#crypto-api-limitations)
The updated Facebook login flow requires the **Web Crypto API** for nonce generation, which is only available in **secure contexts**:
```typescript
// This requires secure context (HTTPS or localhost)
async function sha256(message: string) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); // â Fails in insecure context
// ...
}
```
### Development Environment Issues
[Section titled âDevelopment Environment Issuesâ](#development-environment-issues)
**Common Problem**: `ionic serve` with HTTP URLs breaks Facebook authentication
| Environment | Crypto API Available | Facebook Login Works |
| --------------------------- | -------------------- | -------------------- |
| `http://localhost:3000` | â
Yes | â
Yes |
| `http://127.0.0.1:3000` | â
Yes | â
Yes |
| `http://192.168.1.100:3000` | â No | â No |
| `https://any-domain.com` | â
Yes | â
Yes |
### Solutions for Capacitor Development
[Section titled âSolutions for Capacitor Developmentâ](#solutions-for-capacitor-development)
1. **Use localhost for web testing**
```bash
# Instead of ionic serve --host=0.0.0.0
ionic serve --host=localhost
```
2. **Enable HTTPS in Ionic**
```bash
ionic serve --ssl
```
3. **Test on actual devices**
```bash
# Capacitor apps run in secure context on devices
ionic cap run ios
ionic cap run android
```
4. **Alternative nonce generation for development**
```typescript
async function generateNonce() {
if (typeof crypto !== 'undefined' && crypto.subtle) {
// Secure context - use crypto.subtle
return await sha256(Math.random().toString(36).substring(2, 10));
} else {
// Fallback for development (not secure for production)
console.warn('Using fallback nonce - not secure for production');
return btoa(Math.random().toString(36).substring(2, 10));
}
}
```
### Firebase Integration Note
[Section titled âFirebase Integration Noteâ](#firebase-integration-note)
Recent Firebase documentation requires JWT tokens with nonces for Facebook authentication, regardless of login settings. This approach works with both `limitedLogin: true` and `limitedLogin: false`:
```typescript
// Both modes can return JWT tokens depending on user choice
const loginResult = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
limitedLogin: false, // true = always JWT, false = depends on user tracking choice
nonce: nonce
}
});
```
**Development Limitation**: If youâre using `ionic serve` on a network IP (not localhost), Facebook login will fail due to crypto API restrictions. Use localhost or HTTPS for web testing.
Tip
**Production Safety**: Capacitor apps on iOS/Android always run in secure contexts, so this limitation only affects web development environments.
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
### Common Issues and Solutions
[Section titled âCommon Issues and Solutionsâ](#common-issues-and-solutions)
1. **Key hash errors on Android**
* Double check that youâve added the correct key hash to the Facebook dashboard
* For release builds, make sure youâve added both debug and release key hashes
* Verify youâre using the correct keystore when generating the hash
2. **Facebook login button doesnât appear**
* Verify all manifest entries are correct
* Check that your Facebook App ID and Client Token are correct
* Ensure youâve properly initialized the SDK
3. **Common iOS issues**
* Make sure all Info.plist entries are correct
* Verify URL schemes are properly configured
* Check that your bundle ID matches whatâs registered in the Facebook dashboard
### Testing
[Section titled âTestingâ](#testing)
1. **Before testing, add test users in the Facebook Developer Console**
* Go to Roles > Test Users
* Create a test user
* Use these credentials for testing
2. **Test both debug and release builds**
* Debug build with debug key hash
* Release build with release key hash
* Test on both emulator and physical devices
Remember to test the full login flow, including:
* Successful login
* Login cancellation
* Error handling
* Logout functionality
## Keep going from Facebook Login Setup
[Section titled âKeep going from Facebook Login Setupâ](#keep-going-from-facebook-login-setup)
If you are using **Facebook Login Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Firebase Google Login on Android
> Learn how to set up Google Sign-In with Firebase Authentication on Android using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will help you integrate Google Sign-In with Firebase Authentication on Android. I assume you have already completed the [general Firebase Google setup](/docs/plugins/social-login/firebase/google/general/)
Note
I will assume that you have not yet created your Android app in the Firebase Console. If you have, your steps will be slightly different.
## Setup Steps
[Section titled âSetup Stepsâ](#setup-steps)
1. Go to your project overview over at [console.cloud.google.com](https://console.cloud.google.com/)

2. Click on the `Add app` button

Note
It is possible that you will have to look here for this button. This applies only if you have already created a different app in the Firebase Console.

3. Select `Android`

4. Fill the first part of the form
1. Fill the `Android package name`
1. Open Android Studio at your app using `npx cap open android`
2. At the very bottom of the navigator, find the `Gradle Scripts` 
3. Find `build.gradle` for the module `app` 
4. Copy the `android.defaultConfig.applicationId`. This will be your `package name` in the Firebase console 
Note
The ID shown here will differ from the one I will use for the rest of the guide. I will use `app.capgo.plugin.SocialLogin` for the rest of the guide.
5. Paste it in the Firebase console 
2. Click on the `Register app` button 
5. Skip the `Download and then add config file` step

6. Skip the `Add firebase SDK` step

7. Click on the `Continue to console` button

8. If you do not get automatically authenticated, go to `settings` -> `general` -> `your apps` -> find your android app and click on it

9. Get your SHA1 fingerprint
Follow steps 10-11 from the [Google Login Android setup guide](/docs/plugins/social-login/google/android/#using-google-login-on-android):
1. Now, open the terminal. Make sure that you are in the `android` folder of your app and run `./gradlew signInReport`

2. Scroll to the top of this command. You should see the following. Copy the `SHA1`.

Note
I will use a slightly different SHA1 for the rest of the guide, because I have changed computes since writing the original Google login android setup guide.
Caution
The SHA1 is very important to get right. If you mess up, the authentication will fail in strange ways. Please ****[READ THE GOOGLE LOGIN ANDROID SETUP GUIDE](/docs/plugins/social-login/google/android/#using-google-login-on-android)**** to get it right.
10. Add the SHA1 to the Firebase project
1. Click on the `Add fingerprint` button 
2. Paste the SHA1 you copied in the previous step 
3. Click on the `Save` button 
11. Get your web client ID
1. Go to `Build` -> `Authentication` 
2. Click on the `Sign-in method` button 
3. Click on the `Google` provider 
4. Click on the `Web SDK configuration` button 
5. Copy the `Web client ID`. This will be your `webClientId` in the `initialize` method of the plugin.

12. Use the web client ID in JS.
Note
I recommend using the `authenticateWithGoogle` helper function available in the [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) file of the example app.
At this point, you are ****TECHNICALLY**** ready to use Google Sign-In with Firebase Authentication on Android. However, I would recommend double-checking the setup in the Google Cloud console as explained in the next step.
## Double-check the setup in Google Cloud console
[Section titled âDouble-check the setup in Google Cloud consoleâ](#double-check-the-setup-in-google-cloud-console)
In order to make sure that the setup is correct, you should double-check the setup in the Google Cloud console.
1. Go to [console.cloud.google.com](https://console.cloud.google.com/)
2. Find your project
1. Click on the project selector 
2. Search up your project by the exact name of your Firebase project and click on it. In my case, it is `sociallogin-tutorial-app`. 
3. Open the search bar and open `credentials`
1. Open the search bar 
2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot) 
4. Verify that you see both the Android and Web client IDs in the list.

Caution
If you do not see both the Android and Web client IDs in the list, you have made a mistake in the setup. Please go back and check your steps.
It is also possible, and it has happened to me, that you already have added the Android SHA1 hash with the same app ID in a different project. This will result in Firbase being unable to create an Android client ID. In this case, you will need to remove the SHA1 from the other project as well as on Firebase (using the Firebase console to remove the android app) and recreate it on Firbase
5. Verify that the Android client ID is correctly configured in the Firebase console.
1. Click on the `Android` app 
2. Confirm that the SHA1 hash is correctly configured and that it matches the one you copied in the previous steps. 
6. Verify that the Web client ID is correctly configured in the Firebase console.
1. Click on the `Web` app 
2. Confirm that the client ID matches the one you copied in the previous steps. 
Note
Please ignore the rest of the settings of the web client. We will discuss this on the [web setup guide](/docs/plugins/social-login/firebase/google/web/).
Voila! You are now ready to use Google Sign-In with Firebase Authentication on Android.
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication hangs or fails:
* Verify the `idToken` audience matches your Firebase web client ID
* Check that Google Sign-In is enabled in Firebase Console
* Ensure the SHA-1 fingerprint is correctly configured
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/src/authUtils.ts) for reference
## Keep going from Firebase Google Login on Android
[Section titled âKeep going from Firebase Google Login on Androidâ](#keep-going-from-firebase-google-login-on-android)
If you are using **Firebase Google Login on Android** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Firebase Google Login - General Setup
> Learn how to set up Google Sign-In with Firebase Authentication using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will walk you through integrating Google Sign-In with Firebase Authentication using the Capacitor Social Login plugin. This setup allows you to use native Google Sign-In on mobile platforms while leveraging Firebase Auth for backend authentication.
## Setup Steps
[Section titled âSetup Stepsâ](#setup-steps)
1. Please go to [console.cloud.google.com](https://console.cloud.google.com/)
2. Select the project you want to use 
3. Go to the `Authentication` menu
1. Click on `build`
2. Click on `Authentication`

4. Click on the `Get started` button 
5. Select EITHER `Email/Password` AND `Google` OR `Google` ONLY
Note
I will select `Email/Password` AND `Google`, as I want to use both, but you could select only `Google`. This is something you can change later.
    
6. Enable the `Google` provider 
7. Add the support email  
8. Change the `Public-facing name for project`.
Note
This will be displayed to the users, so I recommend changing it to something more descriptive.

9. Click on the `Save` button 
VoilĂ , you have now enabled Google Sign-In with Firebase Authentication đ
## Keep going from Firebase Google Login - General Setup
[Section titled âKeep going from Firebase Google Login - General Setupâ](#keep-going-from-firebase-google-login---general-setup)
If you are using **Firebase Google Login - General Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Firebase Google Login on iOS
> Learn how to set up Google Sign-In with Firebase Authentication on iOS using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will help you integrate Google Sign-In with Firebase Authentication on iOS. I assume you have already completed the [general Firebase Google setup](/docs/plugins/social-login/firebase/google/general/).
Note
I will assume that you have not yet created your iOS app in the Firebase Console. If you have, your steps will be slightly different.
## Setup Steps
[Section titled âSetup Stepsâ](#setup-steps)
1. Go to your project overview over at [console.cloud.google.com](https://console.cloud.google.com/)

2. Click on the `Add app` button

Note
It is possible that you will have to look here for this button. This applies only if you have already created a different app in the Firebase Console.

3. Select `iOS`

4. Fill the first part of the form
1. Fill the `Apple bundle ID`
1. Open Xcode at your app using `npx cap open ios`
2. Double click on `App` 
3. Ensure that you are on `Targets -> App` 
4. Find your `Bundle Identifier` 
Note
The ID shown here will differ from the one I will use for the rest of the guide. I will use `app.capgo.plugin.SocialLogin` for the rest of the guide.
5. Copy the `Bundle Identifier` and paste it in the Firebase console 
2. Click on the `Register app` button 
5. Skip the `Download config file` step

6. Skip the `Add firebase SDK` step

7. Skip the `Add initialization code` step

8. Click on the `Continue to console` button

9. Get your iOS client ID and your `YOUR_DOT_REVERSED_IOS_CLIENT_ID`
1. Go to Google Cloud Console at [console.cloud.google.com](https://console.cloud.google.com/)
2. Find your project
1. Click on the project selector 
2. Search up your project by the exact name of your Firebase project and click on it. In my case, it is `sociallogin-tutorial-app`. 
3. Open the search bar and open `credentials`
1. Open the search bar 
2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot) 
4. Click on the `iOS client for [YOUR_APP_ID] (auto created by Google Service)` one. In my case, it is `sociallogin-tutorial-app`.

5. Copy the `Client ID` as well as the `iOS URL scheme`. This will be respectively your `iOSClientId` and `YOUR_DOT_REVERSED_IOS_CLIENT_ID`.
Note
You will pass the `iOSClientId` in the `initialize` method of the plugin, while you will use the `YOUR_DOT_REVERSED_IOS_CLIENT_ID` in the `Info.plist` file of your app, as explained in the next part of this guide.

10. Get your web client ID
1. Go back to the Firebase console and go to `Build` -> `Authentication` 
2. Click on the `Sign-in method` button 
3. Click on the `Google` provider 
4. Click on the `Web SDK configuration` button 
5. Copy the `Web client ID`. This will be your `webClientId` in the `initialize` method of the plugin. 
11. Modify your appâs Info.plist
1. Open Xcode and find the `Info.plist` file

2. Right click this file and open it as source code

3. At the bottom of your `Plist` file, you will see a `` tag

4. Insert the following fragment just before the closing `` tag

```xml
CFBundleURLTypes
CFBundleURLSchemes
YOUR_DOT_REVERSED_IOS_CLIENT_ID
GIDClientID
YOUR_IOS_CLIENT_ID.apps.googleusercontent.com
```
5. Change the `YOUR_DOT_REVERSED_IOS_CLIENT_ID` to the value copied in step 9 (the iOS URL scheme)

Caution
Ensure that this value **STARTS** with `com.googleusercontent.apps`
12. Change the `YOUR_IOS_CLIENT_ID` to the iOS Client ID you copied in step 9
13. Save the file with `Command + S`
14. Modify the `AppDelegate.swift`
1. Open the AppDelegate

2. Insert `import GoogleSignIn` at the top of the file

3. Find the `func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])` function

4. Modify the function to look like this
```swift
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
var handled: Bool
handled = GIDSignIn.sharedInstance.handle(url)
if handled {
return true
}
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
```

5. Save the file with `Command + S`
15. Using the Google login in your app
At this step, you are ready to use the Google login in your app. Please use the [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) file of the example app to authenticate with Google.
The user will be automatically created in Firebase Auth on first sign-in
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication hangs or fails:
* Verify the `idToken` audience matches your Firebase web client ID
* Check that Google Sign-In is enabled in Firebase Console
* Ensure Info.plist has the correct URL schemes and GIDClientID
* Verify `iOSServerClientId` matches your web client ID
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/src/authUtils.ts) for reference
## Keep going from Firebase Google Login on iOS
[Section titled âKeep going from Firebase Google Login on iOSâ](#keep-going-from-firebase-google-login-on-ios)
If you are using **Firebase Google Login on iOS** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Firebase Google Login on Web
> Learn how to set up Google Sign-In with Firebase Authentication on Web using Firebase's built-in Google Sign-In.
## Introduction
[Section titled âIntroductionâ](#introduction)
The Capacitor Social Login plugin **does not support web platforms**. For web applications, you should use Firebaseâs built-in Google Sign-In directly, which provides a more reliable popup-based authentication flow.
## Why Not Use the Plugin on Web?
[Section titled âWhy Not Use the Plugin on Web?â](#why-not-use-the-plugin-on-web)
The Capacitor Social Login plugin is designed for native mobile platforms (Android and iOS) where it can leverage platform-specific authentication flows. For web, Firebaseâs native `signInWithPopup` method is:
* â
More reliable and better supported
* â
Handles browser session storage automatically
* â
Provides better error handling
* â
No additional configuration needed
## Setup Steps
[Section titled âSetup Stepsâ](#setup-steps)
1. **Configure Firebase Project**
Ensure your Firebase project has Google Sign-In enabled:
* Go to [Firebase Console](https://console.firebase.google.com/)
* Navigate to Authentication > Sign-in method
* Enable Google Sign-In provider
2. Add your authorized domains
1. Go to your project overview over at [console.cloud.google.com](https://console.cloud.google.com/) 
2. Open the `Authentication` menu 
3. Click on the `Settings` button 
4. Setup the `Authorized domains` 
## Example Implementation
[Section titled âExample Implementationâ](#example-implementation)
See the [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) file in the example app for a complete implementation that:
* Uses Firebaseâs `signInWithPopup` for web platforms
* Uses Capacitor Social Login plugin for Android/iOS platforms
* Handles platform detection automatically
The example shows how to conditionally use Firebaseâs built-in method for web while using the plugin for native platforms.
## Additional Resources
[Section titled âAdditional Resourcesâ](#additional-resources)
* [Firebase Authentication Documentation](https://firebase.google.com/docs/auth) - Complete Firebase Auth documentation
* [Firebase Google Sign-In for Web](https://firebase.google.com/docs/auth/web/google-signin) - Official Firebase guide for Google Sign-In on web
* [Google Login Setup Guide](/docs/plugins/social-login/google/general/) - Guide for configuring authorized domains and OAuth consent screen
## Keep going from Firebase Google Login on Web
[Section titled âKeep going from Firebase Google Login on Webâ](#keep-going-from-firebase-google-login-on-web)
If you are using **Firebase Google Login on Web** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Firebase Integration Introduction
> Learn how to integrate Firebase Authentication with the Capacitor Social Login plugin for a complete authentication solution.
## Overview
[Section titled âOverviewâ](#overview)
This tutorial will guide you through setting up Firebase Authentication with the Capacitor Social Login plugin. This integration allows you to use native social login providers (Google, Apple, Facebook, Twitter) on mobile platforms while leveraging Firebase Auth for backend authentication and Firestore for data storage.
## What Youâll Learn
[Section titled âWhat Youâll Learnâ](#what-youll-learn)
* How to configure Firebase Authentication
* How to integrate Capacitor Social Login plugin with Firebase Auth
* Platform-specific setup for Android, iOS, and Web
## What Youâll Need
[Section titled âWhat Youâll Needâ](#what-youll-need)
Before you begin, make sure you have:
1. **A Firebase Project**
* Create a project at [Firebase Console](https://console.firebase.google.com/)
* Enable Authentication (Email/Password and Google Sign-In)
* Get your Firebase configuration credentials
2. **Firebase JS SDK**
* Install Firebase in your project:
```bash
npm install firebase
```
3. **A Capacitor Project**
* An existing Capacitor application
* Capacitor Social Login plugin installed:
```bash
npm install @capgo/capacitor-social-login
npx cap sync
```
## Example Application
[Section titled âExample Applicationâ](#example-application)
A complete working example is available in the repository:
**Code Repository**: [You can find the code repository here](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app)
The example app demonstrates:
* Email/password authentication with Firebase
* Google Sign-In integration (Android, iOS, and Web)
* A simple key-value store using Firebase Firestore collections
* User-specific data storage in Firestore subcollections
## A word about using the Firebase SDK on Capacitor
[Section titled âA word about using the Firebase SDK on Capacitorâ](#a-word-about-using-the-firebase-sdk-on-capacitor)
When using the Firebase JS SDK on Capacitor, you need to be aware that when using the authentication methods, you need to initialize the Firebase Auth instance a bit differently.
On the web platform, you would use the `getAuth` function to get the Firebase Auth instance.
```typescript
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
```
Unfortunately, on Capacitor, this does not work and causes Firebase auth to hang. As stated in [this blog post](https://harryherskowitz.com/2021/08/23/firebase-capacitor.html), you need to use the `initializeAuth` function to initialize the Firebase Auth instance. This looks like this:
```typescript
import { initializeApp } from 'firebase/app';
import { initializeAuth } from 'firebase/auth';
const app = initializeApp(firebaseConfig);
function whichAuth() {
let auth;
if (Capacitor.isNativePlatform()) {
auth = initializeAuth(app, {
persistence: indexedDBLocalPersistence,
});
} else {
auth = getAuth(app);
}
return auth;
}
export const auth = whichAuth();
```
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
Continue with the setup guides:
* [Firebase Setup](../google/general/) - Configure Firebase project
* [Android Setup](../google/android/) - Android-specific configuration
* [iOS Setup](../google/ios/) - iOS-specific configuration
* [Web Setup](../google/web/) - Web-specific configuration
## Keep going from Firebase Integration Introduction
[Section titled âKeep going from Firebase Integration Introductionâ](#keep-going-from-firebase-integration-introduction)
If you are using **Firebase Integration Introduction** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Getting Started
> Discover how to install and configure the Capacitor Social Login plugin to enhance your app's authentication with seamless integration for Google, Apple, Facebook, and generic OAuth2 logins.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/cap-go/capacitor-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `cap-go/capacitor-skills` to install the `@capgo/capacitor-social-login` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```bash
npm install @capgo/capacitor-social-login
```
2. **Sync with native projects**
```bash
npx cap sync
```
3. **Initialize in app startup**
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
google: {
webClientId: 'your-google-web-client-id',
iOSClientId: 'your-google-ios-client-id',
iOSServerClientId: 'your-google-web-client-id',
mode: 'online',
},
apple: {
clientId: 'your-apple-service-id',
useProperTokenExchange: true,
useBroadcastChannel: true,
},
facebook: {
appId: 'your-facebook-app-id',
},
twitter: {
clientId: 'your-twitter-client-id',
redirectUrl: 'myapp://oauth/twitter',
},
oauth2: {
github: {
appId: 'your-github-client-id',
authorizationBaseUrl: 'https://github.com/login/oauth/authorize',
accessTokenEndpoint: 'https://github.com/login/oauth/access_token',
redirectUrl: 'myapp://oauth/github',
scope: 'read:user user:email',
pkceEnabled: true,
},
},
});
```
## Core flow examples
[Section titled âCore flow examplesâ](#core-flow-examples)
### Login
[Section titled âLoginâ](#login)
```typescript
await SocialLogin.login({
provider: 'google',
options: { scopes: ['profile', 'email'] },
});
await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'github',
scope: 'read:user user:email',
},
});
```
### Session checks
[Section titled âSession checksâ](#session-checks)
```typescript
const status = await SocialLogin.isLoggedIn({ provider: 'google' });
await SocialLogin.logout({ provider: 'google' });
```
### Auth codes and refresh
[Section titled âAuth codes and refreshâ](#auth-codes-and-refresh)
```typescript
// For providers that support this mode
const authCodeResult = await SocialLogin.getAuthorizationCode({ provider: 'google' });
await SocialLogin.refresh({ provider: 'google', options: {} as never });
```
### Advanced helpers
[Section titled âAdvanced helpersâ](#advanced-helpers)
```typescript
const jwt = await SocialLogin.decodeIdToken({
idToken: 'eyJhbGciOi...',
});
const { date } = await SocialLogin.getAccessTokenExpirationDate({
accessTokenExpirationDate: Date.now() + 3600 * 1000,
});
const expired = await SocialLogin.isAccessTokenExpired({
accessTokenExpirationDate: Date.now() + 1000,
});
const active = await SocialLogin.isRefreshTokenAvailable({ refreshToken: 'a-token' });
```
## Provider-specific notes
[Section titled âProvider-specific notesâ](#provider-specific-notes)
### Google offline mode
[Section titled âGoogle offline modeâ](#google-offline-mode)
`google.mode: 'offline'` returns `serverAuthCode` from login. In this mode logout, isLoggedIn, getAuthorizationCode, and refresh are not available.
Use `serverAuthCode` only as input to your backend token exchange. If you need to call `SocialLogin.refresh()` in the app, use `google.mode: 'online'` instead.
### Apple
[Section titled âAppleâ](#apple)
Set `useProperTokenExchange: true` for strict token handling and `useBroadcastChannel: true` for Android simplified setup.
### OAuth2 web redirect flow
[Section titled âOAuth2 web redirect flowâ](#oauth2-web-redirect-flow)
Use `OAuth2LoginOptions.flow: 'redirect'` for web flows that navigate away from the page.
## Dynamic provider dependencies
[Section titled âDynamic provider dependenciesâ](#dynamic-provider-dependencies)
You can configure which providers to include to reduce native app size. This is most useful when your app only needs specific providers.
Add provider configuration to `capacitor.config.ts`:
```typescript
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.app',
appName: 'MyApp',
webDir: 'dist',
plugins: {
SocialLogin: {
providers: {
google: true,
facebook: true,
apple: true,
twitter: false,
},
logLevel: 1,
},
},
};
export default config;
```
Provider values mean:
* `true`: the provider is enabled and its native dependencies are bundled.
* `false`: the provider is disabled and its native dependencies are not bundled.
Important details:
* Run `npx cap sync` after changing provider configuration.
* If no provider configuration is supplied, all providers default to `true` for backward compatibility.
* Disabling a provider with `false` makes it unavailable at runtime, even if that provider only uses system APIs.
* This configuration only affects iOS and Android; it does not affect Web.
* Apple Sign-In on Android uses OAuth without external SDK dependencies.
* Twitter/X uses standard OAuth 2.0 without external SDK dependencies.
To include only Google Sign-In and Apple Sign-In:
```typescript
plugins: {
SocialLogin: {
providers: {
google: true,
facebook: false,
apple: true,
twitter: false,
},
},
}
```
## Related documentation
[Section titled âRelated documentationâ](#related-documentation)
* [Integrations overview](/docs/plugins/social-login/integrations/)
* [Better Auth integration](/docs/plugins/social-login/better-auth/)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Troubleshooting](/docs/plugins/social-login/troubleshooting/)
* [Privacy manifest and iOS URL handlers](/docs/plugins/social-login/privacy-and-ios-handlers/)
* [Migrate from Ionic Auth Connect](/docs/upgrade/from-ionic-auth-connect/)
* [Social Login Auth Connect migration guide](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [Migrate legacy providers](/docs/plugins/social-login/migrations/google/)
* [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/)
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Google Login on Android
> This guide provides a comprehensive walkthrough on setting up Google Login using Capacitor for Android devices, detailing each step to ensure a smooth integration process and addressing potential challenges you may encounter.
## Introduction
[Section titled âIntroductionâ](#introduction)
In this guide, you will learn how to setup Google Login with Capgo Social Login for Android. I assume that you have already read the [general setup guide](/docs/plugins/social-login/google/general/).
## Using Google login on Android
[Section titled âUsing Google login on Androidâ](#using-google-login-on-android)
In this part, you will learn how to setup Google login in Android.
Caution
The Android SHA1 certificate is beyond painful and I wouldnât wish it on anyone to have to set this up. The following steps assume the simplest scenario of an app that isnât published to Google Play Store and that is only used by the local simulator, or development hardware device.
If you have deployed your app to Google Play Store, you **MUST** add an additional Android client ID that contains the SHA1 from Google Play console for production releases. You can find the SHA1 hash that Google Play uses to sign your release bundle under `Test and release > Setup > App Signing`.
Finally, itâs important to mention that if you mess up, the error will NOT be obvious. It may be very difficult to debug. If you struggle with the setup, please look at the [Github issues](https://github.com/Cap-go/capacitor-social-login/issues).
Additionally, you may look at the troubleshooting section of the [Google Login setup for Android](#troubleshooting) for more information.
Note
You may create multiple Android client IDs. This is required if you have multiple SHA1 certificates.
1. Create an Android client ID.
1. Click on the search bar

2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot)

3. Click on the `create credentials`

4. Select `OAuth client ID`

5. Select the `Android` application type

6. Open Android Studio
7. At the very bottom of the navigator, find the `Gradle Scripts`

8. Find `build.gradle` for the module `app`

9. Copy the `android.defaultConfig.applicationId`. This will be your `package name` in the Google console

10. Now, open the terminal. Make sure that you are in the `android` folder of your app and run `./gradlew signInReport`

11. Scroll to the top of this command. You should see the following. Copy the `SHA1`.

12. Now, go back to the Google Console. Enter your `applicationId` as the `Package Name` and your SHA1 in the certificate field and click `create`

2. Create a web client (this is required for Android)
1. Go to the `Create credentials` page in Google Console
2. Set application type to `Web`

3. Click `Create`

4. Copy the client ID, youâll use this as the `webClientId` in your JS/TS code

3. Modify your `MainActivity`
1. Please open your app in Android Studio. You can run `cap open android`
2. Find `MainActivity.java`
1. Open the `app` folder

2. Find `java`

3. Find your `MainActivity.java` and click on it

3. Modify `MainActivity.java`. Please add the following code
```java
import ee.forgr.capacitor.social.login.GoogleProvider;
import ee.forgr.capacitor.social.login.SocialLoginPlugin;
import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin;
import com.getcapacitor.PluginHandle;
import com.getcapacitor.Plugin;
import android.content.Intent;
import android.util.Log;
import com.getcapacitor.BridgeActivity;
// ModifiedMainActivityForSocialLoginPlugin is VERY VERY important !!!!!!
public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) {
PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin");
if (pluginHandle == null) {
Log.i("Google Activity Result", "SocialLogin login handle is null");
return;
}
Plugin plugin = pluginHandle.getInstance();
if (!(plugin instanceof SocialLoginPlugin)) {
Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin");
return;
}
((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data);
}
}
// This function will never be called, leave it empty
@Override
public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {}
}
```
4. Save the file
4. Use Google Login in your application
1. First, import `SocialLogin`
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
```
2. Call initialize. This should be called only once.
```typescript
// onMounted is Vue specific
// webClientId is the client ID you got in the web client creation step not the android client ID.
onMounted(() => {
SocialLogin.initialize({
google: {
webClientId: '673324426943-avl4v9ubdas7a0u7igf7in03pdj1dkmg.apps.googleusercontent.com',
}
})
})
```
3. Call `SocialLogin.login`. Create a button and run the following code on click.
```typescript
const res = await SocialLogin.login({
provider: 'google',
options: {}
})
// handle the response
console.log(JSON.stringify(res))
```
Caution
If you initialize Google with `mode: 'offline'`, `SocialLogin.login()` returns `serverAuthCode` for your backend exchange flow. Do not call `SocialLogin.refresh({ provider: 'google' })` in the app in that mode. Exchange `serverAuthCode` on your backend, store the Google refresh token there, and refresh on the backend.
5. Configure the emulator for testing
1. Go into `Device manager` and click the plus button

2. Create a virtual device

3. Select any device with a `Play Store` icon

As you can see, the `pixel 8` supports the `Play Store` services
4. Click `next`

5. Make sure that the OS image is of type `Google Play`. **IT MUST** be of type `Google Play`

6. Click next

7. Confirm your device. You can name your emulator as you prefer

8. Go into `Device Manager` and boot up your simulator

9. After the simulator boots up, go into its settings

10. Go into `Google Play`

11. Click `Update` and wait about 60 seconds

6. Test your application
If you did everything correctly, you should see the Google login flow working properly:

## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If you have any issues, first compare your app configuration with the Google Cloud Console values. Android Google Sign-In failures are usually caused by a package name, SHA-1, or client ID mismatch.
### Credential Manager, SHA-1, and Firebase
[Section titled âCredential Manager, SHA-1, and Firebaseâ](#credential-manager-sha-1-and-firebase)
On Android this plugin uses Google Credential Manager (`androidx.credentials` and Sign in with Google), not the legacy `GoogleSignInClient` API. Logcat errors such as `GetCredentialCustomException: [28444] Developer console is not set up correctly` come from that stack.
Filter Logcat with `GoogleProvider` or `CapgoSocialLogin` after a failed login. The plugin logs your package name, signing SHA-1, and a masked `webClientId` so you can compare them against Google Cloud Console.
### Required Google Cloud setup
[Section titled âRequired Google Cloud setupâ](#required-google-cloud-setup)
You need two kinds of OAuth 2.0 client IDs in the same Google Cloud project:
| Client type | Used for | Where it goes |
| ---------------------------- | ----------------------------------------- | --------------------------------------------------------------- |
| Web application | Server / ID token audience | `webClientId` in `SocialLogin.initialize()` |
| Android, one per signing key | Proves your APK is allowed to call Google | Google Cloud Console only. Do not pass this ID to `webClientId` |
A common mistake is using the Android client ID as `webClientId`. Credential Manager requires the Web client ID there. The Android client only needs the correct package name and SHA-1 registered in the console.
Create one Android OAuth client for each certificate that signs builds you test:
* Debug: from `./gradlew signingReport` for the debug variant.
* Release: from the APK or AAB you actually install.
* Play App Signing: from Play Console > App integrity > App signing key certificate. This is required for Play Store builds even if your upload key SHA-1 is already registered.
The `applicationId` in `android/app/build.gradle` must exactly match the Android OAuth client package name, including any `.debug` suffix.
If the OAuth consent screen is in Testing mode, add every Google account you test with under Audience > Test users. Publishing the app to Production is not required for `email` and `profile` scopes. Digital Asset Links (`assetlinks.json`) are not required for Sign in with Google through Credential Manager.
Google Cloud changes can take up to a few hours to propagate.
### Error 28444: Developer console is not set up correctly
[Section titled âError 28444: Developer console is not set up correctlyâ](#error-28444-developer-console-is-not-set-up-correctly)
This almost always means Google rejected the combination of installed APK signing certificate, package name, and `webClientId`. Work through this checklist:
1. Confirm `webClientId` is the Web application client ID and ends with `.apps.googleusercontent.com`.
2. Run the app, reproduce the failure, and read Logcat for `GoogleProvider`, `signingSha1=`, and `package=`.
3. In [Google Cloud Console > Credentials](https://console.cloud.google.com/apis/credentials), open your Android OAuth client and verify the exact package name and SHA-1.
4. If testing a release build, register the SHA-1 from that build, not only the debug keystore.
5. If the app is distributed through Play Store, also register the Play App Signing SHA-1.
6. Ensure Web and Android clients live in the same Google Cloud project.
7. If the consent screen is in Testing, confirm the Google account is a test user.
8. Wait and retry after console changes.
`USER_CANCELLED` after picking an account on a misconfigured debug build can still be a SHA-1 or client ID mismatch. Fix the console setup above first.
### Extract SHA-1 from the build you install
[Section titled âExtract SHA-1 from the build you installâ](#extract-sha-1-from-the-build-you-install)
Debug or local builds:
```bash
cd android && ./gradlew signingReport
```
Signed release APK:
```bash
keytool -printcert -jarfile android/app/release/app-release.apk
```
Add that SHA-1 to an Android OAuth client with the matching package name, reinstall the same signed APK, and test again:
```bash
adb install android/app/release/app-release.apk
```
### Reading the login result
[Section titled âReading the login resultâ](#reading-the-login-result)
Tokens are nested under `result`:
```typescript
const login = await SocialLogin.login({ provider: 'google' });
const idToken = login.result?.idToken;
```
For Firebase Auth, create credentials with that `idToken` and use the Web client ID as `webClientId` in `initialize`.
If you cannot get the development SHA-1 certificate, try a custom keystore. [This issue comment](https://github.com/Cap-go/capacitor-social-login/issues/147#issuecomment-2849742574) explains how to add a keystore to your project.
## Keep going from Google Login on Android
[Section titled âKeep going from Google Login on Androidâ](#keep-going-from-google-login-on-android)
If you are using **Google Login on Android** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Google Login Setup
> This guide provides a comprehensive overview on setting up Google Login using Capacitor, detailing each step to ensure a seamless integration process and addressing potential challenges you may encounter.
## Introduction
[Section titled âIntroductionâ](#introduction)
In this guide, you will learn how to setup Google Login with Capgo Social Login. You will need the following in order to setup Google Login:
* A Google account
## General setup
[Section titled âGeneral setupâ](#general-setup)
Note
This step is required regardless of which the platform you decide to use.
In this part, you will setup the login screen displayed by Google.
1. Please go to [console.cloud.google.com](https://console.cloud.google.com/)
2. Click on the project selector 
3. If you donât have a project already, please **create a new project**.
1. Click on `New project` 
2. Name your project and click `Create` 
3. Ensure that you are on the right project 
4. Start to configure the `OAuth consent screen`
1. Click on the search bar

2. Search for `OAuth consent screen` and click on it

3. Configure the consent screen
Note
I will assume that you are developing an app open to the public, so I will use the `external` user type. Please select the user type that suits you the best AND click `create`
Click on `create`

5. Fill the information about your app
1. Letâs start with the `App Information`

* Please type in your `App Name`
Caution
**THIS WILL BE DISPLAYED TO THE USERS**
* Enter the `user support email`
Note
You can learn more about the support email [here](https://support.google.com/cloud/answer/10311615#user-support-email\&zippy=%2Cuser-support-email/)
2. You **CAN** add the app logo.

Note
This is not obligatory and I will skip this step
3. You **SHOULD** configure the `App domain`

Note
I will not do that because this is just a simple demonstration that will **NOT** get published, but I strongly recommend filling this section.
4. You **HAVE TO** provide the developerâs email

5. Click on `save and continue`

6. Configure the scopes
1. Click on `add or remove scopes`

2. Select the following scopes and click `update`

3. Click `save and continue`

7. Add a test user
1. Click on `add users` 
2. Enter your Google email, click enter, and click `add` 
3. Click `save and continue` 
8. Click `back to dashboard` 
9. Submit your app for verification
Note
I strongly recommend submitting you app for verification. This is outside the scope of this tutorial. You can learn more [here](https://support.google.com/cloud/answer/13463073/). This isnât required for local testing, but is required for production.
## Differences between online access and offline access
[Section titled âDifferences between online access and offline accessâ](#differences-between-online-access-and-offline-access)
There are multiple ways to use Google Login with Capacitor. Here is a table that summarizes the differences between the two:
| | Online access | Offline access |
| :---------------------: | :-----------: | :------------: |
| Requires a backend | â | â
|
| Long-lived access token | â | â
|
| Easy setup | â
| â |
Note
**Offline mode** means that your backend can access Googleâs APIs even when the user is not actively using your app (i.e., when the user is âofflineâ from your appâs perspective). Long-lived access tokens enable this functionality by allowing the backend to call Google APIs on behalf of the user at any time.
Caution
**Offline mode REQUIRES a backend server.** When using offline mode, the frontend receives minimal information (primarily just a server auth code). Without a backend to exchange this code for tokens and user information, offline mode provides no useful data to your frontend application.
Caution
`SocialLogin.refresh({ provider: 'google' })` does **not** work with `google.mode: 'offline'`. In offline mode the plugin only gives you `serverAuthCode`, and your backend must exchange that code for access and refresh tokens, then refresh those tokens on the backend.
If you still do not know which one you should choose, please consider the following scenarios:
1. You want the user to login, immediately after you are going to issue him a custom JWT. Your app will NOT call Google APIs
In this case, choose online access.
2. Your app will call some Google APIs from the client, but never from the backend
In this case, choose online access
3. Your app will call some google APIs from the backend, but only when the user is actively using the app
In this case, choose online access
4. Your app will periodically check the userâs calendar, even when he is not actively using the app
In this case, choose offline access
## An example backend for online access
[Section titled âAn example backend for online accessâ](#an-example-backend-for-online-access)
In this part of the tutorial, I will show how to validate the user on your backend.
This example will be very simple and it will be based on the following technologies:
* [Typescript](https://www.typescriptlang.org/)
* [Hono](https://hono.dev/)
* [Javascriptâs fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch)
You can find the code for this example [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/141c01d93a85240e31a0d488a89df13c842708b1/index.ts#L135-L153)
As you can see:

The idea is rather simple. You send a simple `GET` request to `https://www.googleapis.com/oauth2/v3/tokeninfo` and this returns you whether the token is valid or not and if it it is, it gives you the email of the user. It also gives you some other info about the user token

From there, you could issue the user with your own JWT or issue some sort of session cookie. The possibilities are endless, for the final auth implementation.
If you do want to call Google APIâs, I would strongly recommend looking at [Googleâs OAuth 2.0 Playground](https://developers.google.com/oauthplayground). From there you can easily see what APIs you can call.
## Using offline access with your own backend
[Section titled âUsing offline access with your own backendâ](#using-offline-access-with-your-own-backend)
In order to use offline access you will need the following:
* An HTTP server
In this example, I will be using the following technologies to provide the offline access in my app:
* [Hono](https://hono.dev/)
* [Hono Zod validator](https://hono.dev/docs/guides/validation#with-zod)
* [Zod](https://zod.dev/)
* [Hono JWT](https://hono.dev/docs/helpers/jwt#jwt-authentication-helper)
* [LowDb](https://www.npmjs.com/package/lowdb) (a simple database)
The code for this example can be found [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/aac7a8c909f650a8c2cd7f88c97f5f3c594ce9ba/index.ts#L139-L287)
As for the client code, it looks like this:
```typescript
import { Capacitor } from '@capacitor/core';
import { GoogleLoginOfflineResponse, SocialLogin } from '@capgo/capacitor-social-login';
import { usePopoutStore } from '@/popoutStore'; // <-- specific to my app
const baseURL = "[redacted]";
async function fullLogin() {
await SocialLogin.initialize({
google: {
webClientId: '[redacted]',
iOSClientId: '[redacted]',
iOSServerClientId: 'The same value as webClientId',
mode: 'offline' // <-- important
}
})
const response = await SocialLogin.login({
provider: 'google',
options: {
forceRefreshToken: true // <-- important
}
})
if (response.provider === 'google') {
const result = response.result as GoogleLoginOfflineResponse
const res = await fetch(`${baseURL}/auth/google_offline`, {
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
serverAuthCode: result.serverAuthCode,
platform: Capacitor.getPlatform()
}),
method: "POST"
})
if (res.status !== 200) {
popoutStore.popout("Full google login failed", "check console");
return
}
const { jwt } = await res.json();
const userinfo = await fetch(`${baseURL}/auth/get_google_user`, {
headers: {
Authorization: `Bearer ${jwt}`
}
})
if (userinfo.status !== 200) {
popoutStore.popout("Full google (userinfo) login failed", "check console");
return
}
popoutStore.popout("userinfo res", await userinfo.text());
}
}
```
Notice what is missing here: there is no `SocialLogin.refresh()` call in the app. That is intentional. In Google offline mode, refresh happens after your backend exchanges `serverAuthCode` and stores the refresh token securely.
## Keep going from Google Login Setup
[Section titled âKeep going from Google Login Setupâ](#keep-going-from-google-login-setup)
If you are using **Google Login Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Google Login on iOS
> This guide offers a comprehensive walkthrough for configuring Google Login with Capacitor on iOS, detailing each step to ensure a smooth integration process.
## Introduction
[Section titled âIntroductionâ](#introduction)
In this guide, you will learn how to setup Google Login with Capgo Social Login for iOS. I assume that you have already read the [general setup guide](/docs/plugins/social-login/google/general/).
## Using Google login on iOS
[Section titled âUsing Google login on iOSâ](#using-google-login-on-ios)
In this part, you will learn how to setup Google login in iOS.
1. Create an iOS client ID in the Google console
1. Click on the search bar

2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot)

3. Click on the `create credentials`

4. Select `OAuth client ID`

5. Select the `Application type` to `iOS`

6. Find the bundle ID
1. Open Xcode
2. Double click on `App`

3. Ensure that you are on `Targets -> App`

4. Find your `Bundle Identifier`

5. Go back to the Google Console and paste your `Bundle Identifier` into `Bundle ID`

7. Optionally, add your `App Store ID` or `Team ID` into the client ID if you have published your app to App Store
8. After filling all the details, click `create`

9. Click `OK`

10. Open the newly created iOS client

11. Copy the following data

Note
The `nr. 1` in this image will later become the `iOSClientId` in the `initialize` call.
The `nr. 2` in this image will later become `YOUR_DOT_REVERSED_IOS_CLIENT_ID`
2. Modify your appâs Info.plist
1. Open Xcode and find the `Info.plist` file

2. Right click this file and open it as source code

3. At the bottom of your `Plist` file, you will see a `` tag

4. Insert the following fragment just before the closing `` tag

```xml
CFBundleURLTypes
CFBundleURLSchemes
YOUR_DOT_REVERSED_IOS_CLIENT_ID
```
5. Change the `YOUR_DOT_REVERSED_IOS_CLIENT_ID` to the value copied in the previous step

Caution
Ensure that this value **STARTS** with `com.googleusercontent.apps`
6. Save the file with `Command + S`
3. Modify the `AppDelegate.swift`
1. Open the AppDelegate

2. Insert `import GoogleSignIn` at the top of the file

3. Find the `func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])` function

4. Modify the function to look like this
```swift
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
var handled: Bool
handled = GIDSignIn.sharedInstance.handle(url)
if handled {
return true
}
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
```

5. Save the file with `Command + S`
4. Setup Google login in your JavaScript/TypeScript code
1. Import `SocialLogin` and `Capacitor`
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
import { Capacitor } from '@capacitor/core';
```
2. Call the initialize method (this should be called only once)
**Basic setup (online mode - recommended for most apps):**
```typescript
// onMounted is Vue specific
onMounted(() => {
SocialLogin.initialize({
google: {
iOSClientId: '673324426943-redacted.apps.googleusercontent.com',
mode: 'online' // Default mode
}
})
})
```
**Advanced setup with additional client IDs:**
```typescript
onMounted(() => {
SocialLogin.initialize({
google: {
webClientId: 'YOUR_WEB_CLIENT_ID', // Optional: for web platform support
iOSClientId: 'YOUR_IOS_CLIENT_ID', // Required: from step 1
iOSServerClientId: 'YOUR_WEB_CLIENT_ID', // Optional: same as webClientId, needed for some advanced features
mode: 'online' // 'online' or 'offline'
}
})
})
```
Note
**Client ID Requirements:**
* `iOSClientId`: **Required** - Must end with `googleusercontent.com` (from step 1 above)
* `webClientId`: **Optional** - Only needed if you also support web platform or need advanced features
* `iOSServerClientId`: **Optional** - Should be the same value as `webClientId` when provided
For advanced setup with `webClientId` and `iOSServerClientId`, see the [web setup guide](/docs/plugins/social-login/google/web/) for creating these credentials.
Caution
**About offline mode:** When using `mode: 'offline'`, the login response will not contain user data directly. Instead, youâll receive a server auth code that must be exchanged for user information via your backend server. `SocialLogin.refresh({ provider: 'google' })` is not available in this mode; refresh must happen on your backend after exchanging `serverAuthCode`. See the [general setup guide](/docs/plugins/social-login/google/general/#using-offline-access-with-your-own-backend) for implementation details.
3. Implement the login function. Create a button and run the following code on click
**For online mode:**
```typescript
const res = await SocialLogin.login({
provider: 'google',
options: {}
})
// handle the response - contains user data
console.log(JSON.stringify(res))
```
**For offline mode:**
```typescript
const res = await SocialLogin.login({
provider: 'google',
options: {
forceRefreshToken: true // Recommended for offline mode
}
})
// res contains serverAuthCode, not user data
// Send serverAuthCode to your backend to get user information
// Do not call SocialLogin.refresh() in offline mode
console.log('Server auth code:', res.result.serverAuthCode)
```
5. Test your application
1. Build your app and run `cap sync`
2. If youâve done everything correctly, you should see the Google login flow working properly

Note
The language in the Google prompt depends on your deviceâs language settings.
## Known Problems
[Section titled âKnown Problemsâ](#known-problems)
### Privacy Screen Plugin Incompatibility
[Section titled âPrivacy Screen Plugin Incompatibilityâ](#privacy-screen-plugin-incompatibility)
The Google Login plugin is incompatible with [@capacitor/privacy-screen](https://github.com/ionic-team/capacitor-privacy-screen). When using both plugins together, the Google login webview will be interrupted by the privacy screen.
**Workaround:** Call `await PrivacyScreen.disable();` before calling the login function:
```typescript
import { PrivacyScreen } from '@capacitor/privacy-screen';
import { SocialLogin } from '@capgo/capacitor-social-login';
await PrivacyScreen.disable();
await SocialLogin.login({
provider: 'google',
options: {}
});
```
## Keep going from Google Login on iOS
[Section titled âKeep going from Google Login on iOSâ](#keep-going-from-google-login-on-ios)
If you are using **Google Login on iOS** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Google Login on Web
> This guide provides a comprehensive walkthrough for setting up Google Login on web applications using Capacitor and the @capgo/capacitor-social-login plugin, ensuring a seamless integration process by covering all necessary steps and configurations.
## Introduction
[Section titled âIntroductionâ](#introduction)
In this guide, you will learn how to setup Google Login with Capgo Social Login for web applications. I assume that you have already read the [general setup guide](/docs/plugins/social-login/google/general/).
## Using Google login on the web
[Section titled âUsing Google login on the webâ](#using-google-login-on-the-web)
Using the google login on the web is rather simple. In order to use it, you have to do the following:
1. Create a web client in the Google Console
Note
If you have already configured Google Login for Android, you can skip this step as youâve already created a web client. You can proceed directly to step 2.
1. Click on the search bar

2. Search for `credentials` and click on the `APIs and Services` option (number 2 on the screenshot)

3. Click on the `create credentials`

4. Select `OAuth client ID`

5. Select the `Application type` as `Web application`

6. Name your client and click `Create`

7. Copy the client ID, youâll use this as the `webClientId` in your application

2. Configure the web client in the Google Console
1. Please open the [credentials page](https://console.cloud.google.com/apis/credentials) and click on your web client

2. Now, please add the `Authorized JavaScript origins`. This should include all the addresses that you might use for your app. In might case, I will **ONLY** use localhost, but since I use a custom port I have to add both `http://localhost` and `http://localhost:5173`
1. Please click on `add URI`

2. Please type your URL

3. Please repeat until you added all the URLs
4. When you finish, your screen should look something like this

3. Now, please add some `Authorized redirect URIs`. This will depend on what page do you depend to use the CapacitorSocialLogin plugin on. In my case, I am going to be using it on `http://localhost:5173/auth`
1. Please click on `ADD URI`

2. Enter your URL and click `ADD URL` again

4. Click `save`

3. Now, you should be ready to call `login` from JavaScript like so:
1. First, import `SocialLogin`
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
```
2. Then, call initialize. This should be called ONLY once.
```typescript
// onMounted is Vue specific
// webClientId is the client ID you got in the web client creation step not the android client ID.
onMounted(() => {
SocialLogin.initialize({
google: {
webClientId: '673324426943-avl4v9ubdas7a0u7igf7in03pdj1dkmg.apps.googleusercontent.com',
}
})
})
```
Web Redirect Handling
When using Google login on web, you **MUST** call any function from the plugin when the redirect happens to initialize the plugin so it can handle the redirect and close the popup window. You can call either `isLoggedIn()` OR `initialize()` - both will trigger the redirect handling:
```typescript
// Option 1: Call isLoggedIn when the redirect page loads
SocialLogin.isLoggedIn({ provider: 'google' }).catch(() => {
// Ignore the result, this is just to initialize the plugin
});
// Option 2: Call initialize when the redirect page loads
SocialLogin.initialize({
google: {
webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
}
}).catch(() => {
// Ignore any errors, this is just to handle the redirect
});
```
Caution
On Web, `SocialLogin.refresh({ provider: 'google' })` is not implemented, even when using `google.mode: 'online'`. If you need a fresh Google token on Web, call `SocialLogin.login({ provider: 'google', ... })` again.
3. Create a login button that calls `SocialLogin.login` when clicked
```typescript
const res = await SocialLogin.login({
provider: 'google',
options: {}
})
// Handle the response
console.log(JSON.stringify(res));
```
## Keep going from Google Login on Web
[Section titled âKeep going from Google Login on Webâ](#keep-going-from-google-login-on-web)
If you are using **Google Login on Web** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Integrations
> Backend and auth-platform integrations for @capgo/capacitor-social-login, including Better Auth, Firebase, Supabase, and OAuth providers such as Auth0, Okta, Cognito, and Keycloak.
## Overview
[Section titled âOverviewâ](#overview)
These guides show how to combine `@capgo/capacitor-social-login` with backend authentication platforms and session layers.
Use these when you want native mobile login on the device but still want a backend or auth service to own users, sessions, and token verification.
## OAuth provider integrations
[Section titled âOAuth provider integrationsâ](#oauth-provider-integrations)
[ Auth0](/docs/plugins/social-login/integrations/auth0/)
[Auth Connect preset and direct OAuth2 examples.](/docs/plugins/social-login/integrations/auth0/)
[ Microsoft Entra ID](/docs/plugins/social-login/integrations/azure/)
[Azure preset support and direct OAuth2 configuration.](/docs/plugins/social-login/integrations/azure/)
[ AWS Cognito](/docs/plugins/social-login/integrations/cognito/)
[Cognito preset and direct endpoint examples.](/docs/plugins/social-login/integrations/cognito/)
[ GitHub](/docs/plugins/social-login/integrations/github/)
[Generic OAuth2 configuration for GitHub login.](/docs/plugins/social-login/integrations/github/)
[ Keycloak](/docs/plugins/social-login/integrations/keycloak/)
[OIDC discovery and direct endpoint examples.](/docs/plugins/social-login/integrations/keycloak/)
[ Okta](/docs/plugins/social-login/integrations/okta/)
[Okta preset and direct OAuth2 examples.](/docs/plugins/social-login/integrations/okta/)
[ OneLogin](/docs/plugins/social-login/integrations/onelogin/)
[OneLogin preset and direct endpoint examples.](/docs/plugins/social-login/integrations/onelogin/)
## Backend and session integrations
[Section titled âBackend and session integrationsâ](#backend-and-session-integrations)
[ Better Auth](/docs/plugins/social-login/better-auth/)
[Native token handoff examples for Google, Apple, Facebook, and Generic OAuth guidance.](/docs/plugins/social-login/better-auth/)
[ Firebase](/docs/plugins/social-login/firebase/introduction/)
[Use native provider login with Firebase Authentication and Firestore.](/docs/plugins/social-login/firebase/introduction/)
[ Supabase](/docs/plugins/social-login/supabase/introduction/)
[Use native provider login with Supabase Auth and PostgreSQL.](/docs/plugins/social-login/supabase/introduction/)
## Keep going from Integrations
[Section titled âKeep going from Integrationsâ](#keep-going-from-integrations)
If you are using **Integrations** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Auth0
> Integrate Auth0 with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-social-login` supports Auth0 in two ways:
* `SocialLoginAuthConnect` with the `auth0` preset
* Direct `oauth2` configuration if you want full control over endpoints
## Auth Connect preset example
[Section titled âAuth Connect preset exampleâ](#auth-connect-preset-example)
```typescript
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
await SocialLoginAuthConnect.initialize({
authConnect: {
auth0: {
domain: 'https://your-tenant.auth0.com',
clientId: 'your-auth0-client-id',
redirectUrl: 'myapp://oauth/auth0',
audience: 'https://your-api.example.com',
},
},
});
const result = await SocialLoginAuthConnect.login({
provider: 'auth0',
});
```
## Direct OAuth2 example
[Section titled âDirect OAuth2 exampleâ](#direct-oauth2-example)
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
auth0: {
appId: 'your-auth0-client-id',
authorizationBaseUrl: 'https://your-tenant.auth0.com/authorize',
accessTokenEndpoint: 'https://your-tenant.auth0.com/oauth/token',
redirectUrl: 'myapp://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://your-api.example.com',
},
logoutUrl: 'https://your-tenant.auth0.com/v2/logout',
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'auth0',
},
});
```
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [Better Auth integration](/docs/plugins/social-login/better-auth/)
## Keep going from Auth0
[Section titled âKeep going from Auth0â](#keep-going-from-auth0)
If you are using **Auth0** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Microsoft Entra ID
> Integrate Microsoft Entra ID or Azure AD with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration.
## Overview
[Section titled âOverviewâ](#overview)
Microsoft Entra ID is supported through:
* The `azure` Auth Connect preset
* Direct `oauth2` configuration against Microsoft identity endpoints
## Auth Connect preset example
[Section titled âAuth Connect preset exampleâ](#auth-connect-preset-example)
```typescript
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
await SocialLoginAuthConnect.initialize({
authConnect: {
azure: {
tenantId: 'common',
clientId: 'your-azure-client-id',
redirectUrl: 'myapp://oauth/azure',
},
},
});
const result = await SocialLoginAuthConnect.login({
provider: 'azure',
});
```
## Direct OAuth2 example
[Section titled âDirect OAuth2 exampleâ](#direct-oauth2-example)
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
azure: {
appId: 'your-azure-client-id',
authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
redirectUrl: 'myapp://oauth/azure',
scope: 'openid profile email User.Read',
pkceEnabled: true,
resourceUrl: 'https://graph.microsoft.com/v1.0/me',
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'azure',
},
});
```
## Notes
[Section titled âNotesâ](#notes)
* Replace `common` with your tenant ID for single-tenant apps.
* `resourceUrl` can point to Microsoft Graph when you want profile data immediately after login.
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [Better Auth integration](/docs/plugins/social-login/better-auth/)
## Keep going from Microsoft Entra ID
[Section titled âKeep going from Microsoft Entra IDâ](#keep-going-from-microsoft-entra-id)
If you are using **Microsoft Entra ID** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# AWS Cognito
> Integrate Amazon Cognito with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration.
## Overview
[Section titled âOverviewâ](#overview)
Amazon Cognito is supported through the `cognito` Auth Connect preset. You can also configure it manually with direct OAuth2 endpoints.
## Auth Connect preset example
[Section titled âAuth Connect preset exampleâ](#auth-connect-preset-example)
```typescript
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
await SocialLoginAuthConnect.initialize({
authConnect: {
cognito: {
domain: 'https://your-domain.auth.region.amazoncognito.com',
clientId: 'your-cognito-client-id',
redirectUrl: 'myapp://oauth/cognito',
},
},
});
const result = await SocialLoginAuthConnect.login({
provider: 'cognito',
});
```
## Direct OAuth2 example
[Section titled âDirect OAuth2 exampleâ](#direct-oauth2-example)
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
cognito: {
appId: 'your-cognito-client-id',
authorizationBaseUrl: 'https://your-domain.auth.region.amazoncognito.com/oauth2/authorize',
accessTokenEndpoint: 'https://your-domain.auth.region.amazoncognito.com/oauth2/token',
redirectUrl: 'myapp://oauth/cognito',
scope: 'openid profile email',
pkceEnabled: true,
resourceUrl: 'https://your-domain.auth.region.amazoncognito.com/oauth2/userInfo',
logoutUrl: 'https://your-domain.auth.region.amazoncognito.com/logout',
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'cognito',
},
});
```
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
## Keep going from AWS Cognito
[Section titled âKeep going from AWS Cognitoâ](#keep-going-from-aws-cognito)
If you are using **AWS Cognito** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# GitHub
> Integrate GitHub OAuth with @capgo/capacitor-social-login using the built-in generic OAuth2 provider.
## Overview
[Section titled âOverviewâ](#overview)
GitHub is supported through the built-in generic `oauth2` provider.
## Example
[Section titled âExampleâ](#example)
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
github: {
appId: 'your-github-client-id',
authorizationBaseUrl: 'https://github.com/login/oauth/authorize',
accessTokenEndpoint: 'https://github.com/login/oauth/access_token',
redirectUrl: 'myapp://oauth/github',
scope: 'read:user user:email',
pkceEnabled: true,
resourceUrl: 'https://api.github.com/user',
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'github',
},
});
console.log(result.result.accessToken?.token);
console.log(result.result.resourceData);
```
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Better Auth integration](/docs/plugins/social-login/better-auth/)
## Keep going from GitHub
[Section titled âKeep going from GitHubâ](#keep-going-from-github)
If you are using **GitHub** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Keycloak
> Integrate Keycloak with @capgo/capacitor-social-login using OIDC discovery or direct OAuth2 endpoint configuration.
## Overview
[Section titled âOverviewâ](#overview)
Keycloak works through the built-in generic OAuth2 provider. You do not need a new native provider for Keycloak. Configure your Keycloak client as an OpenID Connect client and use `provider: 'oauth2'` with a stable `providerId`, such as `keycloak`.
## Keycloak client settings
[Section titled âKeycloak client settingsâ](#keycloak-client-settings)
In your Keycloak realm, create or open the client used by your Capacitor app:
1. Use the authorization code flow.
2. Keep PKCE enabled in the app config.
3. Register every redirect URI that your app uses, for example `myapp://oauth/keycloak` for mobile and an HTTPS callback URL for production web.
4. Use scopes such as `openid profile email`. Add `offline_access` only when your realm and client are configured to issue refresh tokens.
The exact issuer URL is deployment-specific. Current Keycloak deployments commonly publish realm metadata at `https://keycloak.example.com/realms/my-realm/.well-known/openid-configuration`, but some deployments include an extra base path. Use the issuer URL shown by your realmâs OpenID Endpoint Configuration.
## OIDC discovery example
[Section titled âOIDC discovery exampleâ](#oidc-discovery-example)
Prefer `issuerUrl` so the plugin can discover the authorization, token, and logout endpoints from the realm metadata:
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
const keycloakIssuer = 'https://keycloak.example.com/realms/my-realm';
await SocialLogin.initialize({
oauth2: {
keycloak: {
appId: 'your-keycloak-client-id',
issuerUrl: keycloakIssuer,
redirectUrl: 'myapp://oauth/keycloak',
scope: 'openid profile email',
pkceEnabled: true,
resourceUrl: `${keycloakIssuer}/protocol/openid-connect/userinfo`,
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'keycloak',
},
});
console.log(result.result.idToken);
console.log(result.result.accessToken?.token);
console.log(result.result.resourceData);
```
For refresh tokens, request `offline_access` only if your Keycloak realm and client allow refresh tokens for this app:
```typescript
await SocialLogin.initialize({
oauth2: {
keycloak: {
appId: 'your-keycloak-client-id',
issuerUrl: keycloakIssuer,
redirectUrl: 'myapp://oauth/keycloak',
scope: 'openid profile email offline_access',
pkceEnabled: true,
},
},
});
```
## Manual endpoint fallback
[Section titled âManual endpoint fallbackâ](#manual-endpoint-fallback)
If OIDC discovery is blocked or your deployment does not expose metadata to the app, configure the endpoints directly. Keep the base URL exactly as your Keycloak deployment publishes it:
```typescript
const keycloakRealmUrl = 'https://keycloak.example.com/realms/my-realm';
await SocialLogin.initialize({
oauth2: {
keycloak: {
appId: 'your-keycloak-client-id',
authorizationBaseUrl: `${keycloakRealmUrl}/protocol/openid-connect/auth`,
accessTokenEndpoint: `${keycloakRealmUrl}/protocol/openid-connect/token`,
redirectUrl: 'myapp://oauth/keycloak',
scope: 'openid profile email',
pkceEnabled: true,
resourceUrl: `${keycloakRealmUrl}/protocol/openid-connect/userinfo`,
logoutUrl: `${keycloakRealmUrl}/protocol/openid-connect/logout`,
},
},
});
```
## Auth Connect compatibility
[Section titled âAuth Connect compatibilityâ](#auth-connect-compatibility)
Keycloak is not one of the Auth Connect preset provider IDs. If you already use `SocialLoginAuthConnect`, you can still initialize the generic `oauth2` provider and log in with `provider: 'oauth2'` plus `providerId: 'keycloak'`.
See the [Keycloak OpenID Connect endpoint reference](https://www.keycloak.org/securing-apps/oidc-layers) for discovery and endpoint paths.
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Better Auth integration](/docs/plugins/social-login/better-auth/)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
## Keep going from Keycloak
[Section titled âKeep going from Keycloakâ](#keep-going-from-keycloak)
If you are using **Keycloak** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Okta
> Integrate Okta with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration.
## Overview
[Section titled âOverviewâ](#overview)
Okta is supported through:
* The `okta` Auth Connect preset
* Direct `oauth2` configuration against your Okta issuer
## Auth Connect preset example
[Section titled âAuth Connect preset exampleâ](#auth-connect-preset-example)
```typescript
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
await SocialLoginAuthConnect.initialize({
authConnect: {
okta: {
issuer: 'https://dev-12345.okta.com/oauth2/default',
clientId: 'your-okta-client-id',
redirectUrl: 'myapp://oauth/okta',
},
},
});
const result = await SocialLoginAuthConnect.login({
provider: 'okta',
});
```
## Direct OAuth2 example
[Section titled âDirect OAuth2 exampleâ](#direct-oauth2-example)
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
okta: {
appId: 'your-okta-client-id',
authorizationBaseUrl: 'https://your-domain.okta.com/oauth2/default/v1/authorize',
accessTokenEndpoint: 'https://your-domain.okta.com/oauth2/default/v1/token',
redirectUrl: 'myapp://oauth/okta',
scope: 'openid profile email offline_access',
pkceEnabled: true,
resourceUrl: 'https://your-domain.okta.com/oauth2/default/v1/userinfo',
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'okta',
},
});
```
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Better Auth integration](/docs/plugins/social-login/better-auth/)
## Keep going from Okta
[Section titled âKeep going from Oktaâ](#keep-going-from-okta)
If you are using **Okta** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# OneLogin
> Integrate OneLogin with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration.
## Overview
[Section titled âOverviewâ](#overview)
OneLogin is supported through the `onelogin` Auth Connect preset. You can also configure it manually using direct OAuth2 endpoints.
## Auth Connect preset example
[Section titled âAuth Connect preset exampleâ](#auth-connect-preset-example)
```typescript
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
await SocialLoginAuthConnect.initialize({
authConnect: {
onelogin: {
issuer: 'https://your-tenant.onelogin.com/oidc/2',
clientId: 'your-onelogin-client-id',
redirectUrl: 'myapp://oauth/onelogin',
},
},
});
const result = await SocialLoginAuthConnect.login({
provider: 'onelogin',
});
```
## Direct OAuth2 example
[Section titled âDirect OAuth2 exampleâ](#direct-oauth2-example)
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
onelogin: {
appId: 'your-onelogin-client-id',
authorizationBaseUrl: 'https://your-tenant.onelogin.com/oidc/2/auth',
accessTokenEndpoint: 'https://your-tenant.onelogin.com/oidc/2/token',
redirectUrl: 'myapp://oauth/onelogin',
scope: 'openid profile email',
pkceEnabled: true,
resourceUrl: 'https://your-tenant.onelogin.com/oidc/2/me',
logoutUrl: 'https://your-tenant.onelogin.com/oidc/2/logout',
},
},
});
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'onelogin',
},
});
```
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
## Keep going from OneLogin
[Section titled âKeep going from OneLoginâ](#keep-going-from-onelogin)
If you are using **OneLogin** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Apple Sign-In Migration to @capgo/social-login
> This comprehensive guide provides detailed instructions for transitioning from the @capacitor-community/apple-sign-in plugin to the @capgo/capacitor-social-login plugin, ensuring a smooth migration process and improved capabilities.
## Overview
[Section titled âOverviewâ](#overview)
This guide outlines the transition from the legacy `@capacitor-community/apple-sign-in` plugin to the modern `@capgo/capacitor-social-login` package. The new plugin provides a unified interface for multiple social authentication providers with improved TypeScript support and active maintenance.
## Installation
[Section titled âInstallationâ](#installation)
1. Remove the old package:
```bash
npm uninstall @capacitor-community/apple-sign-in
```
2. Install the new package:
```bash
npm install @capgo/capacitor-social-login
npx cap sync
```
## Code Changes
[Section titled âCode Changesâ](#code-changes)
### Import Changes
[Section titled âImport Changesâ](#import-changes)
```diff
import { SignInWithApple } from '@capacitor-community/apple-sign-in';
import { SocialLogin } from '@capgo/capacitor-social-login';
```
### Initialization
[Section titled âInitializationâ](#initialization)
**Key Change**: The new plugin requires an initialization step that wasnât needed before.
```diff
// No initialization needed in old package
// For iOS: Basic configuration
await SocialLogin.initialize({
apple: {} // Basic iOS configuration
});
// For Android: Additional configuration required
await SocialLogin.initialize({
apple: {
clientId: 'YOUR_SERVICE_ID', // Service ID from Apple Developer Portal
redirectUrl: 'https://your-backend.com/callback' // Your backend callback URL
}
});
```
**Important Note**: For iOS, you provide basic configuration, while Android requires additional details including a Service ID and backend callback URL for web-based OAuth authentication.
### Sign In
[Section titled âSign Inâ](#sign-in)
The login process simplifies from multiple parameters to a cleaner API:
```diff
const result = await SignInWithApple.authorize({
clientId: 'com.your.app',
redirectURI: 'https://your-app.com/callback',
scopes: 'email name',
state: '12345',
nonce: 'nonce'
});
const result = await SocialLogin.login({
provider: 'apple',
options: {
// Optional: Add scopes if needed
scopes: ['email', 'name'],
nonce: 'nonce'
}
});
```
The new plugin uses `login()` with `provider: 'apple'` and optional scopes rather than passing individual configuration values like `clientId` and `redirectURI`.
### Response Type Changes
[Section titled âResponse Type Changesâ](#response-type-changes)
Results now include an `accessToken` object with expiration details and a structured `profile` section, replacing the flatter response format of the original package:
```typescript
// Old response type
interface AppleSignInResponse {
response: {
user: string;
email: string | null;
givenName: string | null;
familyName: string | null;
identityToken: string | null;
authorizationCode: string | null;
};
}
// New response type
interface SocialLoginResponse {
provider: 'apple';
result: {
accessToken: {
token: string;
expiresIn?: number;
refreshToken?: string;
} | null;
idToken: string | null;
profile: {
user: string;
email: string | null;
givenName: string | null;
familyName: string | null;
};
};
}
```
### New Capabilities
[Section titled âNew Capabilitiesâ](#new-capabilities)
The updated plugin introduces functionality that wasnât available in the predecessor:
**Checking Login Status**
```diff
// Not available in old package
const status = await SocialLogin.isLoggedIn({
provider: 'apple'
});
```
**Logout Functionality**
```diff
// Not available in old package
await SocialLogin.logout({
provider: 'apple'
});
```
These methods provide `isLoggedIn()` to verify authentication status and `logout()` functionality.
## Platform Specific Changes
[Section titled âPlatform Specific Changesâ](#platform-specific-changes)
### iOS Setup
[Section titled âiOS Setupâ](#ios-setup)
**iOS** maintains familiar setup procedures through Xcode capabilities:
1. The iOS setup remains largely the same. You still need to:
* Enable âSign In with Appleâ capability in Xcode
* Configure your app in the Apple Developer Portal
* No additional code changes required for iOS
### Android Setup
[Section titled âAndroid Setupâ](#android-setup)
**Android** now receives native support via web-based OAuth authentication:
The new plugin provides Android support out of the box, but requires additional setup:
1. Create a Services ID in the Apple Developer Portal
2. Configure a web authentication endpoint
3. Set up your Android app to handle the OAuth flow
4. Backend service configuration is required
For detailed Android setup instructions, please refer to the [Android Setup Guide](/docs/plugins/social-login/apple/android/).
## Key Advantages
[Section titled âKey Advantagesâ](#key-advantages)
The modernized package provides:
1. **Unified APIs** across multiple social providers (Google, Facebook, Apple)
2. **Improved TypeScript typing** with better type definitions
3. **Active community maintenance** compared to the deprecated version
4. **Built-in Android support** through web-based authentication
5. **Persistent login state management**
6. **Better error handling** with consistent error types
## Breaking Changes
[Section titled âBreaking Changesâ](#breaking-changes)
1. **Explicit initialization is now required** - no default configuration
2. **Response object structure has changed** - nested result format
3. **Android implementation requires a backend service** for OAuth
4. **Token refresh handling is different** - improved token management
5. **Error handling and error types have changed** - more detailed errors
For more detailed setup instructions, please refer to the [official documentation](/docs/plugins/social-login/apple/general/).
## Keep going from Apple Sign-In Migration to @capgo/social-login
[Section titled âKeep going from Apple Sign-In Migration to @capgo/social-loginâ](#keep-going-from-apple-sign-in-migration-to-capgosocial-login)
If you are using **Apple Sign-In Migration to @capgo/social-login** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Facebook Login Migration to @capgo/social-login
> This detailed guide provides step-by-step instructions for transitioning from the @capacitor-community/facebook-login plugin to the @capgo/capacitor-social-login plugin, ensuring a smooth migration process with enhanced features.
## Overview
[Section titled âOverviewâ](#overview)
This guide provides comprehensive instructions for migrating from `@capacitor-community/facebook-login` to `@capgo/capacitor-social-login`. The new plugin modernizes Facebook authentication with a unified API that supports multiple social providers, improved TypeScript support, and enhanced capabilities.
## Installation
[Section titled âInstallationâ](#installation)
1. Remove the old package:
```bash
npm uninstall @capacitor-community/facebook-login
```
2. Install the new package:
```bash
npm install @capgo/capacitor-social-login
npx cap sync
```
## Code Changes
[Section titled âCode Changesâ](#code-changes)
### Import Changes
[Section titled âImport Changesâ](#import-changes)
```diff
import { FacebookLogin } from '@capacitor-community/facebook-login';
import { SocialLogin } from '@capgo/capacitor-social-login';
```
### Initialization
[Section titled âInitializationâ](#initialization)
**Key Change**: The new package requires explicit setup in your code:
```diff
// Old package required no explicit initialization in code
// Configuration was done only in native platforms
// New package requires explicit initialization
await SocialLogin.initialize({
facebook: {
appId: 'YOUR_FACEBOOK_APP_ID', // Required for web and Android
clientToken: 'YOUR_CLIENT_TOKEN' // Required for Android
}
});
```
### Login
[Section titled âLoginâ](#login)
The login method now accepts a provider parameter:
```diff
const FACEBOOK_PERMISSIONS = ['email', 'public_profile'];
const result = await FacebookLogin.login({ permissions: FACEBOOK_PERMISSIONS });
const result = await SocialLogin.login({
provider: 'facebook',
options: {
permissions: ['email', 'public_profile'],
limitedLogin: false,
nonce: 'optional_nonce'
}
});
```
### Response Type Changes
[Section titled âResponse Type Changesâ](#response-type-changes)
The response structure has been modernized with a more comprehensive profile object:
```typescript
// Old response type
interface FacebookLoginResponse {
accessToken: {
applicationId: string;
userId: string;
token: string;
expires: string;
};
recentlyGrantedPermissions: string[];
recentlyDeniedPermissions: string[];
}
// New response type
interface FacebookLoginResponse {
provider: 'facebook';
result: {
accessToken: {
token: string;
applicationId?: string;
expires?: string;
userId?: string;
permissions?: string[];
declinedPermissions?: string[];
} | null;
idToken: string | null;
profile: {
userID: string;
email: string | null;
friendIDs: string[];
birthday: string | null;
ageRange: { min?: number; max?: number } | null;
gender: string | null;
location: { id: string; name: string } | null;
hometown: { id: string; name: string } | null;
profileURL: string | null;
name: string | null;
imageURL: string | null;
};
};
}
```
**Key Differences**:
* Response now includes a `provider` field identifying the authentication provider
* More detailed `profile` object with additional user information
* Consistent structure across all social login providers
### Checking Login Status
[Section titled âChecking Login Statusâ](#checking-login-status)
```diff
const result = await FacebookLogin.getCurrentAccessToken();
const isLoggedIn = result && result.accessToken;
const status = await SocialLogin.isLoggedIn({
provider: 'facebook'
});
const isLoggedIn = status.isLoggedIn;
```
### Logout
[Section titled âLogoutâ](#logout)
```diff
await FacebookLogin.logout();
await SocialLogin.logout({
provider: 'facebook'
});
```
## Platform Specific Changes
[Section titled âPlatform Specific Changesâ](#platform-specific-changes)
### Android Setup
[Section titled âAndroid Setupâ](#android-setup)
Configuration is now handled through the initialize method:
```diff
// AndroidManifest.xml changes remain the same
// strings.xml become irrelevant
// Additionally initialize in your code:
await SocialLogin.initialize({
facebook: {
appId: 'your-app-id',
clientToken: 'your-client-token' // New requirement
}
});
```
**Important**: Client token is now required for Android authentication.
### iOS Setup
[Section titled âiOS Setupâ](#ios-setup)
1. The iOS setup in `AppDelegate.swift` remains the same:
```swift
import FBSDKCoreKit
// In application:didFinishLaunchingWithOptions:
FBSDKCoreKit.ApplicationDelegate.shared.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
// In application:openURL:options:
ApplicationDelegate.shared.application(
app,
open: url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation]
)
```
2. The `Info.plist` configuration remains the same:
```xml
CFBundleURLTypes
CFBundleURLSchemes
fb[APP_ID]
FacebookAppID
[APP_ID]
FacebookClientToken
[CLIENT_TOKEN]
FacebookDisplayName
[APP_NAME]
LSApplicationQueriesSchemes
fbapi
fbauth
fb-messenger-share-api
fbauth2
fbshareextension
```
## Breaking Changes
[Section titled âBreaking Changesâ](#breaking-changes)
Summary of breaking changes when migrating:
1. **Explicit initialization is now required** - Must call `initialize()` before use
2. **Response object structure has changed significantly** - New nested result format with enhanced profile data
3. **Client token is now required for Android** - Additional configuration needed
4. **Different method names and parameter structures** - Provider-based approach
5. **Error handling and error types have changed** - More detailed error information
## Key Advantages
[Section titled âKey Advantagesâ](#key-advantages)
The new plugin provides:
* **Unified API** across multiple social providers (Google, Apple, Facebook)
* **Improved TypeScript support** with better type definitions
* **Enhanced profile data** with more user information
* **Active maintenance** and community support
* **Consistent error handling** across all providers
* **Better token management** with proper expiration handling
For more detailed setup instructions, please refer to the [official documentation](/docs/plugins/social-login/facebook/).
## Keep going from Facebook Login Migration to @capgo/social-login
[Section titled âKeep going from Facebook Login Migration to @capgo/social-loginâ](#keep-going-from-facebook-login-migration-to-capgosocial-login)
If you are using **Facebook Login Migration to @capgo/social-login** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Google Auth Migration to @capgo/social-login
> This guide outlines transitioning from the older Google Auth plugin to the newer Capgo social login package, which unifies multiple social authentication providers.
## Overview
[Section titled âOverviewâ](#overview)
This guide provides comprehensive steps for migrating from `@codetrix-studio/capacitor-google-auth` to `@capgo/capacitor-social-login`, ensuring a smooth transition and improved authentication experience. The new plugin unifies multiple social authentication providers under a single, consistent API.
## Installation
[Section titled âInstallationâ](#installation)
1. Remove the old package:
```bash
npm uninstall @codetrix-studio/capacitor-google-auth
```
2. Install the new package:
```bash
npm install @capgo/capacitor-social-login
npx cap sync
```
## Important Changes in Google Auth Setup
[Section titled âImportant Changes in Google Auth Setupâ](#important-changes-in-google-auth-setup)
### Web Client ID Requirement
[Section titled âWeb Client ID Requirementâ](#web-client-id-requirement)
**Critical Change**: The updated plugin requires using a Web Client ID across all platforms.
Youâll need to:
1. Create a Web Client ID in Google Cloud Console if you donât have one ([How to get the credentials](/docs/plugins/social-login/google/general/))
2. Use this Web Client ID in the `webClientId` field for all platforms
3. For Android, you still need to create an Android Client ID with your SHA1, but this is only for verification purposes - the token wonât be used ([Android setup guide](/docs/plugins/social-login/google/android/))
## Code Changes
[Section titled âCode Changesâ](#code-changes)
### Import Changes
[Section titled âImport Changesâ](#import-changes)
```diff
import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth';
import { SocialLogin } from '@capgo/capacitor-social-login';
```
### Initialization
[Section titled âInitializationâ](#initialization)
The setup transforms from a simple `GoogleAuth.initialize()` call to a more structured `SocialLogin.initialize()` with nested Google configuration:
```diff
GoogleAuth.initialize({
clientId: 'CLIENT_ID.apps.googleusercontent.com',
scopes: ['profile', 'email'],
grantOfflineAccess: true,
});
await SocialLogin.initialize({
google: {
webClientId: 'WEB_CLIENT_ID.apps.googleusercontent.com', // Use Web Client ID for all platforms
iOSClientId: 'IOS_CLIENT_ID', // for iOS
mode: 'offline' // replaces grantOfflineAccess
}
});
```
### Sign In
[Section titled âSign Inâ](#sign-in)
The login method changes from `GoogleAuth.signIn()` to `SocialLogin.login()` with explicit provider specification:
```diff
const user = await GoogleAuth.signIn();
const res = await SocialLogin.login({
provider: 'google',
options: {
scopes: ['email', 'profile'],
forceRefreshToken: true // if you need refresh token
}
});
```
## Platform Specific Changes
[Section titled âPlatform Specific Changesâ](#platform-specific-changes)
### Android
[Section titled âAndroidâ](#android)
1. Update your `MainActivity.java` ([Full Android setup guide](/docs/plugins/social-login/google/android/)):
```diff
import ee.forgr.capacitor.social.login.GoogleProvider;
import ee.forgr.capacitor.social.login.SocialLoginPlugin;
import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin;
import com.getcapacitor.PluginHandle;
import com.getcapacitor.Plugin;
import android.content.Intent;
import android.util.Log;
public class MainActivity extends BridgeActivity {
public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) {
PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin");
if (pluginHandle == null) {
Log.i("Google Activity Result", "SocialLogin login handle is null");
return;
}
Plugin plugin = pluginHandle.getInstance();
if (!(plugin instanceof SocialLoginPlugin)) {
Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin");
return;
}
((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data);
}
}
public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {}
}
```
### iOS
[Section titled âiOSâ](#ios)
1. No major changes needed in AppDelegate.swift ([iOS setup guide](/docs/plugins/social-login/google/ios/))
2. Update your configuration in `capacitor.config.json`, we donât use it in the new plugin:
```diff
{
"plugins": {
"GoogleAuth": {
"scopes": ["profile", "email"],
"serverClientId": "xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"forceCodeForRefreshToken": true
}
}
```
### Web
[Section titled âWebâ](#web)
1. Remove the Google Sign-In meta tags from your `index.html` if you were using them:
```diff
```
## Response Type Changes
[Section titled âResponse Type Changesâ](#response-type-changes)
The authentication response now provides a structured object containing provider information, access tokens, ID tokens, and user profile data:
```typescript
interface GoogleLoginResponse {
provider: 'google';
result: {
accessToken: {
token: string;
expires: string;
// ... other token fields
} | null;
idToken: string | null;
profile: {
email: string | null;
familyName: string | null;
givenName: string | null;
id: string | null;
name: string | null;
imageUrl: string | null;
};
};
}
```
The response structure includes:
* **provider**: Identifies the authentication provider (`'google'`)
* **result.accessToken**: Access token details with expiration
* **result.idToken**: ID token for authentication
* **result.profile**: User profile information including email, name, and image URL
## Additional Capabilities
[Section titled âAdditional Capabilitiesâ](#additional-capabilities)
The new package supports multiple social authentication providers beyond Google:
* [Apple Sign-In](/docs/plugins/social-login/apple/general/)
* [Facebook Login](/docs/plugins/social-login/facebook/)
This unified approach provides:
* Consistent API across all providers
* Improved TypeScript support
* Better error handling
* Active maintenance and community support
Check the [main documentation](/docs/plugins/social-login/google/general/) for detailed setup instructions for these additional providers.
## Keep going from Google Auth Migration to @capgo/social-login
[Section titled âKeep going from Google Auth Migration to @capgo/social-loginâ](#keep-going-from-google-auth-migration-to-capgosocial-login)
If you are using **Google Auth Migration to @capgo/social-login** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Ionic Auth Connect Migration to @capgo/capacitor-social-login
> Migrate from Ionic Auth Connect to Capgo Social Login with OAuth2 and provider-native sign-in.
## Overview
[Section titled âOverviewâ](#overview)
Capgo Social Login replaces Ionic Auth Connect with a provider-native OAuth2 flow for Google, Apple, Facebook, and other identity providers. It supports multiple providers in one plugin and works across iOS, Android, and Web.
## Why this works
[Section titled âWhy this worksâ](#why-this-works)
The plugin includes an Auth Connect compatibility wrapper named `SocialLoginAuthConnect`. It maps familiar Ionic Auth Connect provider IDs onto the built-in OAuth2 engine, so you can keep using names such as `auth0`, `azure`, and `okta`.
## Install
[Section titled âInstallâ](#install)
```bash
npm install @capgo/capacitor-social-login
npx cap sync
```
## Replace your imports
[Section titled âReplace your importsâ](#replace-your-imports)
```typescript
// Before
import { AuthConnect } from '@ionic-enterprise/auth-connect';
// After
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
```
## Initialize providers
[Section titled âInitialize providersâ](#initialize-providers)
Use the `authConnect` presets when you want the same provider IDs that Ionic Auth Connect used:
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
auth0: {
domain: 'https://your-tenant.auth0.com',
clientId: 'your-auth0-client-id',
redirectUrl: 'myapp://oauth/auth0',
audience: 'https://your-api.example.com',
},
azure: {
tenantId: 'common',
clientId: 'your-azure-client-id',
redirectUrl: 'myapp://oauth/azure',
},
cognito: {
domain: 'https://your-domain.auth.region.amazoncognito.com',
clientId: 'your-cognito-client-id',
redirectUrl: 'myapp://oauth/cognito',
},
okta: {
issuer: 'https://dev-12345.okta.com/oauth2/default',
clientId: 'your-okta-client-id',
redirectUrl: 'myapp://oauth/okta',
},
onelogin: {
issuer: 'https://your-tenant.onelogin.com/oidc/2',
clientId: 'your-onelogin-client-id',
redirectUrl: 'myapp://oauth/onelogin',
},
},
});
```
## Supported provider IDs
[Section titled âSupported provider IDsâ](#supported-provider-ids)
* `auth0`
* `azure`
* `cognito`
* `okta`
* `onelogin`
## Login, logout, and token access
[Section titled âLogin, logout, and token accessâ](#login-logout-and-token-access)
```typescript
const result = await SocialLoginAuthConnect.login({
provider: 'auth0',
});
const status = await SocialLoginAuthConnect.isLoggedIn({
provider: 'auth0',
});
const code = await SocialLoginAuthConnect.getAuthorizationCode({
provider: 'auth0',
});
await SocialLoginAuthConnect.logout({
provider: 'auth0',
});
```
## Provider-specific preset examples
[Section titled âProvider-specific preset examplesâ](#provider-specific-preset-examples)
### Auth0 preset example
[Section titled âAuth0 preset exampleâ](#auth0-preset-example)
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
auth0: {
domain: 'https://your-tenant.auth0.com',
clientId: 'your-auth0-client-id',
redirectUrl: 'myapp://oauth/auth0',
audience: 'https://your-api.example.com',
},
},
});
const auth0Result = await SocialLoginAuthConnect.login({
provider: 'auth0',
});
console.log(auth0Result.result.idToken);
```
### Azure preset example
[Section titled âAzure preset exampleâ](#azure-preset-example)
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
azure: {
tenantId: 'common',
clientId: 'your-azure-client-id',
redirectUrl: 'myapp://oauth/azure',
},
},
});
const azureResult = await SocialLoginAuthConnect.login({
provider: 'azure',
});
console.log(azureResult.result.resourceData);
```
### Cognito preset example
[Section titled âCognito preset exampleâ](#cognito-preset-example)
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
cognito: {
domain: 'https://your-domain.auth.region.amazoncognito.com',
clientId: 'your-cognito-client-id',
redirectUrl: 'myapp://oauth/cognito',
},
},
});
const cognitoResult = await SocialLoginAuthConnect.login({
provider: 'cognito',
});
console.log(cognitoResult.result.idToken);
```
### Okta preset example
[Section titled âOkta preset exampleâ](#okta-preset-example)
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
okta: {
issuer: 'https://dev-12345.okta.com/oauth2/default',
clientId: 'your-okta-client-id',
redirectUrl: 'myapp://oauth/okta',
},
},
});
const oktaResult = await SocialLoginAuthConnect.login({
provider: 'okta',
});
console.log(oktaResult.result.resourceData);
```
### OneLogin preset example
[Section titled âOneLogin preset exampleâ](#onelogin-preset-example)
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
onelogin: {
issuer: 'https://your-tenant.onelogin.com/oidc/2',
clientId: 'your-onelogin-client-id',
redirectUrl: 'myapp://oauth/onelogin',
},
},
});
const oneloginResult = await SocialLoginAuthConnect.login({
provider: 'onelogin',
});
console.log(oneloginResult.result.idToken);
```
## Overriding endpoints
[Section titled âOverriding endpointsâ](#overriding-endpoints)
Each preset creates a default OAuth2 configuration from `domain` or `issuer`. If your tenant uses custom endpoints, override them directly:
```typescript
await SocialLoginAuthConnect.initialize({
authConnect: {
onelogin: {
issuer: 'https://your-tenant.onelogin.com/oidc/2',
clientId: 'your-onelogin-client-id',
redirectUrl: 'myapp://oauth/onelogin',
authorizationBaseUrl: 'https://your-tenant.onelogin.com/oidc/2/auth',
accessTokenEndpoint: 'https://your-tenant.onelogin.com/oidc/2/token',
resourceUrl: 'https://your-tenant.onelogin.com/oidc/2/me',
logoutUrl: 'https://your-tenant.onelogin.com/oidc/2/logout',
},
},
});
```
## Direct OAuth2 configuration
[Section titled âDirect OAuth2 configurationâ](#direct-oauth2-configuration)
If you do not want presets, configure the same providers directly in the generic OAuth2 docs:
* [OAuth2 and OIDC provider guide](/docs/plugins/social-login/oauth2/)
## Migration notes
[Section titled âMigration notesâ](#migration-notes)
1. **The compatibility layer is OAuth2-based** It keeps the provider names, not Ionicâs native implementation details.
2. **Refresh tokens still depend on scopes** Request `offline_access` or the provider-specific equivalent when you need refresh tokens.
3. **Custom endpoints can override presets** If the preset is close but not exact, override only the endpoints that differ.
4. **Direct `oauth2` entries win** If you define both `authConnect.auth0` and `oauth2.auth0`, the direct `oauth2` config takes precedence.
## Related Documentation
[Section titled âRelated Documentationâ](#related-documentation)
* [Social Login getting started](/docs/plugins/social-login/getting-started/)
* [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/)
* [Migrate from Ionic Auth Connect](/docs/upgrade/from-ionic-auth-connect/)
* [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/)
## Keep going from Ionic Auth Connect Migration to @capgo/capacitor-social-login
[Section titled âKeep going from Ionic Auth Connect Migration to @capgo/capacitor-social-loginâ](#keep-going-from-ionic-auth-connect-migration-to-capgocapacitor-social-login)
If you are using **Ionic Auth Connect Migration to @capgo/capacitor-social-login** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# V7 Migration Guide
> Guide for migrating from earlier versions to V7 of @capgo/capacitor-social-login
## Introduction
[Section titled âIntroductionâ](#introduction)
Caution
This guide is for the `V1`/`V7` version of the plugin for people who were still using this plugin with the `0.x.x` version.
This guide will cover the following:
* Migrating to the V1 version from the `main` version
* Migrating to the V1 version from the `development` version
## Important changes in V1
[Section titled âImportant changes in V1â](#important-changes-in-v1)
V1 is just a port of the development version into main. It does, however, include a lot of important changes that are not available in the `main` V0 version. Those changes include:
* Access scopes for Google login
* Offline mode for Google login
* Unification of the different implementations
* Extensive testing was conducted to ensure that all implementations of the Google Provider behave in the same way between platforms
## Migration from the V0 main version
[Section titled âMigration from the V0 main versionâ](#migration-from-the-v0-main-version)
* Changes in the `MainActivity.java` for Android
* Please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `MainActivity.java`
* Please add redirect urls in the Google Console. Without adding redirect urls, Google login will not work.
* Again, please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `Authorized redirect URIs`
* Please ensure that you are not using `grantOfflineAccess` in the config. This feature is not supported in V1.
* Please ensure that authentication works on all the platforms.
## Migration from the V0 development version
[Section titled âMigration from the V0 development versionâ](#migration-from-the-v0-development-version)
* Changes in the `MainActivity.java` for Android
* Please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `MainActivity.java`. In V1, you **HAVE TO** implement `ModifiedMainActivityForSocialLoginPlugin` in your main activity. This change is crucial for the plugin to work
* Please add redirect urls in the Google Console. Without adding redirect urls, Google login will not work.
* Again, please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `Authorized redirect URIs`
* Please ensure that types and variable names are correct. Please know that types and variables might not match between development and V1.
* Please ensure that authentication works on all the platforms.
## Keep going from V7 Migration Guide
[Section titled âKeep going from V7 Migration Guideâ](#keep-going-from-v7-migration-guide)
If you are using **V7 Migration Guide** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Generic OAuth2 Providers
> Configure GitHub, Azure AD, Auth0, Okta, Keycloak, and other OAuth2 or OIDC providers with the Capgo Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
The Capgo Social Login plugin includes a built-in OAuth2 and OpenID Connect engine. You can use it to connect any standards-based identity provider, including:
* GitHub
* Azure AD / Microsoft Entra ID
* Auth0
* Okta
* Keycloak
* Custom OAuth2 or OIDC servers
The `oauth2` configuration is multi-provider by design. You can register several providers at once and then select one at login time with `providerId`.
## What you need
[Section titled âWhat you needâ](#what-you-need)
Before you configure a provider, collect:
* Your OAuth client ID
* A redirect URL that matches your app scheme or web callback URL
* An authorization endpoint
* A token endpoint for authorization code flow, or an `issuerUrl` for OIDC discovery
* The scopes your app needs, such as `openid profile email`
## Multi-provider configuration
[Section titled âMulti-provider configurationâ](#multi-provider-configuration)
Use `SocialLogin.initialize()` once during app startup and register every provider you need:
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.initialize({
oauth2: {
github: {
appId: 'your-github-client-id',
authorizationBaseUrl: 'https://github.com/login/oauth/authorize',
accessTokenEndpoint: 'https://github.com/login/oauth/access_token',
redirectUrl: 'myapp://oauth/github',
scope: 'read:user user:email',
pkceEnabled: true,
resourceUrl: 'https://api.github.com/user',
},
azure: {
appId: 'your-azure-client-id',
authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
redirectUrl: 'myapp://oauth/azure',
scope: 'openid profile email User.Read',
pkceEnabled: true,
resourceUrl: 'https://graph.microsoft.com/v1.0/me',
},
auth0: {
issuerUrl: 'https://your-tenant.auth0.com',
appId: 'your-auth0-client-id',
redirectUrl: 'myapp://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://your-api.example.com',
},
},
},
});
```
## OIDC discovery and aliases
[Section titled âOIDC discovery and aliasesâ](#oidc-discovery-and-aliases)
If your provider exposes an OpenID Connect discovery document, `issuerUrl` is the simplest setup:
```typescript
await SocialLogin.initialize({
oauth2: {
keycloak: {
issuerUrl: 'https://sso.example.com/realms/mobile',
clientId: 'mobile-app',
redirectUrl: 'myapp://oauth/keycloak',
scope: 'openid profile email offline_access',
pkceEnabled: true,
},
},
});
```
The plugin also supports common OAuth and OIDC aliases:
* `clientId` as an alias of `appId`
* `authorizationEndpoint` as an alias of `authorizationBaseUrl`
* `tokenEndpoint` as an alias of `accessTokenEndpoint`
* `endSessionEndpoint` as an alias of `logoutUrl`
* `scopes` as an alias of `scope`
Also available:
* `additionalParameters` for auth request overrides
* `additionalTokenParameters` for token exchange overrides
* `additionalResourceHeaders` for custom resource endpoint headers
* `additionalLogoutParameters` and `postLogoutRedirectUrl` for logout flows
* `loginHint`, `prompt`, and `iosPrefersEphemeralSession`
## Auth Connect-compatible presets
[Section titled âAuth Connect-compatible presetsâ](#auth-connect-compatible-presets)
If you are migrating from Ionic Auth Connect and want to keep the same provider names, use `SocialLoginAuthConnect`.
```typescript
import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login';
await SocialLoginAuthConnect.initialize({
authConnect: {
auth0: {
domain: 'https://your-tenant.auth0.com',
clientId: 'your-auth0-client-id',
redirectUrl: 'myapp://oauth/auth0',
audience: 'https://your-api.example.com',
},
azure: {
tenantId: 'common',
clientId: 'your-azure-client-id',
redirectUrl: 'myapp://oauth/azure',
},
okta: {
issuer: 'https://dev-12345.okta.com/oauth2/default',
clientId: 'your-okta-client-id',
redirectUrl: 'myapp://oauth/okta',
},
},
});
```
Supported preset provider IDs:
* `auth0`
* `azure`
* `cognito`
* `okta`
* `onelogin`
If a provider needs custom endpoints, either override them in the preset or bypass presets and configure the provider directly in `oauth2`.
## Configuration options
[Section titled âConfiguration optionsâ](#configuration-options)
| Option | Type | Required | Description |
| ------------------------------------------------ | ------------------------ | -------- | ---------------------------------------------- |
| `appId` / `clientId` | string | Yes | OAuth2 client identifier |
| `issuerUrl` | string | No | OIDC discovery base URL |
| `authorizationBaseUrl` / `authorizationEndpoint` | string | Yes\* | Authorization endpoint URL |
| `accessTokenEndpoint` / `tokenEndpoint` | string | No\* | Token endpoint URL |
| `redirectUrl` | string | Yes | Callback URL |
| `scope` / `scopes` | string / string\[] | No | Requested scopes |
| `pkceEnabled` | boolean | No | Defaults to `true` |
| `responseType` | `'code'` or `'token'` | No | Defaults to `'code'` |
| `resourceUrl` | string | No | User info or resource endpoint |
| `logoutUrl` / `endSessionEndpoint` | string | No | Logout or end-session URL |
| `postLogoutRedirectUrl` | string | No | Redirect URL after logout |
| `additionalParameters` | `Record` | No | Extra auth request params |
| `additionalTokenParameters` | `Record` | No | Extra token request params |
| `additionalResourceHeaders` | `Record` | No | Extra headers for `resourceUrl` |
| `additionalLogoutParameters` | `Record` | No | Extra logout params |
| `loginHint` | string | No | Shortcut for `additionalParameters.login_hint` |
| `prompt` | string | No | Shortcut for `additionalParameters.prompt` |
| `iosPrefersEphemeralSession` | boolean | No | Prefer ephemeral browser session on iOS |
| `logsEnabled` | boolean | No | Enable verbose debug logging |
`authorizationBaseUrl` and `accessTokenEndpoint` are only optional when `issuerUrl` is enough for discovery. Explicit endpoints always win over discovered values.
## Using OAuth2 login
[Section titled âUsing OAuth2 loginâ](#using-oauth2-login)
### Login
[Section titled âLoginâ](#login)
```typescript
const result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'github',
scope: 'read:user user:email',
loginHint: 'user@example.com',
},
});
```
### Redirect flow on web
[Section titled âRedirect flow on webâ](#redirect-flow-on-web)
Use `flow: 'redirect'` if you want a full-page redirect instead of a popup:
```typescript
await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'auth0',
flow: 'redirect',
},
});
```
On the page that receives the callback, parse the login result:
```typescript
const result = await SocialLogin.handleRedirectCallback();
if (result?.provider === 'oauth2') {
console.log(result.result.providerId);
}
```
### Login status and logout
[Section titled âLogin status and logoutâ](#login-status-and-logout)
```typescript
const status = await SocialLogin.isLoggedIn({
provider: 'oauth2',
providerId: 'github',
});
await SocialLogin.logout({
provider: 'oauth2',
providerId: 'github',
});
```
### Refresh tokens
[Section titled âRefresh tokensâ](#refresh-tokens)
```typescript
await SocialLogin.refresh({
provider: 'oauth2',
options: {
providerId: 'github',
},
});
const refreshed = await SocialLogin.refreshToken({
provider: 'oauth2',
providerId: 'github',
refreshToken: 'existing-refresh-token',
});
```
`refresh()` uses the refresh token stored by the plugin. `refreshToken()` lets you pass a refresh token yourself and returns the fresh OAuth2 response.
### Get the current access token
[Section titled âGet the current access tokenâ](#get-the-current-access-token)
```typescript
const code = await SocialLogin.getAuthorizationCode({
provider: 'oauth2',
providerId: 'github',
});
console.log(code.accessToken);
```
## Provider-specific examples
[Section titled âProvider-specific examplesâ](#provider-specific-examples)
### GitHub example
[Section titled âGitHub exampleâ](#github-example)
Use GitHub when you want a simple OAuth app flow and basic profile data:
```typescript
await SocialLogin.initialize({
oauth2: {
github: {
appId: 'your-github-client-id',
authorizationBaseUrl: 'https://github.com/login/oauth/authorize',
accessTokenEndpoint: 'https://github.com/login/oauth/access_token',
redirectUrl: 'myapp://oauth/github',
scope: 'read:user user:email',
pkceEnabled: true,
resourceUrl: 'https://api.github.com/user',
},
},
});
const githubResult = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'github',
},
});
console.log(githubResult.result.accessToken?.token);
console.log(githubResult.result.resourceData);
```
### Azure AD / Microsoft Entra ID example
[Section titled âAzure AD / Microsoft Entra ID exampleâ](#azure-ad--microsoft-entra-id-example)
Use Azure when you need Microsoft Graph data such as the user profile:
```typescript
await SocialLogin.initialize({
oauth2: {
azure: {
appId: 'your-azure-client-id',
authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
redirectUrl: 'myapp://oauth/azure',
scope: 'openid profile email User.Read',
pkceEnabled: true,
resourceUrl: 'https://graph.microsoft.com/v1.0/me',
},
},
});
const azureResult = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'azure',
},
});
console.log(azureResult.result.idToken);
console.log(azureResult.result.resourceData);
```
### Auth0 example
[Section titled âAuth0 exampleâ](#auth0-example)
Auth0 is a good fit when you need OIDC plus a custom API audience:
```typescript
await SocialLogin.initialize({
oauth2: {
auth0: {
appId: 'your-auth0-client-id',
authorizationBaseUrl: 'https://your-tenant.auth0.com/authorize',
accessTokenEndpoint: 'https://your-tenant.auth0.com/oauth/token',
redirectUrl: 'myapp://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://your-api.example.com',
},
},
},
});
const auth0Result = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'auth0',
flow: 'redirect',
},
});
```
If you use redirect flow on web, read the result back on the callback page:
```typescript
const auth0Result = await SocialLogin.handleRedirectCallback();
if (auth0Result?.provider === 'oauth2') {
console.log(auth0Result.result.idToken);
}
```
### Okta example
[Section titled âOkta exampleâ](#okta-example)
```typescript
await SocialLogin.initialize({
oauth2: {
okta: {
appId: 'your-okta-client-id',
authorizationBaseUrl: 'https://your-domain.okta.com/oauth2/default/v1/authorize',
accessTokenEndpoint: 'https://your-domain.okta.com/oauth2/default/v1/token',
redirectUrl: 'myapp://oauth/okta',
scope: 'openid profile email offline_access',
pkceEnabled: true,
resourceUrl: 'https://your-domain.okta.com/oauth2/default/v1/userinfo',
},
},
});
const oktaResult = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'okta',
},
});
console.log(oktaResult.result.resourceData);
```
### Keycloak example
[Section titled âKeycloak exampleâ](#keycloak-example)
Use discovery when your provider publishes `/.well-known/openid-configuration`:
```typescript
await SocialLogin.initialize({
oauth2: {
keycloak: {
issuerUrl: 'https://sso.example.com/realms/mobile',
clientId: 'mobile-app',
redirectUrl: 'myapp://oauth/keycloak',
scope: 'openid profile email offline_access',
pkceEnabled: true,
},
},
});
const keycloakResult = await SocialLogin.login({
provider: 'oauth2',
options: {
providerId: 'keycloak',
},
});
console.log(keycloakResult.result.idToken);
```
## OAuth2 response shape
[Section titled âOAuth2 response shapeâ](#oauth2-response-shape)
Successful OAuth2 logins return:
| Field | Description |
| -------------- | ------------------------------------------------ |
| `providerId` | The configured provider key used for the login |
| `accessToken` | Access token payload or `null` |
| `idToken` | OIDC ID token if the provider returned one |
| `refreshToken` | Refresh token if the requested scopes allowed it |
| `resourceData` | Raw JSON fetched from `resourceUrl` |
| `scope` | Granted scopes |
| `tokenType` | Usually `bearer` |
| `expiresIn` | Token lifetime in seconds |
## Provider setup reference
[Section titled âProvider setup referenceâ](#provider-setup-reference)
### GitHub
[Section titled âGitHubâ](#github)
1. **Create an OAuth app** Open [GitHub Developer Settings](https://github.com/settings/developers) and create a new OAuth App.
2. **Set the callback URL** Use your app redirect URL, for example `myapp://oauth/github`.
3. **Configure the plugin**
```typescript
await SocialLogin.initialize({
oauth2: {
github: {
appId: 'your-github-client-id',
authorizationBaseUrl: 'https://github.com/login/oauth/authorize',
accessTokenEndpoint: 'https://github.com/login/oauth/access_token',
redirectUrl: 'myapp://oauth/github',
scope: 'read:user user:email',
pkceEnabled: true,
resourceUrl: 'https://api.github.com/user',
},
},
});
```
### Azure AD / Microsoft Entra ID
[Section titled âAzure AD / Microsoft Entra IDâ](#azure-ad--microsoft-entra-id)
1. **Register an app** Go to Azure Portal, open `App registrations`, and create a native or mobile app registration.
2. **Add the redirect URI** Add a mobile or desktop redirect URI that matches your app callback URL.
3. **Configure the plugin**
```typescript
await SocialLogin.initialize({
oauth2: {
azure: {
appId: 'your-azure-client-id',
authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
redirectUrl: 'myapp://oauth/azure',
scope: 'openid profile email User.Read',
pkceEnabled: true,
resourceUrl: 'https://graph.microsoft.com/v1.0/me',
},
},
});
```
Note
Replace `common` with your tenant ID if your app is single-tenant.
### Auth0
[Section titled âAuth0â](#auth0)
1. **Create a native application** Open the [Auth0 Dashboard](https://manage.auth0.com) and create a Native app.
2. **Set allowed callback URLs** Add the exact redirect URL used by your Capacitor app.
3. **Configure the plugin**
```typescript
await SocialLogin.initialize({
oauth2: {
auth0: {
appId: 'your-auth0-client-id',
authorizationBaseUrl: 'https://your-tenant.auth0.com/authorize',
accessTokenEndpoint: 'https://your-tenant.auth0.com/oauth/token',
redirectUrl: 'myapp://oauth/auth0',
scope: 'openid profile email offline_access',
pkceEnabled: true,
additionalParameters: {
audience: 'https://your-api.example.com',
},
logoutUrl: 'https://your-tenant.auth0.com/v2/logout',
},
},
});
```
### Okta
[Section titled âOktaâ](#okta)
1. **Create an OIDC native app** In Okta Admin Console, create an OIDC Native Application.
2. **Add your redirect URI** Register the exact callback URL used by your app.
3. **Configure the plugin**
```typescript
await SocialLogin.initialize({
oauth2: {
okta: {
appId: 'your-okta-client-id',
authorizationBaseUrl: 'https://your-domain.okta.com/oauth2/default/v1/authorize',
accessTokenEndpoint: 'https://your-domain.okta.com/oauth2/default/v1/token',
redirectUrl: 'myapp://oauth/okta',
scope: 'openid profile email offline_access',
pkceEnabled: true,
resourceUrl: 'https://your-domain.okta.com/oauth2/default/v1/userinfo',
},
},
});
```
### Keycloak and custom OIDC providers
[Section titled âKeycloak and custom OIDC providersâ](#keycloak-and-custom-oidc-providers)
If your provider supports OpenID Connect discovery, prefer `issuerUrl`:
```typescript
await SocialLogin.initialize({
oauth2: {
keycloak: {
issuerUrl: 'https://sso.example.com/realms/mobile',
clientId: 'mobile-app',
redirectUrl: 'myapp://oauth/keycloak',
scope: 'openid profile email offline_access',
pkceEnabled: true,
},
},
});
```
If discovery is not available, configure the authorization and token endpoints manually.
## Platform-specific notes
[Section titled âPlatform-specific notesâ](#platform-specific-notes)
### iOS
[Section titled âiOSâ](#ios)
* The plugin uses `ASWebAuthenticationSession`.
* Set `iosPrefersEphemeralSession: true` if you want a private browser session with no shared cookies.
### Android
[Section titled âAndroidâ](#android)
* OAuth redirects return through your app scheme and host.
* Make sure the provider callback URL exactly matches your Android deep link setup.
* The plugin already handles the OAuth activity. Only add custom intent filters if your app needs a different redirect pattern.
### Web
[Section titled âWebâ](#web)
* Popup flow is the default and works well for single-page apps.
* Redirect flow is better when the provider blocks popups or your auth rules require top-level navigation.
* Some providers block direct browser token exchange with CORS. In those cases, use a backend exchange or a provider setup that allows public clients.
## Security best practices
[Section titled âSecurity best practicesâ](#security-best-practices)
1. **Use PKCE** Keep `pkceEnabled: true` for public clients.
2. **Prefer authorization code flow** `responseType: 'code'` is safer than implicit flow.
3. **Validate tokens on your backend** Decode and verify issuer, audience, expiration, and signature server-side.
4. **Store refresh tokens securely** For native apps, pair this plugin with [@capgo/capacitor-persistent-account](https://github.com/Cap-go/capacitor-persistent-account).
5. **Use HTTPS everywhere** Production auth endpoints and logout endpoints should always use HTTPS.
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
### `providerId is required`
[Section titled âproviderId is requiredâ](#providerid-is-required)
Every OAuth2 method needs the configured provider key:
```typescript
await SocialLogin.login({
provider: 'oauth2',
options: { providerId: 'github' },
});
```
### `OAuth2 provider "xxx" not configured`
[Section titled âOAuth2 provider "xxx" not configuredâ](#oauth2-provider-xxx-not-configured)
Call `SocialLogin.initialize()` before login and make sure the `providerId` matches the object key under `oauth2`.
### Redirect URL mismatch
[Section titled âRedirect URL mismatchâ](#redirect-url-mismatch)
* Compare the configured redirect URL in your app and provider dashboard character by character.
* Watch for trailing slashes, scheme mismatches, and different hosts.
* Make sure mobile app URL schemes are registered before testing on device.
### No refresh token returned
[Section titled âNo refresh token returnedâ](#no-refresh-token-returned)
Most providers only return refresh tokens when you request scopes like `offline_access` or explicitly force consent. Review the provider-specific policy.
### Debugging token exchange
[Section titled âDebugging token exchangeâ](#debugging-token-exchange)
Enable `logsEnabled: true` on the provider config to inspect generated URLs and token exchange details.
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [Social Login getting started](/docs/plugins/social-login/getting-started/)
* [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/)
## Keep going from Generic OAuth2 Providers
[Section titled âKeep going from Generic OAuth2 Providersâ](#keep-going-from-generic-oauth2-providers)
If you are using **Generic OAuth2 Providers** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Privacy Manifest and iOS URL Handlers
> Add app-level privacy manifests and combine Facebook and Google URL callbacks for @capgo/capacitor-social-login.
## Privacy manifest for app developers
[Section titled âPrivacy manifest for app developersâ](#privacy-manifest-for-app-developers)
If you use Google, Facebook, or Apple login, declare the data collected by those SDKs in your appâs `PrivacyInfo.xcprivacy` file. Add the file in your app at `ios/App/PrivacyInfo.xcprivacy`.
Do not add this file to the plugin package. Adjust the data types to match your appâs usage and the provider SDK documentation.
### Google Sign-In example
[Section titled âGoogle Sign-In exampleâ](#google-sign-in-example)
```json
{
"NSPrivacyCollectedDataTypes": [
{ "NSPrivacyCollectedDataType": "EmailAddress", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false },
{ "NSPrivacyCollectedDataType": "Name", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false },
{ "NSPrivacyCollectedDataType": "UserID", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false }
]
}
```
### Facebook Login example
[Section titled âFacebook Login exampleâ](#facebook-login-example)
```json
{
"NSPrivacyCollectedDataTypes": [
{ "NSPrivacyCollectedDataType": "EmailAddress", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false },
{ "NSPrivacyCollectedDataType": "Name", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false },
{ "NSPrivacyCollectedDataType": "UserID", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false },
{ "NSPrivacyCollectedDataType": "FriendsList", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false }
]
}
```
### Apple Sign-In example
[Section titled âApple Sign-In exampleâ](#apple-sign-in-example)
```json
{
"NSPrivacyCollectedDataTypes": [
{ "NSPrivacyCollectedDataType": "EmailAddress", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false },
{ "NSPrivacyCollectedDataType": "Name", "NSPrivacyCollectedDataTypeLinked": true, "NSPrivacyCollectedDataTypeTracking": false }
]
}
```
Review [Appleâs privacy manifest documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/) for all allowed keys and values.
## Combine Facebook and Google URL handlers
[Section titled âCombine Facebook and Google URL handlersâ](#combine-facebook-and-google-url-handlers)
When an iOS app uses both Facebook and Google login, route the callback URL to both SDKs before passing it back to Capacitor.
In `ios/App/App/AppDelegate.swift`:
```swift
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Return true if the URL was handled by either Facebook or Google authentication.
if FBSDKCoreKit.ApplicationDelegate.shared.application(
app,
open: url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation]
) || GIDSignIn.sharedInstance.handle(url) {
return true
}
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
```
Make sure `AppDelegate.swift` imports the SDKs used by your app:
```swift
import Capacitor
import FBSDKCoreKit
import GoogleSignIn
import UIKit
```
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [Facebook Login Setup](/docs/plugins/social-login/facebook/)
* [Google Login on iOS](/docs/plugins/social-login/google/ios/)
* [Troubleshooting](/docs/plugins/social-login/troubleshooting/)
# Supabase Apple Login on Android
> Learn how to set up Apple Sign-In with Supabase Authentication on Android using the Capacitor Social Login plugin.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
This guide will help you integrate Apple Sign-In with Supabase Authentication on Android. It is assumed that you have already completed:
* the [Supabase Apple Login - General Setup](../general/).
Important
Apple Sign-In on Android requires a backend server because Apple doesnât provide native Android support. Weâll use a Supabase Edge Function as the backend.
## Step 1: Deploy the Backend Edge Function
[Section titled âStep 1: Deploy the Backend Edge Functionâ](#step-1-deploy-the-backend-edge-function)
First, we need to deploy the Supabase Edge Function that will handle the Apple OAuth callback.
1. **Navigate to your Supabase project directory**
```bash
cd your-project/supabase
```
2. **Create the edge function** (if it doesnât exist)
```bash
supabase functions new apple-signin-callback
```
3. **Copy the edge function code**
The complete edge function implementation is available in the [example app](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/supabase/functions/apple-signin-callback).
Copy the following files to your project:
* `supabase/functions/apple-signin-callback/index.ts` - Main edge function code
* `supabase/functions/apple-signin-callback/deno.json` - Import map for dependencies (includes `jose` library for JWT signing)
4. **Configure JWT verification**
The Apple OAuth callback endpoint must be public (no authentication required) because Apple will redirect to it. Update your `supabase/config.toml` file:
```toml
[functions.apple-signin-callback]
enabled = true
verify_jwt = false # Important: Set to false for public OAuth callback
import_map = "./functions/apple-signin-callback/deno.json"
entrypoint = "./functions/apple-signin-callback/index.ts"
```
Important
Setting `verify_jwt = false` makes this endpoint public. This is required because Appleâs OAuth redirect doesnât include Supabase authentication headers. The endpoint validates the OAuth flow itself.
5. **Deploy the function**
```bash
supabase functions deploy apple-signin-callback
```
6. **Get your function URL**
After deployment, youâll get a URL like:
```plaintext
https://your-project-ref.supabase.co/functions/v1/apple-signin-callback
```
If you cannot find it, you can do the following:
1. Open `https://supabase.com/dashboard/project/YOUR_PROJECT_REF/functions`
2. Click on the `apple-signin-callback` function URL to copy it. 
Save this URL
Youâll need this URL in the next step when configuring Apple Developer Portal.
## Step 2: Configure Apple Developer Portal
[Section titled âStep 2: Configure Apple Developer Portalâ](#step-2-configure-apple-developer-portal)
Now we need to configure Apple Developer Portal with the backend URL and get all the required values.
Function URL
Make sure you have your Supabase Edge Function URL from Step 1 before proceeding. Youâll need it to configure the Return URL in Apple Developer Portal.
1. **Follow the Apple Login Android Setup Guide**
Complete the [Apple Login Android Setup guide](/docs/plugins/social-login/apple/android/) to:
* Create a Service ID
* Generate a private key (.p8 file)
* Get your Team ID and Key ID
* Configure the Return URL
2. **Set the Return URL in Apple Developer Portal**
When configuring the Return URL in Apple Developer Portal (step 6.9 of the Apple guide), use your Supabase Edge Function URL:
```plaintext
https://your-project-ref.supabase.co/functions/v1/apple-signin-callback
```
Important: Use Supabase Edge Function URL
**Do NOT** use the redirect URL from the [Apple Login Android Setup guide](/docs/plugins/social-login/apple/android/). That guide uses a custom backend server URL. For Supabase integration, you **must** use your Supabase Edge Function URL instead.
Exact Match Required
The Return URL must match **exactly** what you configure here. Apple is very strict about redirect URI matching.
3. **Collect all required values**
After completing the Apple setup guide, you should have:
* **APPLE\_TEAM\_ID**: Your Apple Developer Team ID
* **APPLE\_KEY\_ID**: The Key ID from Apple Developer Portal
* **APPLE\_PRIVATE\_KEY**: Your .p8 private key file (needs to be base64 encoded)
* **ANDROID\_SERVICE\_ID**: Your Apple Service ID (e.g., `com.example.app.service`)
* **BASE\_REDIRECT\_URL**: Your deep link URL (e.g., `capgo-demo-app://path`)
Deep Link URL
The `BASE_REDIRECT_URL` is the deep link scheme configured in your `AndroidManifest.xml`. This is where the backend will redirect after authentication.
The value of your deep link is dependent on the `android:scheme="capgo-demo-app"` code. If you have set `android:scheme="capgo-demo-app"` in your `AndroidManifest.xml`, then your deep link will be `capgo-demo-app://path`.
## Step 3: Set Supabase Secrets
[Section titled âStep 3: Set Supabase Secretsâ](#step-3-set-supabase-secrets)
Now we need to configure the environment variables (secrets) for the Supabase Edge Function.
1. **Encode your private key**
First, encode your Apple private key (.p8 file) to base64:
```bash
base64 -i AuthKey_XXXXX.p8
```
Copy the entire output (itâs a single long string).
2. **Set secrets using Supabase CLI**
```bash
supabase secrets set APPLE_TEAM_ID=your_team_id
supabase secrets set APPLE_KEY_ID=your_key_id
supabase secrets set APPLE_PRIVATE_KEY=your_base64_encoded_key
supabase secrets set ANDROID_SERVICE_ID=your.service.id
supabase secrets set BASE_REDIRECT_URL=your-app://path
supabase secrets set APPLE_REDIRECT_URI=https://your-project-ref.supabase.co/functions/v1/apple-signin-callback
```
Replace Placeholders
Replace all the placeholder values with your actual values from Step 2.
3. **Alternative: Set secrets in Supabase Dashboard**
If you prefer using the dashboard:
1. Go to your Supabase project dashboard
2. Navigate to **Edge Functions** â **Settings** â **Secrets**
3. Add each secret variable with its value
Android App Configuration
The [Apple Login Android Setup guide](/docs/plugins/social-login/apple/android/) already covers configuring your Android app:
* Adding the deep link intent filter to `AndroidManifest.xml`
* Adding the `onNewIntent` handler to `MainActivity.java`
Make sure youâve completed those steps before proceeding. The deep link scheme you configure there will be your `BASE_REDIRECT_URL` in Step 3.
## Step 4: Use the Authentication Helper
[Section titled âStep 4: Use the Authentication Helperâ](#step-4-use-the-authentication-helper)
Now you can use the authentication helper in your app code.
### Implementation
[Section titled âImplementationâ](#implementation)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file.
### Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
```typescript
import { authenticateWithAppleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithAppleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
### Update the Helper Function
[Section titled âUpdate the Helper Functionâ](#update-the-helper-function)
When using the `authenticateWithAppleSupabase` helper function, you **must** update the following values to match your configuration:
1. **Update `redirectUrl`** - Set this to your Supabase Edge Function URL:
```typescript
const redirectUrl = platform === 'android'
? 'https://your-project-ref.supabase.co/functions/v1/apple-signin-callback'
: undefined;
```
2. **Update `clientId`** - Set this to your Apple Service ID:
```typescript
await SocialLogin.initialize({
apple: {
clientId: isIOS
? undefined // iOS uses bundle ID automatically
: 'your.service.id.here', // Your Apple Service ID for Android
redirectUrl: redirectUrl,
},
});
```
Important
Replace `'your.service.id.here'` with your actual Apple Service ID (the same value you used for `ANDROID_SERVICE_ID` in Step 3).
See the [complete implementation](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference.
## Step 5: Test the Integration
[Section titled âStep 5: Test the Integrationâ](#step-5-test-the-integration)
1. **Build and run your Android app**
```bash
npx cap sync android
npx cap run android
```
2. **Test the authentication flow**
* Tap the âSign in with Appleâ button
* You should see the Apple OAuth page in a browser
* After authenticating, you should be redirected back to your app
* Check the console logs for any errors
3. **Verify the flow**
The complete flow should be:
1. User taps âSign in with Appleâ
2. App opens browser with Apple OAuth
3. User authenticates with Apple
4. Apple redirects to: `https://your-project-ref.supabase.co/functions/v1/apple-signin-callback`
5. Edge function exchanges code for tokens
6. Edge function redirects to: `your-app://path?id_token=...&access_token=...`
7. Android app receives the deep link and processes the identity token
8. App signs in to Supabase with the identity token
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication fails:
* **Redirect URI mismatch**: Verify the Return URL in Apple Developer Portal matches exactly with `APPLE_REDIRECT_URI` secret
* **Deep link not working**: Check that `AndroidManifest.xml` intent filter matches your `BASE_REDIRECT_URL`
* **Missing secrets**: Verify all secrets are set correctly using `supabase secrets list`
* **Token exchange fails**: Check edge function logs in Supabase Dashboard for detailed error messages
* **App doesnât receive callback**: Ensure `onNewIntent` is properly implemented in MainActivity
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference
## How It Works
[Section titled âHow It Worksâ](#how-it-works)
On Android, Apple Sign-In uses an OAuth redirect flow:
1. **Initialization**: The plugin is initialized with your Service ID and backend redirect URL
2. **OAuth Flow**: Opens a browser/Chrome Custom Tab with Appleâs OAuth page
3. **Backend Callback**: Apple redirects to your Supabase Edge Function with an authorization code
4. **Token Exchange**: The edge function exchanges the code for tokens using Appleâs token endpoint
5. **Deep Link Redirect**: The edge function redirects back to your app with the identity token
6. **Supabase Authentication**: The app receives the token and signs in to Supabase
This flow is necessary because Apple doesnât provide native Android support for Sign in with Apple.
## Keep going from Supabase Apple Login on Android
[Section titled âKeep going from Supabase Apple Login on Androidâ](#keep-going-from-supabase-apple-login-on-android)
If you are using **Supabase Apple Login on Android** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Apple Login - General Setup
> Learn how to integrate Apple Sign-In with Supabase Authentication using the Capacitor Social Login plugin.
## Overview
[Section titled âOverviewâ](#overview)
This guide will help you integrate Apple Sign-In with Supabase Authentication. Apple Sign-In provides a secure, privacy-focused authentication method that works across iOS, Android, and Web platforms.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
Before starting, ensure you have:
1. [Created a Supabase project](https://database.new/)
2. Read the [Apple Login General Setup](/docs/plugins/social-login/apple/general/) guide to setup Apple OAuth credentials
3. Followed the respective platform-specific guides to setup Apple OAuth credentials for your target platform:
* [Android Setup](/docs/plugins/social-login/apple/android/)
* [iOS Setup](/docs/plugins/social-login/apple/ios/)
* [Web Setup](/docs/plugins/social-login/apple/web/)
Note
Before starting the Supabase tutorial, you need to generate the client IDs for the platforms you plan to use.
For iOS, the client ID is the same as your app ID. For Android and Web, the client ID is the same as your service ID. You will use them in step 7 of this guide.
## Enabling Apple OAuth provider in Supabase
[Section titled âEnabling Apple OAuth provider in Supabaseâ](#enabling-apple-oauth-provider-in-supabase)
1. Go to your [Supabase Dashboard](https://app.supabase.com/)
2. Click on your project

3. Do go to the `Authentication` menu

4. Click on the `Providers` tab

5. Find the `Apple` provider

6. Enable the `Apple` provider

7. Fill in the client ID configuration:
Note
If you are using Apple login for iOS, the client ID is the same as your app ID. If you are using Apple login for Android or Web, the client ID is the same as your service ID. If you are using both, you need to provide both the app ID and the service ID.

8. Click on the `Save` button

Note
You ****DO NOT HAVE TO**** setup `Secret key (for OAuth)`. We will do a custom backend implementation to handle the Apple login.
VoilĂ , you have now enabled Apple Sign-In with Supabase Authentication đ
## Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
The complete implementation includes a helper function `authenticateWithAppleSupabase()` that handles the entire Apple Sign-In flow with Supabase. This function:
* Initializes Apple Sign-In with platform-specific configuration
* Handles the authentication flow (native on iOS, OAuth redirect on Android/Web)
* Extracts the identity token from Apple
* Signs in to Supabase with the identity token
Complete Implementation
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file.
### Basic Usage
[Section titled âBasic Usageâ](#basic-usage)
```typescript
import { authenticateWithAppleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithAppleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
### How It Works
[Section titled âHow It Worksâ](#how-it-works)
The helper function automatically handles platform-specific differences:
* **iOS**: Uses native Apple Sign-In (no redirect URL needed, uses bundle ID automatically)
* **Android**: Uses OAuth redirect flow with backend edge function (requires Service ID)
* **Web**: Uses OAuth popup flow (requires Service ID and current page URL as redirect)
The function returns an identity token from Apple, which is then used to authenticate with Supabase using `supabase.auth.signInWithIdToken()`.
## Keep going from Supabase Apple Login - General Setup
[Section titled âKeep going from Supabase Apple Login - General Setupâ](#keep-going-from-supabase-apple-login---general-setup)
If you are using **Supabase Apple Login - General Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Apple Login on iOS Setup
> Learn how to integrate Apple Sign-In with Supabase Authentication on iOS.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
This guide will help you integrate Apple Sign-In with Supabase Authentication on iOS. It is assumed that you have already completed:
* the [Apple Login iOS setup](/docs/plugins/social-login/apple/ios/)
* the [Supabase Apple Login - General Setup](../general/).
## Implementation
[Section titled âImplementationâ](#implementation)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it.
### Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
The `authenticateWithAppleSupabase` function handles the entire authentication flow:
```typescript
import { authenticateWithAppleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithAppleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
## How It Works
[Section titled âHow It Worksâ](#how-it-works)
On iOS, Apple Sign-In uses the native implementation:
1. **Initialization**: The plugin uses your appâs bundle ID automatically (no `clientId` needed)
2. **Native Sign-In**: Uses Appleâs native Sign in with Apple button and authentication flow
3. **Identity Token**: Apple returns an identity token (JWT) containing user information
4. **Supabase Authentication**: The identity token is sent to Supabase using `signInWithIdToken()`
The helper function automatically detects the iOS platform and configures everything appropriately.
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Bundle ID Configuration
[Section titled âBundle ID Configurationâ](#bundle-id-configuration)
* iOS uses your appâs bundle ID automatically for Apple Sign-In
* Make sure your bundle ID matches whatâs configured in Apple Developer Portal
* The bundle ID should have âSign in with Appleâ capability enabled
### Supabase Client ID
[Section titled âSupabase Client IDâ](#supabase-client-id)
In Supabase, configure your Apple provider with:
* **Client ID**: Your iOS App ID (bundle ID) - e.g., `app.capgo.plugin.SocialLogin`
If youâre also using Android/Web, youâll need to provide both the App ID and Service ID in Supabaseâs Client ID field (comma-separated).
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication fails:
* **Bundle ID mismatch**: Verify your bundle ID matches in both Xcode and Apple Developer Portal
* **Capability not enabled**: Ensure âSign in with Appleâ capability is enabled in Xcode
* **Supabase configuration**: Verify your App ID is correctly configured in Supabase Apple provider settings
* **Token validation fails**: Check that the identity token is being received from Apple
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference
## Keep going from Supabase Apple Login on iOS Setup
[Section titled âKeep going from Supabase Apple Login on iOS Setupâ](#keep-going-from-supabase-apple-login-on-ios-setup)
If you are using **Supabase Apple Login on iOS Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Apple Login on Web
> Learn how to integrate Apple Sign-In with Supabase Authentication on Web.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
This guide will help you integrate Apple Sign-In with Supabase Authentication on Web. It is assumed that you have already completed:
* the [Apple Login Web setup](/docs/plugins/social-login/apple/web/)
* the [Supabase Apple Login - General Setup](../general/).
## Implementation
[Section titled âImplementationâ](#implementation)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it.
### Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
The `authenticateWithAppleSupabase` function handles the entire authentication flow:
```typescript
import { authenticateWithAppleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithAppleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
## How It Works
[Section titled âHow It Worksâ](#how-it-works)
On Web, Apple Sign-In uses an OAuth popup flow:
1. **Initialization**: The plugin is initialized with your Service ID and the current page URL as the redirect URL
2. **OAuth Popup**: Opens a popup window with Appleâs OAuth page
3. **User Authentication**: User authenticates with Apple in the popup
4. **Identity Token**: Apple returns an identity token (JWT) containing user information
5. **Supabase Authentication**: The identity token is sent to Supabase using `signInWithIdToken()`
The helper function automatically detects the web platform and configures everything appropriately.
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Service ID Configuration
[Section titled âService ID Configurationâ](#service-id-configuration)
* Web requires your Apple Service ID (same as Android)
* The Service ID must be configured in Apple Developer Portal with the correct Return URLs
* Make sure your domain is added to the allowed domains in Apple Developer Portal
### Redirect URL
[Section titled âRedirect URLâ](#redirect-url)
* On web, the redirect URL is automatically set to `window.location.href` (current page URL)
* This must match one of the Return URLs configured in Apple Developer Portal
* Ensure both the URL with and without trailing slash are configured in Apple Developer Portal
### Supabase Client ID
[Section titled âSupabase Client IDâ](#supabase-client-id)
In Supabase, configure your Apple provider with:
* **Client ID**: Your Apple Service ID (e.g., `com.example.app.service`)
If youâre also using iOS, youâll need to provide both the App ID and Service ID in Supabaseâs Client ID field (comma-separated).
### Update the Helper Function
[Section titled âUpdate the Helper Functionâ](#update-the-helper-function)
When using the `authenticateWithAppleSupabase` helper function, you **must** update the `clientId` to match your Apple Service ID:
```typescript
await SocialLogin.initialize({
apple: {
clientId: isIOS
? undefined // iOS uses bundle ID automatically
: 'your.service.id.here', // Your Apple Service ID for Web and Android
redirectUrl: redirectUrl,
},
});
```
Important
Replace `'your.service.id.here'` with your actual Apple Service ID (the same value you configured in Apple Developer Portal).
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication fails:
* **Service ID mismatch**: Verify your Service ID matches in both Apple Developer Portal and your code
* **Return URL not configured**: Ensure your current page URL (with and without trailing slash) is configured in Apple Developer Portal
* **Popup blocked**: Check browser settings - some browsers block popups by default
* **Domain not allowed**: Verify your domain is added to the allowed domains in Apple Developer Portal
* **Supabase configuration**: Verify your Service ID is correctly configured in Supabase Apple provider settings
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference
## Keep going from Supabase Apple Login on Web
[Section titled âKeep going from Supabase Apple Login on Webâ](#keep-going-from-supabase-apple-login-on-web)
If you are using **Supabase Apple Login on Web** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Google Login on Android
> Learn how to set up Google Sign-In with Supabase Authentication on Android using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will help you integrate Google Sign-In with Supabase Authentication on Android. It is assumed that you have already completed:
* the [Google Login Android setup](/docs/plugins/social-login/google/android/)
* the [Supabase Google Login - General Setup](/docs/plugins/social-login/supabase/google/general/).
## Implementation
[Section titled âImplementationâ](#implementation)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it.
### Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
The `authenticateWithGoogleSupabase` function handles the entire authentication flow:
```typescript
import { authenticateWithGoogleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithGoogleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
## How It Works
[Section titled âHow It Worksâ](#how-it-works)
For a detailed explanation of how the authentication flow works, including nonce generation, JWT validation, and Supabase sign-in, see the [How It Works section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#how-it-works).
For the complete code reference, see the [Complete Code Reference section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#complete-code-reference).
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Nonce Handling
[Section titled âNonce Handlingâ](#nonce-handling)
The nonce implementation follows the pattern from the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase):
* `rawNonce` goes to Supabaseâs `signInWithIdToken()`
* Supabase makes a hash of `rawNonce` and compares it with the `nonceDigest` which is included in the ID token from Google Sign-In
* `nonceDigest` (SHA-256 hash, hex-encoded) goes to the `nonce` parameter in Google Sign-In APIs
### Automatic Retry
[Section titled âAutomatic Retryâ](#automatic-retry)
The implementation includes automatic retry logic:
* If JWT validation fails on first attempt, it logs out and retries once
* This handles cases where cached tokens might have incorrect nonces
* If the retry also fails, an error is returned
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication fails:
* **Invalid audience**: Verify your Google Client IDs match in both Google Cloud Console and Supabase
* **Nonce mismatch**: Check console logs - the function will automatically retry, but you can manually logout first if needed
* **Token validation fails**: Ensure youâre using `mode: 'online'` in the initialize call to get an idToken
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference
## Keep going from Supabase Google Login on Android
[Section titled âKeep going from Supabase Google Login on Androidâ](#keep-going-from-supabase-google-login-on-android)
If you are using **Supabase Google Login on Android** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Google Login - General Setup
> Learn how to set up Google Sign-In with Supabase Authentication using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will walk you through integrating Google Sign-In with Supabase Authentication using the Capacitor Social Login plugin. This setup allows you to use native Google Sign-In on mobile platforms while leveraging Supabase Auth for backend authentication.
## Prerequisites
[Section titled âPrerequisitesâ](#prerequisites)
Before starting, ensure you have:
1. [Created a Supabase project](https://database.new/)
2. Read the [Google Login General Setup](/docs/plugins/social-login/google/general/) guide to setup Google OAuth credentials
3. Followed the respective platform-specific guides to setup Google OAuth credentials for your target platform:
* [Android Setup](/docs/plugins/social-login/google/android/)
* [iOS Setup](/docs/plugins/social-login/google/ios/)
* [Web Setup](/docs/plugins/social-login/google/web/)
Note
Before starting the Supabase tutorial, you need to generate the client IDs for the platforms you plan to use. You will use them in step 7 of this guide.
## Enabling Google OAuth provider in Supabase
[Section titled âEnabling Google OAuth provider in Supabaseâ](#enabling-google-oauth-provider-in-supabase)
1. Go to your [Supabase Dashboard](https://app.supabase.com/)
2. Click on your project

3. Do go to the `Authentication` menu

4. Click on the `Providers` tab

5. Find the `Google` provider

6. Enable the provider

7. Add the client IDs for the platforms you plan to use

Note
This included the web client ID, the iOS client ID and the Android client ID. You can skip providing some of them, depending on the platforms you plan to use.
8. Click on the `Save` button

Note
You ****SHOULD NOT**** setup `Client Secret (for OAuth)` or `Callback URL (for OAuth)`. Some sources might also suggest setting `Skip nonce checks` for Google on iOS, but with this guide this isnât needed.
VoilĂ , you have now enabled Google Sign-In with Supabase Authentication đ
## How Google Sign-In with Supabase Authentication Helper Works
[Section titled âHow Google Sign-In with Supabase Authentication Helper Worksâ](#how-google-sign-in-with-supabase-authentication-helper-works)
This section explains how the Google Sign-In integration with Supabase works under the hood. Understanding this flow will help you implement and troubleshoot the authentication process.
Complete Implementation
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file.
### 1. Nonce Generation
[Section titled â1. Nonce Generationâ](#1-nonce-generation)
The implementation generates a secure nonce pair following the [Supabase nonce requirements](https://react-native-google-signin.github.io/docs/security#usage-with-supabase):
```typescript
// Generate URL-safe random nonce
function getUrlSafeNonce(): string {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
}
// Hash the nonce with SHA-256
async function sha256Hash(message: string): Promise {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}
// Generate nonce pair
async function getNonce(): Promise<{ rawNonce: string; nonceDigest: string }> {
const rawNonce = getUrlSafeNonce();
const nonceDigest = await sha256Hash(rawNonce);
return { rawNonce, nonceDigest };
}
```
**Flow:**
* `rawNonce`: URL-safe random string (64 hex characters)
* `nonceDigest`: SHA-256 hash of `rawNonce` (hex-encoded)
* `nonceDigest` is passed to Google Sign-In â Google includes the nonce digest in the ID token
* `rawNonce` is passed to Supabase â Supabase hashes the raw nonce and compares with the tokenâs nonce
### 2. Google Sign-In
[Section titled â2. Google Sign-Inâ](#2-google-sign-in)
The function initializes the plugin and signs in with Google:
```typescript
await SocialLogin.initialize({
google: {
webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
// iOS only:
iOSClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com',
mode: 'online', // Required to get idToken
},
});
const response = await SocialLogin.login({
provider: 'google',
options: {
scopes: ['email', 'profile'],
nonce: nonceDigest, // Pass the SHA-256 hashed nonce
},
});
```
### 3. JWT Validation
[Section titled â3. JWT Validationâ](#3-jwt-validation)
Before sending the token to Supabase, the implementation validates the JWT token:
```typescript
function validateJWTToken(idToken: string, expectedNonceDigest: string): { valid: boolean; error?: string } {
const decodedToken = decodeJWT(idToken);
// Check audience matches your Google Client IDs
const audience = decodedToken.aud;
if (!VALID_GOOGLE_CLIENT_IDS.includes(audience)) {
return { valid: false, error: 'Invalid audience' };
}
// Check nonce matches
const tokenNonce = decodedToken.nonce;
if (tokenNonce && tokenNonce !== expectedNonceDigest) {
return { valid: false, error: 'Nonce mismatch' };
}
return { valid: true };
}
```
**Why validate before Supabase?**
Validating the JWT token before sending the token to Supabase serves several important purposes:
1. **Prevent Invalid Requests**: If the token has an incorrect audience or nonce mismatch, Supabase will reject the token anyway. Validating first avoids unnecessary API calls and provides clearer error messages.
2. **Token Caching Issues**: On some platforms (especially iOS), Google Sign-In SDK can cache tokens for performance. When a cached token is returned, the cached token may have been generated with a different nonce (or no nonce at all), causing Supabase to reject the token with a ânonce mismatchâ error. By validating before sending to Supabase, we can detect this issue early and automatically retry with a fresh token.
3. **Security** (iOS): On iOS, validation ensures the token was issued for your specific Google Client IDs, preventing potential security issues from using tokens intended for other applications.
4. **Better Error Handling**: Detecting issues before Supabase allows for automatic retry logic, which is essential for handling iOS caching issues transparently.
If validation fails, the function automatically:
1. Logs out from Google (clears cached tokens - critical on iOS)
2. Retries authentication once (forces fresh token generation with correct nonce)
3. If retry also fails, returns an error
### 4. Supabase Sign-In
[Section titled â4. Supabase Sign-Inâ](#4-supabase-sign-in)
Finally, the validated token is sent to Supabase:
```typescript
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: googleResponse.idToken,
nonce: rawNonce, // Pass the raw (unhashed) nonce
});
```
## Complete Code Reference
[Section titled âComplete Code Referenceâ](#complete-code-reference)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file, which includes:
* `getUrlSafeNonce()` - Generates URL-safe random nonce
* `sha256Hash()` - Hashes string with SHA-256
* `getNonce()` - Generates nonce pair
* `decodeJWT()` - Decodes JWT token
* `validateJWTToken()` - Validates JWT audience and nonce
* `authenticateWithGoogleSupabase()` - Main authentication function with automatic retry
### Additional Example Files
[Section titled âAdditional Example Filesâ](#additional-example-files)
* [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) - Example component with redirect handling (Web)
* [SupabaseCreateAccountPage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabaseCreateAccountPage.tsx) - Example create account page
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
Please proceed to the platform-specific setup guide for your target platform:
* [Android Setup](../android/)
* [iOS Setup](../ios/)
* [Web Setup](../web/)
## Keep going from Supabase Google Login - General Setup
[Section titled âKeep going from Supabase Google Login - General Setupâ](#keep-going-from-supabase-google-login---general-setup)
If you are using **Supabase Google Login - General Setup** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Google Login on iOS
> Learn how to set up Google Sign-In with Supabase Authentication on iOS using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will help you integrate Google Sign-In with Supabase Authentication on iOS. It is assumed that you have already completed:
* the [Google Login iOS setup](/docs/plugins/social-login/google/ios/)
* the [Supabase Google Login - General Setup](/docs/plugins/social-login/supabase/google/general/).
## Implementation
[Section titled âImplementationâ](#implementation)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it.
### Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
The `authenticateWithGoogleSupabase` function handles the entire authentication flow:
```typescript
import { authenticateWithGoogleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithGoogleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
## How It Works
[Section titled âHow It Worksâ](#how-it-works)
For a detailed explanation of how the authentication flow works, including nonce generation, JWT validation, and Supabase sign-in, see the [How It Works section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#how-it-works).
## Important Caveats
[Section titled âImportant Caveatsâ](#important-caveats)
### iOS Token Caching and Nonce Issues
[Section titled âiOS Token Caching and Nonce Issuesâ](#ios-token-caching-and-nonce-issues)
iOS Nonce Caching Issue
On iOS, Google Sign-In can cache tokens, which may cause the nonce validation to fail. The `validateJWTToken` function detects this and automatically handles it:
1. **Automatic Detection**: The function checks if the nonce in the token matches the expected `nonceDigest`
2. **Automatic Retry**: If validation fails, it automatically logs out from Google and retries once
3. **Error Handling**: If the retry also fails, an error is returned
**Why this happens**: iOS Google Sign-In SDK caches tokens for performance. When a cached token is returned, it may have been generated with a different nonce (or no nonce), causing a mismatch.
**The solution**: The implementation automatically handles this by logging out and retrying, which forces Google to generate a fresh token with the correct nonce.
**Manual Workaround** (if automatic retry doesnât work):
```typescript
// Logout first to clear cached tokens
await SocialLogin.logout({ provider: 'google' });
// Then authenticate
const result = await authenticateWithGoogleSupabase();
```
This ensures a fresh token is obtained with the correct nonce.
For the complete code reference, see the [Complete Code Reference section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#complete-code-reference).
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Nonce Handling
[Section titled âNonce Handlingâ](#nonce-handling)
The nonce implementation follows the pattern from the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase):
* `rawNonce` goes to Supabaseâs `signInWithIdToken()`
* Supabase makes a hash of `rawNonce` and compares it with the `nonceDigest` which is included in the ID token from Google Sign-In
* `nonceDigest` (SHA-256 hash, hex-encoded) goes to the `nonce` parameter in Google Sign-In APIs
### Automatic Retry Mechanism
[Section titled âAutomatic Retry Mechanismâ](#automatic-retry-mechanism)
The `authenticateWithGoogleSupabase` function includes a `retry` parameter:
* First call (`retry=false`): If validation fails, automatically logs out and retries once
* Retry call (`retry=true`): If validation fails again, immediately returns an error
This handles the iOS token caching issue automatically.
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication fails:
* **Nonce mismatch**: The function automatically retries - check console logs for details. If it persists, manually logout first
* **Invalid audience**: Verify your Google Client IDs match in both Google Cloud Console and Supabase (both iOS and Web client IDs)
* **Token validation fails**: Ensure youâre using `mode: 'online'` in the initialize call to get an idToken
* **Info.plist configuration**: Ensure Info.plist has the correct URL schemes and GIDClientID
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference
## Keep going from Supabase Google Login on iOS
[Section titled âKeep going from Supabase Google Login on iOSâ](#keep-going-from-supabase-google-login-on-ios)
If you are using **Supabase Google Login on iOS** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Google Login on Web
> Learn how to set up Google Sign-In with Supabase Authentication on Web using the Capacitor Social Login plugin.
## Introduction
[Section titled âIntroductionâ](#introduction)
This guide will help you integrate Google Sign-In with Supabase Authentication on Web. It is assumed that you have already completed:
* the [Google Login Web setup](/docs/plugins/social-login/google/web/)
* the [Supabase Google Login - General Setup](/docs/plugins/social-login/supabase/google/general/).
## Implementation
[Section titled âImplementationâ](#implementation)
The complete implementation is available in the [example appâs `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it.
### Using the Authentication Helper
[Section titled âUsing the Authentication Helperâ](#using-the-authentication-helper)
The `authenticateWithGoogleSupabase` function handles the entire authentication flow:
```typescript
import { authenticateWithGoogleSupabase } from './supabaseAuthUtils';
const result = await authenticateWithGoogleSupabase();
if (result.success) {
console.log('Signed in:', result.user);
// Navigate to your authenticated area
} else {
console.error('Error:', result.error);
}
```
## Critical: Redirect Handling
[Section titled âCritical: Redirect Handlingâ](#critical-redirect-handling)
Critical: Redirect Handling
When using Google login on web, you **MUST** call any function from the plugin when the redirect happens to initialize the plugin so it can handle the redirect and close the popup window. You can call either `isLoggedIn()` OR `initialize()` - both will trigger the redirect handling.
This is essential for the OAuth popup flow to work correctly.
### Implementation Example
[Section titled âImplementation Exampleâ](#implementation-example)
Add this to your component that handles Google Sign-In:
```typescript
import { useEffect } from 'react';
import { SocialLogin } from '@capgo/capacitor-social-login';
function SupabasePage() {
// Check Google login status on mount to invoke redirect handling
// This doesn't serve any functional purpose in the UI but ensures
// that any pending OAuth redirects are properly processed
useEffect(() => {
const checkGoogleLoginStatus = async () => {
try {
await SocialLogin.isLoggedIn({ provider: 'google' });
// We don't use the result, this is just to trigger redirect handling
} catch (error) {
// Ignore errors - this is just for redirect handling
console.log('Google login status check completed (redirect handling)');
}
};
checkGoogleLoginStatus();
}, []);
// ... rest of your component
}
```
See the [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) for a complete example.
## How It Works
[Section titled âHow It Worksâ](#how-it-works)
For a detailed explanation of how the authentication flow works, including nonce generation, JWT validation, and Supabase sign-in, see the [How It Works section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#how-it-works).
For the complete code reference, see the [Complete Code Reference section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#complete-code-reference).
Also see:
* [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) - Example component with redirect handling
* [SupabaseCreateAccountPage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabaseCreateAccountPage.tsx) - Example create account page
## Important Notes
[Section titled âImportant Notesâ](#important-notes)
### Redirect Handling
[Section titled âRedirect Handlingâ](#redirect-handling)
When using Google login on web, you **MUST** call any function from the plugin when the redirect happens to initialize the plugin so it can handle the redirect and close the popup window. You can call either `isLoggedIn()` OR `initialize()` - both will trigger the redirect handling.
This is essential for the OAuth popup flow to work correctly. Without this, the popup window wonât close after authentication.
### Nonce Handling
[Section titled âNonce Handlingâ](#nonce-handling)
The nonce implementation follows the pattern from the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase):
* `rawNonce` goes to Supabaseâs `signInWithIdToken()`
* Supabase makes a hash of `rawNonce` and compares it with the `nonceDigest` which is included in the ID token from Google Sign-In
* `nonceDigest` (SHA-256 hash, hex-encoded) goes to the `nonce` parameter in Google Sign-In APIs
### Automatic Retry
[Section titled âAutomatic Retryâ](#automatic-retry)
The implementation includes automatic retry logic:
* If JWT validation fails on first attempt, it logs out and retries once
* This handles cases where cached tokens might have incorrect nonces
* If the retry also fails, an error is returned
## Troubleshooting
[Section titled âTroubleshootingâ](#troubleshooting)
If authentication fails:
* **Redirect not working**: Ensure youâre calling `isLoggedIn()` on component mount (see example above)
* **Invalid audience**: Verify your Google Client IDs match in both Google Cloud Console and Supabase
* **Authorized redirect URLs**: Check that authorized redirect URLs are configured in both Google Cloud Console and Supabase
* **Nonce mismatch**: Check console logs - the function will automatically retry, but you can manually logout first if needed
* **Token validation fails**: Ensure youâre using `mode: 'online'` in the initialize call to get an idToken
* Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference
## Keep going from Supabase Google Login on Web
[Section titled âKeep going from Supabase Google Login on Webâ](#keep-going-from-supabase-google-login-on-web)
If you are using **Supabase Google Login on Web** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Supabase Integration Introduction
> Learn how to integrate Supabase Authentication with the Capacitor Social Login plugin for a complete authentication solution.
## Overview
[Section titled âOverviewâ](#overview)
This tutorial will guide you through setting up Supabase Authentication with the Capacitor Social Login plugin. This integration allows you to use native social login providers (Google, Apple, Facebook, Twitter) on mobile platforms while leveraging Supabase Auth for backend authentication and PostgreSQL for data storage.
## What Youâll Learn
[Section titled âWhat Youâll Learnâ](#what-youll-learn)
* How to configure Supabase Authentication
* How to integrate Capacitor Social Login plugin with Supabase Auth
* Platform-specific setup for Android, iOS, and Web
* How to handle nonces securely for Supabase
## What Youâll Need
[Section titled âWhat Youâll Needâ](#what-youll-need)
Before you begin, make sure you have:
1. **A Supabase Project**
* Create a project at [Supabase Dashboard](https://app.supabase.com/)
* Enable Google OAuth provider
* Get your Supabase project URL and anon key
2. **Supabase JS SDK**
* Install Supabase in your project:
```bash
npm install @supabase/supabase-js
```
3. **A Capacitor Project**
* An existing Capacitor application
* Capacitor Social Login plugin installed:
```bash
npm install @capgo/capacitor-social-login
npx cap sync
```
4. **Platform-Specific Google Setup**
* Complete the Google Sign-In setup for your target platforms:
* [Google Login Android Setup](/docs/plugins/social-login/google/android/)
* [Google Login iOS Setup](/docs/plugins/social-login/google/ios/)
* [Google Login Web Setup](/docs/plugins/social-login/google/web/)
## Example Application
[Section titled âExample Applicationâ](#example-application)
A complete working example is available in the repository:
**Code Repository**: [You can find the code repository here](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app)
The example app demonstrates:
* Email/password authentication with Supabase
* Google Sign-In integration (Android, iOS, and Web)
* A simple key-value store using Supabase PostgreSQL tables
* User-specific data storage with Row Level Security (RLS)
## Key Implementation Details
[Section titled âKey Implementation Detailsâ](#key-implementation-details)
### Nonce Handling
[Section titled âNonce Handlingâ](#nonce-handling)
Supabase requires special nonce handling for security. The implementation follows the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase):
* Generate a `rawNonce` (URL-safe random string)
* Hash it with SHA-256 to get `nonceDigest`
* Pass `nonceDigest` to Google Sign-In
* Pass `rawNonce` to Supabase (Supabase hashes it internally for comparison)
### JWT Validation
[Section titled âJWT Validationâ](#jwt-validation)
The example implementation includes JWT validation to ensure:
* The token audience matches your configured Google Client IDs
* The nonce matches the expected digest
* Automatic retry on validation failure (especially important for iOS)
### Platform-Specific Considerations
[Section titled âPlatform-Specific Considerationsâ](#platform-specific-considerations)
* **iOS**: Token caching can cause nonce issues - the implementation handles this automatically
* **Web**: Must call `isLoggedIn()` on mount to handle OAuth redirects
* **Android**: Standard implementation with SHA-1 fingerprint configuration
## Next Steps
[Section titled âNext Stepsâ](#next-steps)
Continue with the setup guides:
* [Supabase Google Login - General Setup](../google/general/) - Overview and prerequisites
* [Android Setup](../google/android/) - Android-specific configuration
* [iOS Setup](../google/ios/) - iOS-specific configuration
* [Web Setup](../google/web/) - Web-specific configuration
### Apple Sign-In
[Section titled âApple Sign-Inâ](#apple-sign-in)
* [Supabase Apple Login - General Setup](/docs/plugins/social-login/supabase/apple/general/) - Overview and prerequisites
* [iOS Setup](/docs/plugins/social-login/supabase/apple/ios/) - iOS-specific configuration
* [Android Setup](/docs/plugins/social-login/supabase/apple/android/) - Android-specific configuration
## Keep going from Supabase Integration Introduction
[Section titled âKeep going from Supabase Integration Introductionâ](#keep-going-from-supabase-integration-introduction)
If you are using **Supabase Integration Introduction** to plan authentication and account flows, connect it with [Using @capgo/capacitor-social-login](/plugins/capacitor-social-login/) for the native capability in Using @capgo/capacitor-social-login, [@capgo/capacitor-social-login](/docs/plugins/social-login/) for the implementation detail in @capgo/capacitor-social-login, [@capgo/capacitor-passkey](/docs/plugins/passkey/) for the implementation detail in @capgo/capacitor-passkey, [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) for the implementation detail in @capgo/capacitor-native-biometric, and [Two-factor authentication](/docs/webapp/mfa/) for the implementation detail in Two-factor authentication.
# Troubleshooting
> Fix common @capgo/capacitor-social-login setup issues from the plugin README.
## Invalid Privacy Manifest (ITMS-91056)
[Section titled âInvalid Privacy Manifest (ITMS-91056)â](#invalid-privacy-manifest-itms-91056)
If App Store Connect reports this error:
> ITMS-91056: Invalid privacy manifest - The PrivacyInfo.xcprivacy file from the following path is invalid: âŠ
Check your app-level `PrivacyInfo.xcprivacy` file:
* The file must be valid JSON.
* Use only Apple-documented keys and values.
* Do not add a privacy manifest to the plugin package; add it to your app.
See [Privacy manifest and iOS URL handlers](/docs/plugins/social-login/privacy-and-ios-handlers/) for examples.
## Google Play Console AD\_ID permission error
[Section titled âGoogle Play Console AD\_ID permission errorâ](#google-play-console-ad_id-permission-error)
After submitting an app to Google Play, you may see:
```text
Google Api Error: Invalid request - This release includes the com.google.android.gms.permission.AD_ID permission
but your declaration on Play Console says your app doesn't use advertising ID.
```
The Facebook SDK includes `AD_ID` and other advertising-related permissions. If your app does not use Facebook Login, disable the Facebook provider in `capacitor.config.ts`:
```typescript
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
plugins: {
SocialLogin: {
providers: {
google: true,
facebook: false,
apple: true,
},
},
},
};
export default config;
```
Then run:
```bash
npx cap sync
```
The plugin uses stub classes instead of the real Facebook SDK, so Facebook dependencies and permissions are not included in your build.
## Google Sign-In 28444 on Android
[Section titled âGoogle Sign-In 28444 on Androidâ](#google-sign-in-28444-on-android)
`GetCredentialCustomException: [28444] Developer console is not set up correctly` comes from Google Credential Manager when the installed APKâs signing certificate, package name, or `webClientId` does not match Google Cloud Console.
Use the full [Android Google troubleshooting checklist](/docs/plugins/social-login/google/android/#troubleshooting). After a failed login, filter Logcat for `GoogleProvider`; the plugin prints `package`, `signingSha1`, and a masked `webClientId` to compare with your OAuth clients.
## Google Sign-In with Family Link supervised accounts
[Section titled âGoogle Sign-In with Family Link supervised accountsâ](#google-sign-in-with-family-link-supervised-accounts)
Family Link supervised accounts can fail with:
```text
NoCredentialException: No credentials available
```
For apps that need to support Family Link accounts, disable authorized-account filtering for the Google login call:
```typescript
import { SocialLogin } from '@capgo/capacitor-social-login';
await SocialLogin.login({
provider: 'google',
options: {
style: 'bottom',
filterByAuthorizedAccounts: false,
scopes: ['profile', 'email'],
},
});
```
Key points:
* Set `filterByAuthorizedAccounts` to `false`; the default is `true`.
* The plugin automatically retries with `standard` style if `bottom` style fails with `NoCredentialException`.
* These options only affect Android. iOS handles Family Link accounts normally.
* The error message suggests disabling `filterByAuthorizedAccounts` when this failure is detected.
## Where to store access tokens
[Section titled âWhere to store access tokensâ](#where-to-store-access-tokens)
Use [@capgo/capacitor-persistent-account](https://github.com/Cap-go/capacitor-persistent-account) when your app needs native secure storage for access or refresh tokens.
On Android, it stores data in Account Manager. On iOS, it stores data in Keychain.
## Related docs
[Section titled âRelated docsâ](#related-docs)
* [Getting started](/docs/plugins/social-login/getting-started/)
* [Google Login on Android](/docs/plugins/social-login/google/android/)
* [Provider configuration](/docs/plugins/social-login/getting-started/#dynamic-provider-dependencies)
* [Privacy manifest and iOS URL handlers](/docs/plugins/social-login/privacy-and-ios-handlers/)
# @capgo/capacitor-speech-recognition
> Capacitor plugin for comprehensive on-device speech recognition with live partial results.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for comprehensive on-device speech recognition with live partial results.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `available` - Checks whether the native speech recognition service is usable on the current device.
* `isOnDeviceRecognitionAvailable` - Checks whether the platformâs newer on-device recognition path is available for the selected locale.
* `start` - Begins capturing audio and transcribing speech.
* `stop` - Stops listening and tears down native resources.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `available` | Checks whether the native speech recognition service is usable on the current device. |
| `isOnDeviceRecognitionAvailable` | Checks whether the platformâs newer on-device recognition path is available for the selected locale. |
| `start` | Begins capturing audio and transcribing speech. |
| `stop` | Stops listening and tears down native resources. |
| `forceStop` | Force stops the current session. |
| `getLastPartialResult` | Gets the last cached partial transcription result. |
| `setPTTState` | Updates the current push-to-talk button state. |
| `getSupportedLanguages` | Gets the locales supported by the underlying recognizer. |
| `isListening` | Returns whether the plugin is actively listening for speech. |
| `checkPermissions` | Gets the current permission state. |
| `requestPermissions` | Requests the microphone + speech recognition permissions. |
| `getPluginVersion` | Returns the native plugin version bundled with this package. |
| `addListener` | Listen for segmented session completion events (Android only). |
| `addListener` | Listen for segmented recognition results (Android only). |
| `addListener` | Listen for partial transcription updates emitted while `partialResults` is enabled. |
| `addListener` | Listen for changes to the native listening state. |
| `addListener` | Listen for recognition errors. |
| `addListener` | Listen for the recognizer becoming ready for another session. |
| `removeAllListeners` | Removes every registered listener. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-speech-recognition](https://github.com/Cap-go/capacitor-speech-recognition/).
## Keep going from @capgo/capacitor-speech-recognition
[Section titled âKeep going from @capgo/capacitor-speech-recognitionâ](#keep-going-from-capgocapacitor-speech-recognition)
If you are using **@capgo/capacitor-speech-recognition** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-speech-recognition](/plugins/capacitor-speech-recognition/) for the native capability in Using @capgo/capacitor-speech-recognition, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# Getting Started
> Install @capgo/capacitor-speech-recognition and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-speech-recognition` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-speech-recognition
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `available`
[Section titled âavailableâ](#available)
Checks whether the native speech recognition service is usable on the current device.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.available();
```
### `isOnDeviceRecognitionAvailable`
[Section titled âisOnDeviceRecognitionAvailableâ](#isondevicerecognitionavailable)
Checks whether the platformâs newer on-device recognition path is available for the selected locale.
This is the capability check you should use before enabling `useOnDeviceRecognition`. A `true` result means the current device, OS version, and locale can use the newer on-device path for that platform.
Returns `false` when the device only supports the legacy recognizer path.
Platform SDK docs: iOS: [Speech](https://developer.apple.com/documentation/speech) Android: [SpeechRecognizer](https://developer.android.com/reference/android/speech/SpeechRecognizer)
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.isOnDeviceRecognitionAvailable();
```
### `start`
[Section titled âstartâ](#start)
Begins capturing audio and transcribing speech.
When `partialResults` is `true`, the returned promise resolves immediately and updates are streamed through the `partialResults` listener until the session ends.
The default path keeps the legacy recognizer behavior for backward compatibility. Pass `useOnDeviceRecognition: true` only after checking .
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.start();
```
### `stop`
[Section titled âstopâ](#stop)
Stops listening and tears down native resources.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.stop();
```
### `forceStop`
[Section titled âforceStopâ](#forcestop)
Force stops the current session.
On Android, this first tries a normal stop and then falls back to destroy/recreate after `timeout`. On iOS, the current session is stopped immediately.
If a partial transcript is cached, it is emitted through the `partialResults` listener with `forced: true`.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.forceStop();
```
### `getLastPartialResult`
[Section titled âgetLastPartialResultâ](#getlastpartialresult)
Gets the last cached partial transcription result.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.getLastPartialResult();
```
### `setPTTState`
[Section titled âsetPTTStateâ](#setpttstate)
Updates the current push-to-talk button state.
Use this together with `continuousPTT` or with a custom hold-to-talk flow.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.setPTTState({} as PTTStateOptions);
```
### `getSupportedLanguages`
[Section titled âgetSupportedLanguagesâ](#getsupportedlanguages)
Gets the locales supported by the underlying recognizer.
Android 13+ devices no longer expose this list; in that case `languages` is empty.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.getSupportedLanguages();
```
### `isListening`
[Section titled âisListeningâ](#islistening)
Returns whether the plugin is actively listening for speech.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.isListening();
```
### `checkPermissions`
[Section titled âcheckPermissionsâ](#checkpermissions)
Gets the current permission state.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.checkPermissions();
```
### `requestPermissions`
[Section titled ârequestPermissionsâ](#requestpermissions)
Requests the microphone + speech recognition permissions.
```typescript
import { SpeechRecognition } from '@capgo/capacitor-speech-recognition';
await SpeechRecognition.requestPermissions();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `SpeechRecognitionAvailability`
[Section titled âSpeechRecognitionAvailabilityâ](#speechrecognitionavailability)
```typescript
export interface SpeechRecognitionAvailability {
available: boolean;
}
```
### `SpeechRecognitionStartOptions`
[Section titled âSpeechRecognitionStartOptionsâ](#speechrecognitionstartoptions)
Configure how the recognizer behaves when calling .
```typescript
export interface SpeechRecognitionStartOptions {
/**
* Locale identifier such as `en-US`. When omitted the device language is used.
*/
language?: string;
/**
* Maximum number of final matches returned by native APIs. Defaults to `5`.
*/
maxResults?: number;
/**
* Prompt message shown inside the Android system dialog (ignored on iOS).
*/
prompt?: string;
/**
* When `true`, Android shows the OS speech dialog instead of running inline recognition.
* Defaults to `false`.
*/
popup?: boolean;
/**
* Emits partial transcription updates through the `partialResults` listener while audio is captured.
*/
partialResults?: boolean;
/**
* Enables native punctuation handling where supported (iOS 16+).
*/
addPunctuation?: boolean;
/**
* Opt in to the platform's newer on-device recognition path when available.
*
* On iOS 26+, this uses Apple's `SpeechAnalyzer` / `SpeechTranscriber` pipeline.
* On recent Android versions, this uses the on-device `SpeechRecognizer` path.
*
* It is intentionally opt-in so existing apps keep the legacy flow unless they choose
* to roll out the new behavior.
*
* Use {@link SpeechRecognitionPlugin.isOnDeviceRecognitionAvailable} before enabling it in production.
*
* Platform SDK docs:
* iOS: [Speech](https://developer.apple.com/documentation/speech),
* [SpeechAnalyzer](https://developer.apple.com/documentation/speech/speechanalyzer),
* [SpeechTranscriber](https://developer.apple.com/documentation/speech/speechtranscriber)
* Android: [SpeechRecognizer](https://developer.android.com/reference/android/speech/SpeechRecognizer)
*
* Defaults to `false`.
*/
useOnDeviceRecognition?: boolean;
/**
* Allow a number of milliseconds of silence before splitting the recognition session into segments.
* Required to be greater than zero and currently supported on Android only.
*/
allowForSilence?: number;
/**
* EXPERIMENTAL: Keep a PTT session alive across silence by restarting recognition while the button stays held.
*
* This restart behavior is implemented for Android inline recognition and iOS native recognition.
*/
continuousPTT?: boolean;
}
```
### `SpeechRecognitionMatches`
[Section titled âSpeechRecognitionMatchesâ](#speechrecognitionmatches)
```typescript
export interface SpeechRecognitionMatches {
matches?: string[];
}
```
### `ForceStopOptions`
[Section titled âForceStopOptionsâ](#forcestopoptions)
Options for .
```typescript
export interface ForceStopOptions {
/**
* Android only: timeout in milliseconds before forcing stop via destroy/recreate.
*
* On iOS, the current session is stopped immediately and this value is ignored.
*
* Defaults to `1500`.
*/
timeout?: number;
}
```
### `LastPartialResult`
[Section titled âLastPartialResultâ](#lastpartialresult)
Result from .
```typescript
export interface LastPartialResult {
/**
* Whether a partial result is currently cached.
*/
available: boolean;
/**
* The most recent transcript text known to the native recognizer.
*/
text: string;
/**
* All current match alternatives when available.
*/
matches?: string[];
}
```
### `PTTStateOptions`
[Section titled âPTTStateOptionsâ](#pttstateoptions)
Options for .
```typescript
export interface PTTStateOptions {
/**
* Whether the PTT button is currently held.
*/
held: boolean;
}
```
### `SpeechRecognitionLanguages`
[Section titled âSpeechRecognitionLanguagesâ](#speechrecognitionlanguages)
```typescript
export interface SpeechRecognitionLanguages {
languages: string[];
}
```
### `SpeechRecognitionListening`
[Section titled âSpeechRecognitionListeningâ](#speechrecognitionlistening)
```typescript
export interface SpeechRecognitionListening {
listening: boolean;
}
```
### `SpeechRecognitionPermissionStatus`
[Section titled âSpeechRecognitionPermissionStatusâ](#speechrecognitionpermissionstatus)
Permission map returned by `checkPermissions` and `requestPermissions`.
```typescript
export interface SpeechRecognitionPermissionStatus {
speechRecognition: PermissionState;
}
```
### `SpeechRecognitionSegmentResultEvent`
[Section titled âSpeechRecognitionSegmentResultEventâ](#speechrecognitionsegmentresultevent)
Raised whenever a segmented result is produced (Android only).
```typescript
export interface SpeechRecognitionSegmentResultEvent {
matches: string[];
}
```
### `SpeechRecognitionPartialResultEvent`
[Section titled âSpeechRecognitionPartialResultEventâ](#speechrecognitionpartialresultevent)
Raised whenever a partial transcription is produced.
```typescript
export interface SpeechRecognitionPartialResultEvent {
/**
* Current recognition matches when the native recognizer reports them.
*
* This can be omitted for forced or accumulated-only payloads.
*/
matches?: string[];
/**
* Accumulated transcription from earlier continuous PTT cycles.
*/
accumulated?: string;
/**
* Final accumulated text including the current result.
*/
accumulatedText?: string;
/**
* `true` when the plugin is restarting recognition inside a continuous PTT session.
*/
isRestarting?: boolean;
/**
* `true` when the payload was emitted by `forceStop()`.
*/
forced?: boolean;
}
```
### `SpeechRecognitionListeningEvent`
[Section titled âSpeechRecognitionListeningEventâ](#speechrecognitionlisteningevent)
Raised when the listening state changes.
```typescript
export interface SpeechRecognitionListeningEvent {
/**
* Finite state of the recognition session.
*/
state?: ListeningFiniteState;
/**
* Unique identifier for the current listening session.
*/
sessionId?: number;
/**
* Why this state transition occurred.
*/
reason?: ListeningReason;
/**
* Error code when the transition is caused by an error.
*/
errorCode?: string;
/**
* Backward-compatible binary state used by earlier releases.
*/
status?: 'started' | 'stopped';
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-speech-recognition](/plugins/capacitor-speech-recognition/) for the native capability in Using @capgo/capacitor-speech-recognition, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-speech-synthesis
> Speech Synthesis Plugin for synthesizing speech from text.
## Overview
[Section titled âOverviewâ](#overview)
Speech Synthesis Plugin for synthesizing speech from text.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `speak` - Speaks the given text with specified options. The utterance is added to the speech queue.
* `synthesizeToFile` - Synthesizes speech to an audio file (Android/iOS only). Returns the file path where the audio was saved.
* `cancel` - Cancels all queued utterances and stops current speech.
* `pause` - Pauses speech immediately.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `speak` | Speaks the given text with specified options. The utterance is added to the speech queue. |
| `synthesizeToFile` | Synthesizes speech to an audio file (Android/iOS only). Returns the file path where the audio was saved. |
| `cancel` | Cancels all queued utterances and stops current speech. |
| `pause` | Pauses speech immediately. |
| `resume` | Resumes paused speech. |
| `isSpeaking` | Checks if speech synthesis is currently speaking. |
| `isAvailable` | Checks if speech synthesis is available on the device. |
| `getVoices` | Gets all available voices. |
| `getLanguages` | Gets all available languages. |
| `isLanguageAvailable` | Checks if a specific language is available. |
| `isVoiceAvailable` | Checks if a specific voice is available. |
| `initialize` | Initializes the speech synthesis engine (iOS optimization). This can reduce latency for the first speech request. |
| `activateAudioSession` | Activates the audio session with a specific category (iOS only). |
| `deactivateAudioSession` | Deactivates the audio session (iOS only). |
| `getPluginVersion` | Gets the native plugin version. |
| `addListener` | Listens for when an utterance starts speaking. |
| `addListener` | Listens for when an utterance finishes speaking. |
| `addListener` | Listens for word boundaries during speech. |
| `addListener` | Listens for synthesis errors. |
| `removeAllListeners` | Removes all event listeners. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-speech-synthesis](https://github.com/Cap-go/capacitor-speech-synthesis/).
## Keep going from @capgo/capacitor-speech-synthesis
[Section titled âKeep going from @capgo/capacitor-speech-synthesisâ](#keep-going-from-capgocapacitor-speech-synthesis)
If you are using **@capgo/capacitor-speech-synthesis** to plan native plugin work, connect it with [Using @capgo/capacitor-speech-synthesis](/plugins/capacitor-speech-synthesis/) for the native capability in Using @capgo/capacitor-speech-synthesis, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-speech-synthesis and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-speech-synthesis` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-speech-synthesis
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `speak`
[Section titled âspeakâ](#speak)
Speaks the given text with specified options. The utterance is added to the speech queue.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const result = await SpeechSynthesis.speak({
text: 'Hello, world!',
language: 'en-US',
rate: 1.0,
pitch: 1.0,
volume: 1.0,
queueStrategy: 'Add'
});
console.log('Utterance ID:', result.utteranceId);
```
### `synthesizeToFile`
[Section titled âsynthesizeToFileâ](#synthesizetofile)
Synthesizes speech to an audio file (Android/iOS only). Returns the file path where the audio was saved.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const result = await SpeechSynthesis.synthesizeToFile({
text: 'Hello, world!',
language: 'en-US'
});
console.log('Audio file saved at:', result.filePath);
```
### `cancel`
[Section titled âcancelâ](#cancel)
Cancels all queued utterances and stops current speech.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
await SpeechSynthesis.cancel();
```
### `pause`
[Section titled âpauseâ](#pause)
Pauses speech immediately.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
await SpeechSynthesis.pause();
```
### `resume`
[Section titled âresumeâ](#resume)
Resumes paused speech.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
await SpeechSynthesis.resume();
```
### `isSpeaking`
[Section titled âisSpeakingâ](#isspeaking)
Checks if speech synthesis is currently speaking.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const { isSpeaking } = await SpeechSynthesis.isSpeaking();
console.log('Is speaking:', isSpeaking);
```
### `isAvailable`
[Section titled âisAvailableâ](#isavailable)
Checks if speech synthesis is available on the device.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const { isAvailable } = await SpeechSynthesis.isAvailable();
if (isAvailable) {
console.log('Speech synthesis is available');
}
```
### `getVoices`
[Section titled âgetVoicesâ](#getvoices)
Gets all available voices.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const { voices } = await SpeechSynthesis.getVoices();
voices.forEach(voice => {
console.log(`${voice.name} (${voice.language})`);
});
```
### `getLanguages`
[Section titled âgetLanguagesâ](#getlanguages)
Gets all available languages.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const { languages } = await SpeechSynthesis.getLanguages();
console.log('Available languages:', languages);
```
### `isLanguageAvailable`
[Section titled âisLanguageAvailableâ](#islanguageavailable)
Checks if a specific language is available.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const { isAvailable } = await SpeechSynthesis.isLanguageAvailable({
language: 'es-ES'
});
console.log('Spanish available:', isAvailable);
```
### `isVoiceAvailable`
[Section titled âisVoiceAvailableâ](#isvoiceavailable)
Checks if a specific voice is available.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
const { isAvailable } = await SpeechSynthesis.isVoiceAvailable({
voiceId: 'com.apple.ttsbundle.Samantha-compact'
});
console.log('Voice available:', isAvailable);
```
### `initialize`
[Section titled âinitializeâ](#initialize)
Initializes the speech synthesis engine (iOS optimization). This can reduce latency for the first speech request.
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
await SpeechSynthesis.initialize();
```
### `activateAudioSession`
[Section titled âactivateAudioSessionâ](#activateaudiosession)
Activates the audio session with a specific category (iOS only).
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
await SpeechSynthesis.activateAudioSession({
category: 'Playback'
});
```
### `deactivateAudioSession`
[Section titled âdeactivateAudioSessionâ](#deactivateaudiosession)
Deactivates the audio session (iOS only).
```typescript
import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis';
await SpeechSynthesis.deactivateAudioSession();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `SpeakOptions`
[Section titled âSpeakOptionsâ](#speakoptions)
Options for speaking text.
```typescript
export interface SpeakOptions {
/**
* The text to speak.
*
* @since 1.0.0
*/
text: string;
/**
* The BCP-47 language tag (e.g., 'en-US', 'es-ES').
*
* @since 1.0.0
*/
language?: string;
/**
* The voice identifier to use.
*
* @since 1.0.0
*/
voiceId?: string;
/**
* The pitch of the voice (0.5 to 2.0, default: 1.0).
*
* @since 1.0.0
*/
pitch?: number;
/**
* The speaking rate (0.1 to 10.0, default: 1.0).
*
* @since 1.0.0
*/
rate?: number;
/**
* The volume (0.0 to 1.0, default: 1.0).
*
* @since 1.0.0
*/
volume?: number;
/**
* The queue strategy: 'Add' to append or 'Flush' to replace queue.
* Default: 'Add'
*
* @since 1.0.0
*/
queueStrategy?: 'Add' | 'Flush';
}
```
### `SpeakResult`
[Section titled âSpeakResultâ](#speakresult)
Result from speaking text.
```typescript
export interface SpeakResult {
/**
* Unique identifier for this utterance.
*
* @since 1.0.0
*/
utteranceId: string;
}
```
### `SynthesizeToFileResult`
[Section titled âSynthesizeToFileResultâ](#synthesizetofileresult)
Result from synthesizing to file.
```typescript
export interface SynthesizeToFileResult {
/**
* The file path where audio was saved.
*
* @since 1.0.0
*/
filePath: string;
/**
* Unique identifier for this utterance.
*
* @since 1.0.0
*/
utteranceId: string;
}
```
### `VoiceInfo`
[Section titled âVoiceInfoâ](#voiceinfo)
Information about a voice.
```typescript
export interface VoiceInfo {
/**
* Unique voice identifier.
*
* @since 1.0.0
*/
id: string;
/**
* Display name of the voice.
*
* @since 1.0.0
*/
name: string;
/**
* BCP-47 language code.
*
* @since 1.0.0
*/
language: string;
/**
* Gender of the voice (iOS only).
*
* @since 1.0.0
*/
gender?: 'male' | 'female' | 'neutral';
/**
* Whether this voice requires a network connection.
*
* @since 1.0.0
*/
isNetworkConnectionRequired?: boolean;
/**
* Whether this is the default voice (Web only).
*
* @since 1.0.0
*/
default?: boolean;
}
```
### `IsLanguageAvailableOptions`
[Section titled âIsLanguageAvailableOptionsâ](#islanguageavailableoptions)
Options for checking language availability.
```typescript
export interface IsLanguageAvailableOptions {
/**
* The BCP-47 language code to check.
*
* @since 1.0.0
*/
language: string;
}
```
### `IsVoiceAvailableOptions`
[Section titled âIsVoiceAvailableOptionsâ](#isvoiceavailableoptions)
Options for checking voice availability.
```typescript
export interface IsVoiceAvailableOptions {
/**
* The voice ID to check.
*
* @since 1.0.0
*/
voiceId: string;
}
```
### `ActivateAudioSessionOptions`
[Section titled âActivateAudioSessionOptionsâ](#activateaudiosessionoptions)
Options for activating the audio session (iOS only).
```typescript
export interface ActivateAudioSessionOptions {
/**
* The audio session category.
* - 'Ambient': Mixes with other audio
* - 'Playback': Stops other audio
*
* @since 1.0.0
*/
category: 'Ambient' | 'Playback';
}
```
### `UtteranceEvent`
[Section titled âUtteranceEventâ](#utteranceevent)
Event emitted when utterance starts or ends.
```typescript
export interface UtteranceEvent {
/**
* The utterance identifier.
*
* @since 1.0.0
*/
utteranceId: string;
}
```
### `BoundaryEvent`
[Section titled âBoundaryEventâ](#boundaryevent)
Event emitted at word boundaries.
```typescript
export interface BoundaryEvent {
/**
* The utterance identifier.
*
* @since 1.0.0
*/
utteranceId: string;
/**
* The character index in the text.
*
* @since 1.0.0
*/
charIndex: number;
/**
* The character length of the current word.
*
* @since 1.0.0
*/
charLength?: number;
}
```
### `ErrorEvent`
[Section titled âErrorEventâ](#errorevent)
Event emitted on synthesis error.
```typescript
export interface ErrorEvent {
/**
* The utterance identifier.
*
* @since 1.0.0
*/
utteranceId: string;
/**
* The error message.
*
* @since 1.0.0
*/
error: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-speech-synthesis](/plugins/capacitor-speech-synthesis/) for the native capability in Using @capgo/capacitor-speech-synthesis, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-ssl-pinning
> Capacitor API for inspecting SSL pinning configuration.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor API for inspecting SSL pinning configuration.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `getConfiguration` - Returns the active native configuration visible to the plugin.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | -------------------------------------------------------------- |
| `getConfiguration` | Returns the active native configuration visible to the plugin. |
| `getPluginVersion` | Returns the native implementation version marker. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-ssl-pinning](https://github.com/Cap-go/capacitor-ssl-pinning/).
## Keep going from @capgo/capacitor-ssl-pinning
[Section titled âKeep going from @capgo/capacitor-ssl-pinningâ](#keep-going-from-capgocapacitor-ssl-pinning)
If you are using **@capgo/capacitor-ssl-pinning** to plan security and compliance, connect it with [Using @capgo/capacitor-ssl-pinning](/plugins/capacitor-ssl-pinning/) for the native capability in Using @capgo/capacitor-ssl-pinning, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# Getting Started
> Install @capgo/capacitor-ssl-pinning and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-ssl-pinning` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-ssl-pinning
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { SSLPinning } from '@capgo/capacitor-ssl-pinning';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `getConfiguration`
[Section titled âgetConfigurationâ](#getconfiguration)
Returns the active native configuration visible to the plugin.
```typescript
import { SSLPinning } from '@capgo/capacitor-ssl-pinning';
await SSLPinning.getConfiguration();
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `SSLPinningConfigurationState`
[Section titled âSSLPinningConfigurationStateâ](#sslpinningconfigurationstate)
Static SSL pinning configuration currently visible to the plugin.
```typescript
export interface SSLPinningConfigurationState {
/**
* Whether at least one certificate is configured for native pinning.
*/
configured: boolean;
/**
* Certificate paths from `capacitor.config.*` relative to the app root.
*/
certs: string[];
/**
* Fully-qualified URLs that should bypass SSL pinning.
*/
excludedDomains: string[];
}
```
### `PluginVersionResult`
[Section titled âPluginVersionResultâ](#pluginversionresult)
Plugin version payload.
```typescript
export interface PluginVersionResult {
/**
* Version identifier returned by the platform implementation.
*/
version: string;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan security and compliance, connect it with [Using @capgo/capacitor-ssl-pinning](/plugins/capacitor-ssl-pinning/) for the native capability in Using @capgo/capacitor-ssl-pinning, [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, and [Capgo Security](/security/) for the product workflow in Capgo Security.
# @capgo/capacitor-stream-call
> Uses the https://getstream.io/ SDK to implement calling in Capacitor.
## Overview
[Section titled âOverviewâ](#overview)
Uses the SDK to implement calling in Capacitor.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `login` - Login to Stream Video service.
* `logout` - Logout from Stream Video service.
* `call` - Initiate a call to another user.
* `endCall` - End the current call.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `login` | Login to Stream Video service. |
| `logout` | Logout from Stream Video service. |
| `call` | Initiate a call to another user. |
| `endCall` | End the current call. |
| `joinCall` | Join an existing call. |
| `setMicrophoneEnabled` | Enable or disable microphone. |
| `setCameraEnabled` | Enable or disable camera. |
| `addListener` | Add listener for call events. |
| `addListener` | Listen for lock-screen incoming call (Android only). Fired when the app is shown by full-screen intent before user interaction. |
| `removeAllListeners` | Remove all event listeners. |
| `enableBluetooth` | Enable bluetooth audio. |
| `acceptCall` | Accept an incoming call. |
| `rejectCall` | Reject an incoming call. |
| `isCameraEnabled` | Check if camera is enabled. |
| `getCallStatus` | Get the current call status. |
| `getRingingCall` | Get the current ringing call. |
| `toggleViews` | Cycle through the available video layouts. |
| `setSpeaker` | Set speakerphone on. |
| `switchCamera` | Switch camera. |
| `getCallInfo` | Get detailed information about an active call including caller details. |
| `setDynamicStreamVideoApikey` | Set a dynamic Stream Video API key that overrides the static one. |
| `getDynamicStreamVideoApikey` | Get the currently set dynamic Stream Video API key. |
| `getCurrentUser` | Get the current userâs information. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-streamcall](https://github.com/Cap-go/capacitor-streamcall/).
## Keep going from @capgo/capacitor-stream-call
[Section titled âKeep going from @capgo/capacitor-stream-callâ](#keep-going-from-capgocapacitor-stream-call)
If you are using **@capgo/capacitor-stream-call** to plan native plugin work, connect it with [Using @capgo/capacitor-stream-call](/plugins/capacitor-streamcall/) for the native capability in Using @capgo/capacitor-stream-call, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-stream-call and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-stream-call` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-stream-call
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `login`
[Section titled âloginâ](#login)
Login to Stream Video service
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.login({
token: 'your-token',
userId: 'user-123',
name: 'John Doe',
apiKey: 'your-api-key'
});
```
### `logout`
[Section titled âlogoutâ](#logout)
Logout from Stream Video service
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.logout();
```
### `call`
[Section titled âcallâ](#call)
Initiate a call to another user
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.call({
userId: 'user-456',
type: 'video',
ring: true
});
```
### `endCall`
[Section titled âendCallâ](#endcall)
End the current call
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.endCall();
```
### `joinCall`
[Section titled âjoinCallâ](#joincall)
Join an existing call
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.joinCall({ callId: 'call001', callType: 'default' });
```
### `setMicrophoneEnabled`
[Section titled âsetMicrophoneEnabledâ](#setmicrophoneenabled)
Enable or disable microphone
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.setMicrophoneEnabled({ enabled: false });
```
### `setCameraEnabled`
[Section titled âsetCameraEnabledâ](#setcameraenabled)
Enable or disable camera
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.setCameraEnabled({ enabled: false });
```
### `enableBluetooth`
[Section titled âenableBluetoothâ](#enablebluetooth)
Enable bluetooth audio
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.enableBluetooth();
```
### `acceptCall`
[Section titled âacceptCallâ](#acceptcall)
Accept an incoming call
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.acceptCall();
```
### `rejectCall`
[Section titled ârejectCallâ](#rejectcall)
Reject an incoming call
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.rejectCall();
```
### `isCameraEnabled`
[Section titled âisCameraEnabledâ](#iscameraenabled)
Check if camera is enabled
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
const isCameraEnabled = await StreamCall.isCameraEnabled();
console.log(isCameraEnabled);
```
### `getCallStatus`
[Section titled âgetCallStatusâ](#getcallstatus)
Get the current call status
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
const callStatus = await StreamCall.getCallStatus();
console.log(callStatus);
```
### `getRingingCall`
[Section titled âgetRingingCallâ](#getringingcall)
Get the current ringing call
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
const ringingCall = await StreamCall.getRingingCall();
console.log(ringingCall);
```
### `toggleViews`
[Section titled âtoggleViewsâ](#toggleviews)
Cycle through the available video layouts
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
const { newLayout } = await StreamCall.toggleViews();
console.log(`Layout switched to ${newLayout}`);
```
### `setSpeaker`
[Section titled âsetSpeakerâ](#setspeaker)
Set speakerphone on
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.setSpeaker({ name: 'speaker' });
```
### `switchCamera`
[Section titled âswitchCameraâ](#switchcamera)
Switch camera
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.switchCamera({ camera: 'back' });
```
### `getCallInfo`
[Section titled âgetCallInfoâ](#getcallinfo)
Get detailed information about an active call including caller details
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.getCallInfo({} as { callId: string });
```
### `setDynamicStreamVideoApikey`
[Section titled âsetDynamicStreamVideoApikeyâ](#setdynamicstreamvideoapikey)
Set a dynamic Stream Video API key that overrides the static one
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
await StreamCall.setDynamicStreamVideoApikey({ apiKey: 'new-api-key' });
```
### `getDynamicStreamVideoApikey`
[Section titled âgetDynamicStreamVideoApikeyâ](#getdynamicstreamvideoapikey)
Get the currently set dynamic Stream Video API key
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
const result = await StreamCall.getDynamicStreamVideoApikey();
if (result.hasDynamicKey) {
console.log('Dynamic API key:', result.apiKey);
} else {
console.log('Using static API key from resources');
}
```
### `getCurrentUser`
[Section titled âgetCurrentUserâ](#getcurrentuser)
Get the current userâs information
```typescript
import { StreamCall } from '@capgo/capacitor-stream-call';
const currentUser = await StreamCall.getCurrentUser();
console.log(currentUser);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `LoginOptions`
[Section titled âLoginOptionsâ](#loginoptions)
```typescript
export interface LoginOptions {
/** Stream Video API token */
token: string;
/** User ID for the current user */
userId: string;
/** Display name for the current user */
name: string;
/** Optional avatar URL for the current user */
imageURL?: string;
/** Stream Video API key */
apiKey: string;
/** ID of the HTML element where the video will be rendered */
magicDivId?: string;
pushNotificationsConfig?: PushNotificationsConfig;
}
```
### `SuccessResponse`
[Section titled âSuccessResponseâ](#successresponse)
```typescript
export interface SuccessResponse {
/** Whether the operation was successful */
success: boolean;
callId?: string;
}
```
### `CallOptions`
[Section titled âCallOptionsâ](#calloptions)
```typescript
export interface CallOptions {
/** User ID of the person to call */
userIds: string[];
/** Type of call, defaults to 'default' */
type?: CallType;
/** Whether to ring the other user, defaults to true */
ring?: boolean;
/** Team name to call */
team?: string;
/** Whether to start the call with video enabled, defaults to false */
video?: boolean;
/** Custom data to be passed to the call */
custom?: Record<
string,
| string
| boolean
| number
| null
| Record
| string[]
| boolean[]
| number[]
>;
}
```
### `CallEvent`
[Section titled âCallEventâ](#callevent)
```typescript
export interface CallEvent {
/** ID of the call */
callId: string;
/** Current state of the call */
state: CallState;
/** User ID of the participant in the call who triggered the event */
userId?: string;
/** Reason for the call state change, if applicable */
reason?: string;
/** Information about the caller (for incoming calls) */
caller?: CallMember;
/** List of call members */
members?: CallMember[];
custom?: Record<
string,
| string
| boolean
| number
| null
| Record
| string[]
| boolean[]
| number[]
>;
count?: number;
}
```
### `IncomingCallPayload`
[Section titled âIncomingCallPayloadâ](#incomingcallpayload)
```typescript
export interface IncomingCallPayload {
/** Full call CID (e.g. default:123) */
cid: string;
/** Event type (currently always "incoming") */
type: 'incoming';
/** Information about the caller */
caller?: CallMember;
/** Custom data to be passed to the call */
custom?: Record<
string,
| string
| boolean
| number
| null
| Record
| string[]
| boolean[]
| number[]
>;
/**
* Get the native Capacitor plugin version
*
* @returns {Promise<{ id: string }>} an Promise with version for this device
* @throws An error if the something went wrong
*/
getPluginVersion(): Promise<{ version: string }>;
}
```
### `CameraEnabledResponse`
[Section titled âCameraEnabledResponseâ](#cameraenabledresponse)
```typescript
export interface CameraEnabledResponse {
enabled: boolean;
}
```
### `StreamCallLayout`
[Section titled âStreamCallLayoutâ](#streamcalllayout)
```typescript
export type StreamCallLayout = 'grid' | 'spotlight' | 'dynamic' | 'fullScreen' | 'fullscreen';
```
### `DynamicApiKeyResponse`
[Section titled âDynamicApiKeyResponseâ](#dynamicapikeyresponse)
```typescript
export interface DynamicApiKeyResponse {
/** The dynamic API key if set, null if not */
apiKey: string | null;
/** Whether a dynamic key is currently set */
hasDynamicKey: boolean;
}
```
### `CurrentUserResponse`
[Section titled âCurrentUserResponseâ](#currentuserresponse)
```typescript
export interface CurrentUserResponse {
/** User ID of the current user */
userId: string;
/** Display name of the current user */
name: string;
/** Avatar URL of the current user */
imageURL?: string;
/** Whether the user is currently logged in */
isLoggedIn: boolean;
}
```
### `PushNotificationsConfig`
[Section titled âPushNotificationsConfigâ](#pushnotificationsconfig)
```typescript
export interface PushNotificationsConfig {
pushProviderName: string;
voipProviderName: string;
}
```
### `CallType`
[Section titled âCallTypeâ](#calltype)
```typescript
export type CallType = 'default' | 'audio' | 'audio_room' | 'livestream' | 'development';
```
### `CallState`
[Section titled âCallStateâ](#callstate)
```typescript
export type CallState =
// User-facing states
| 'idle'
| 'ringing'
| 'joining'
| 'reconnecting'
| 'joined'
| 'leaving'
| 'left'
// Event-specific states
| 'created'
| 'session_started'
| 'rejected'
| 'participant_counts'
| 'missed'
| 'accepted'
| 'ended'
| 'camera_enabled'
| 'camera_disabled'
| 'speaker_enabled'
| 'speaker_disabled'
| 'microphone_enabled'
| 'microphone_disabled'
| 'outgoing_call_ended'
| 'unknown';
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-stream-call](/plugins/capacitor-streamcall/) for the native capability in Using @capgo/capacitor-stream-call, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-stripe-identity
> Capacitor plugin for Stripe Identity verification.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for Stripe Identity verification.
## Why Capgo?
[Section titled âWhy Capgo?â](#why-capgo)
These plugins are maintained forks of [@capacitor-community/stripe](https://github.com/capacitor-community/stripe). Capgo split the community project into focused packages with docs, example apps, and CI for each Stripe surface.
Capgo tracks open issues and pull requests in the community repository, ports relevant fixes into our repos, and ships them on current Stripe SDKs. We aim to be more reactive than the community maintainers when bugs or platform updates land.
For document and selfie verification, `@capgo/capacitor-stripe-identity` is the maintained upgrade path from the community package.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Load Stripe with your publishable key.
* `create` - Create a verification sheet from your backend session.
* `present` - Present the Identity verification flow.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------ |
| `initialize` | Load Stripe with your publishable key. |
| `create` | Create a verification sheet from your backend session. |
| `present` | Present the Identity verification flow. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-stripe-identity](https://github.com/Cap-go/capacitor-stripe-identity/).
## Related plugins
[Section titled âRelated pluginsâ](#related-plugins)
* [@capgo/native-purchases](/docs/plugins/native-purchases/) for App Store and Play Store subscriptions
* [@capgo/capacitor-pay](/docs/plugins/pay/) for wallet-only Apple Pay / Google Pay without Stripe Payment Sheet
# Getting Started
> Install @capgo/capacitor-stripe-identity and start using its Capacitor API.
## Install
[Section titled âInstallâ](#install)
```bash
bun add @capgo/capacitor-stripe-identity
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { StripeIdentity } from '@capgo/capacitor-stripe-identity';
```
## Verification flow
[Section titled âVerification flowâ](#verification-flow)
Create a VerificationSession on your backend, then pass the IDs to the plugin:
```typescript
await StripeIdentity.initialize({ publishableKey: 'pk_test_...' });
await StripeIdentity.create({
verificationId: 'vs_...',
ephemeralKeySecret: 'ek_...',
clientSecret: 'vs_..._secret_...', // web only
});
await StripeIdentity.present();
```
## Example app
[Section titled âExample appâ](#example-app)
```bash
git clone https://github.com/Cap-go/capacitor-stripe-identity.git
cd capacitor-stripe-identity/example-app
bun install
bun run start
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page documents the current public API from [Cap-go/capacitor-stripe-identity](https://github.com/Cap-go/capacitor-stripe-identity/).
# @capgo/capacitor-stripe-pay
> Capacitor plugin for Stripe Payment Sheet, Apple Pay, and Google Pay.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for Stripe Payment Sheet, Apple Pay, and Google Pay.
## Why Capgo?
[Section titled âWhy Capgo?â](#why-capgo)
These plugins are maintained forks of [@capacitor-community/stripe](https://github.com/capacitor-community/stripe). Capgo split the community project into focused packages with docs, example apps, and CI for each Stripe surface.
Capgo tracks open issues and pull requests in the community repository, ports relevant fixes into our repos, and ships them on current Stripe SDKs. We aim to be more reactive than the community maintainers when bugs or platform updates land.
For Payment Sheet, Apple Pay, and Google Pay, `@capgo/capacitor-stripe-pay` is the maintained upgrade path from the community package.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Set the Stripe publishable key and optional Connect account.
* `createPaymentSheet` - Create a Payment Sheet from your backend client secret.
* `presentPaymentSheet` - Present the native Stripe Payment Sheet.
* `updateApplePaySheet` - Update Apple Pay line items after shipping contact selection.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| --------------------- | ------------------------------------------------------------- |
| `initialize` | Set the Stripe publishable key and optional Connect account. |
| `createPaymentSheet` | Create a Payment Sheet from your backend client secret. |
| `presentPaymentSheet` | Present the native Stripe Payment Sheet. |
| `updateApplePaySheet` | Update Apple Pay line items after shipping contact selection. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-stripe-pay](https://github.com/Cap-go/capacitor-stripe-pay/).
## Related plugins
[Section titled âRelated pluginsâ](#related-plugins)
* [@capgo/native-purchases](/docs/plugins/native-purchases/) for App Store and Play Store subscriptions
* [@capgo/capacitor-pay](/docs/plugins/pay/) for wallet-only Apple Pay / Google Pay without Stripe Payment Sheet
# Getting Started
> Install @capgo/capacitor-stripe-pay and start using its Capacitor API.
## Install
[Section titled âInstallâ](#install)
```bash
bun add @capgo/capacitor-stripe-pay
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { Stripe } from '@capgo/capacitor-stripe-pay';
```
## Subscriptions and in-app purchases
[Section titled âSubscriptions and in-app purchasesâ](#subscriptions-and-in-app-purchases)
For App Store / Play Store subscriptions and entitlements, use [@capgo/native-purchases](/docs/plugins/native-purchases/) instead of this plugin.
## Payment Sheet flow
[Section titled âPayment Sheet flowâ](#payment-sheet-flow)
```typescript
await Stripe.initialize({ publishableKey: 'pk_test_...' });
await Stripe.createPaymentSheet({
paymentIntentClientSecret: 'pi_..._secret_...',
merchantDisplayName: 'My Store',
});
const result = await Stripe.presentPaymentSheet();
```
## Direct Charges (Stripe Connect)
[Section titled âDirect Charges (Stripe Connect)â](#direct-charges-stripe-connect)
Pass `stripeAccount` on `initialize()` or per `createPaymentSheet()` when creating PaymentIntents on a connected account.
## Example app
[Section titled âExample appâ](#example-app)
```bash
git clone https://github.com/Cap-go/capacitor-stripe-pay.git
cd capacitor-stripe-pay/example-app
bun install
bun run start
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page documents the current public API from [Cap-go/capacitor-stripe-pay](https://github.com/Cap-go/capacitor-stripe-pay/).
# @capgo/capacitor-stripe-terminal
> Capacitor plugin for Stripe Terminal in-person payments.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor plugin for Stripe Terminal in-person payments.
## Why Capgo?
[Section titled âWhy Capgo?â](#why-capgo)
These plugins are maintained forks of [@capacitor-community/stripe](https://github.com/capacitor-community/stripe). Capgo split the community project into focused packages with docs, example apps, and CI for each Stripe surface.
Capgo tracks open issues and pull requests in the community repository, ports relevant fixes into our repos, and ships them on current Stripe SDKs. We aim to be more reactive than the community maintainers when bugs or platform updates land.
For in-person payments with Tap to Pay and physical readers, `@capgo/capacitor-stripe-terminal` is the maintained upgrade path from the community package.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `initialize` - Initialize Terminal with a connection token provider endpoint.
* `discoverReaders` - Discover Tap to Pay, Bluetooth, or Internet readers.
* `cancelDiscoverReaders` - Cancel an in-flight reader discovery session.
* `connectReader` - Connect to a discovered reader.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ----------------------- | -------------------------------------------------------------- |
| `initialize` | Initialize Terminal with a connection token provider endpoint. |
| `discoverReaders` | Discover Tap to Pay, Bluetooth, or Internet readers. |
| `cancelDiscoverReaders` | Cancel an in-flight reader discovery session. |
| `connectReader` | Connect to a discovered reader. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Reference Values
[Section titled âReference Valuesâ](#reference-values)
Use these values with `TapToPayUxConfiguration.darkMode` when configuring Tap to Pay.
### Tap To Pay Dark Mode
[Section titled âTap To Pay Dark Modeâ](#tap-to-pay-dark-mode)
| Value | User will find |
| -------- | ------------------------------------------------ |
| `SYSTEM` | Match the device system appearance. |
| `DARK` | Force the Tap to Pay flow into dark appearance. |
| `LIGHT` | Force the Tap to Pay flow into light appearance. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-stripe-terminal](https://github.com/Cap-go/capacitor-stripe-terminal/).
## Related plugins
[Section titled âRelated pluginsâ](#related-plugins)
* [@capgo/native-purchases](/docs/plugins/native-purchases/) for App Store and Play Store subscriptions
* [@capgo/capacitor-pay](/docs/plugins/pay/) for wallet-only Apple Pay / Google Pay without Stripe Payment Sheet
# Getting Started
> Install @capgo/capacitor-stripe-terminal and start using its Capacitor API.
## Install
[Section titled âInstallâ](#install)
```bash
bun add @capgo/capacitor-stripe-terminal
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { StripeTerminal } from '@capgo/capacitor-stripe-terminal';
```
## Backend requirement
[Section titled âBackend requirementâ](#backend-requirement)
Stripe Terminal requires a secure backend endpoint that creates connection tokens. Pass that URL to `initialize()`.
```typescript
await StripeTerminal.initialize({
tokenProviderEndpoint: 'https://api.example.com/stripe/terminal/connection-token',
isTest: true,
});
await StripeTerminal.discoverReaders({ type: 'tap-to-pay' });
```
## Example app
[Section titled âExample appâ](#example-app)
```bash
git clone https://github.com/Cap-go/capacitor-stripe-terminal.git
cd capacitor-stripe-terminal/example-app
bun install
bun run start
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page documents the current public API from [Cap-go/capacitor-stripe-terminal](https://github.com/Cap-go/capacitor-stripe-terminal/).
# @capgo/capacitor-supabase
> Use native Supabase authentication in Capacitor while still pairing it with @supabase/supabase-js where it makes sense.
## Overview
[Section titled âOverviewâ](#overview)
`@capgo/capacitor-supabase` focuses on the parts where native Supabase SDKs give Capacitor apps real value: authentication flows, session persistence, token refresh, and direct JWT access.
Use the plugin for native auth and session management, then pass the native JWT to `@supabase/supabase-js` for Realtime, Storage, Edge Functions, and advanced querying.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* Native Supabase client initialization.
* Email/password sign-in and sign-up.
* OTP, magic link, OAuth, and session helpers.
* Native auth state listeners.
* JWT access for JavaScript or backend calls.
* Basic native `select`, `insert`, `update`, and `delete` helpers.
## Recommended Architecture
[Section titled âRecommended Architectureâ](#recommended-architecture)
| Concern | Recommended path |
| --------------------------------- | ------------------------------------------------ |
| Authentication | Use `@capgo/capacitor-supabase`. |
| Session persistence and refresh | Use `@capgo/capacitor-supabase`. |
| Advanced database queries | Use `@supabase/supabase-js` with the native JWT. |
| Realtime, Storage, Edge Functions | Use `@supabase/supabase-js`. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-supabase](https://github.com/Cap-go/capacitor-supabase/).
## Keep going from @capgo/capacitor-supabase
[Section titled âKeep going from @capgo/capacitor-supabaseâ](#keep-going-from-capgocapacitor-supabase)
If you are using **@capgo/capacitor-supabase** to plan security and compliance, connect it with [Encryption](/docs/live-updates/encryption/) for the implementation detail in Encryption, [Compliance](/docs/live-updates/compliance/) for the implementation detail in Compliance, [Capgo Security Scanner](/security-scanner/) for the product workflow in Capgo Security Scanner, [Capgo Security](/security/) for the product workflow in Capgo Security, and [Capgo Trust Center](/trust/) for the product workflow in Capgo Trust Center.
# Getting Started
> Install and initialize the native Supabase Capacitor plugin.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-supabase` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
npm install @capgo/capacitor-supabase
npx cap sync
```
## Android Minimum SDK
[Section titled âAndroid Minimum SDKâ](#android-minimum-sdk)
The Android implementation requires Android 8.0 or newer. Set `minSdkVersion = 26` in `android/variables.gradle`.
## Initialize The Client
[Section titled âInitialize The Clientâ](#initialize-the-client)
```ts
import { CapacitorSupabase } from '@capgo/capacitor-supabase';
await CapacitorSupabase.initialize({
supabaseUrl: 'https://your-project.supabase.co',
supabaseKey: 'your-anon-key',
});
```
## Sign In And Access The JWT
[Section titled âSign In And Access The JWTâ](#sign-in-and-access-the-jwt)
```ts
const { session, user } = await CapacitorSupabase.signInWithPassword({
email: 'user@example.com',
password: 'password123',
});
console.log('User', user?.id);
console.log('JWT available', Boolean(session?.accessToken));
```
## Listen For Auth Changes
[Section titled âListen For Auth Changesâ](#listen-for-auth-changes)
```ts
const listener = await CapacitorSupabase.addListener('authStateChange', ({ event, session }) => {
console.log('Auth event', event);
console.log('Current JWT available', Boolean(session?.accessToken));
});
await listener.remove();
```
## Pair Native Auth With supabase-js
[Section titled âPair Native Auth With supabase-jsâ](#pair-native-auth-with-supabase-js)
```ts
import { createClient } from '@supabase/supabase-js';
const { session } = await CapacitorSupabase.getSession();
const supabase = createClient('https://your-project.supabase.co', 'your-anon-key', {
global: {
headers: {
Authorization: `Bearer ${session?.accessToken}`,
},
},
});
const { data } = await supabase.from('table').select('*');
console.log(data);
```
## Native Database Helpers
[Section titled âNative Database Helpersâ](#native-database-helpers)
```ts
const { data, error } = await CapacitorSupabase.select({
table: 'users',
columns: 'id, name, email',
filter: { active: true },
limit: 10,
orderBy: 'created_at',
ascending: false,
});
console.log(data, error);
```
## Recommended Usage
[Section titled âRecommended Usageâ](#recommended-usage)
* Use this plugin for authentication and session management.
* Keep Realtime, Storage, Edge Functions, and advanced querying in `@supabase/supabase-js`.
* Pass the native JWT into the JavaScript client whenever you need the rest of the Supabase surface area.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native plugin work, connect it with [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives, and [Capgo Native Builds](/native-build/) for the product workflow in Capgo Native Builds.
# @capgo/capacitor-textinteraction
> Toggle text interaction in Capacitor based iOS apps.
## Overview
[Section titled âOverviewâ](#overview)
Toggle text interaction in Capacitor based iOS apps.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `toggle` - Toggle text interaction (selection) on the Capacitor WebView.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------ | ------------------------------------------------------------- |
| `toggle` | Toggle text interaction (selection) on the Capacitor WebView. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-textinteraction](https://github.com/Cap-go/capacitor-textinteraction/).
## Keep going from @capgo/capacitor-textinteraction
[Section titled âKeep going from @capgo/capacitor-textinteractionâ](#keep-going-from-capgocapacitor-textinteraction)
If you are using **@capgo/capacitor-textinteraction** to plan native plugin work, connect it with [Using @capgo/capacitor-textinteraction](/plugins/capacitor-textinteraction/) for the native capability in Using @capgo/capacitor-textinteraction, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-textinteraction and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-textinteraction` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-textinteraction
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { TextInteraction } from '@capgo/capacitor-textinteraction';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `toggle`
[Section titled âtoggleâ](#toggle)
Toggle text interaction (selection) on the Capacitor WebView.
â ïž Disabling text interaction prevents all text input controls from working while disabled. Use it sparingly and re-enable when text entry is required.
iOS only.
```typescript
import { TextInteraction } from '@capgo/capacitor-textinteraction';
await TextInteraction.toggle({} as TextInteractionOptions);
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `TextInteractionOptions`
[Section titled âTextInteractionOptionsâ](#textinteractionoptions)
```typescript
export interface TextInteractionOptions {
/**
* Whether text interaction should be enabled or disabled. Disabling hides the
* magnifier lens reintroduced with iOS 15.
*/
enabled: boolean;
}
```
### `TextInteractionResult`
[Section titled âTextInteractionResultâ](#textinteractionresult)
```typescript
export interface TextInteractionResult {
/**
* `true` when the platform supports toggling text interaction (iOS >= 14.5), otherwise `false`.
*/
success: boolean;
/**
* Get the native Capacitor plugin version
*
* @returns {Promise<{ id: string }>} an Promise with version for this device
* @throws An error if the something went wrong
*/
getPluginVersion(): Promise<{ version: string }>;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-textinteraction](/plugins/capacitor-textinteraction/) for the native capability in Using @capgo/capacitor-textinteraction, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-transitions
> Add native-feeling route transitions and optional iOS edge swipe-back to Capacitor apps without adopting Ionic UI.
Ionic-style motion
Use iOS and Android route animations modeled after mobile navigation patterns without shipping Ionic components.
Swipe back
Enable an iOS edge gesture that follows the finger and can auto-enable only inside native Capacitor iOS.
Framework agnostic
Use web components directly or helpers for React, Vue, Angular, Svelte, and Solid.
No UI lock-in
Bring your own toolbar, content, footer, and router while the library coordinates transition layers.
## When To Use It
[Section titled âWhen To Use Itâ](#when-to-use-it)
`@capgo/capacitor-transitions` is for apps that want Ionic-quality page motion without adopting Ionicâs component system. It keeps navigation in your existing web router and animates page elements inside the Capacitor WebView.
Use it when you need:
* push, pop, and root route transitions that feel close to platform conventions
* coordinated header, content, and footer motion
* page caching for fast back navigation
* an optional iOS edge swipe-back gesture that follows the userâs finger
* framework-specific setup helpers without a framework-specific router
Note
This package does not render native UIKit or Android navigation chrome. For native navbars, tabbars, and native transition shells, use [@capgo/native-navigation](/docs/plugins/native-navigation/).
## Demo
[Section titled âDemoâ](#demo)

React transition flow
## Core API
[Section titled âCore APIâ](#core-api)
* `` owns the animated route stack.
* `` wraps each page.
* ``, ``, and `` identify the regions that should move together.
* `initTransitions(options?)` initializes framework bindings.
* `setDirection('forward' | 'back' | 'root' | 'none')` tells the next router update which animation to run.
* `setupRouterOutlet(element, options?)` connects an outlet to lifecycle and gesture behavior.
* `setupPage(element, callbacks?)` registers page lifecycle callbacks.
## Platform Model
[Section titled âPlatform Modelâ](#platform-model)
`platform="auto"` chooses the iOS or Android animation profile from the runtime environment. You can force `platform="ios"` or `platform="android"` when testing.
`swipe-gesture="auto"` uses Capacitor runtime helpers and enables the edge gesture only in native iOS Capacitor apps. Use `true` to force it on or `false` to disable it.
The gesture is implemented in the web layer. It is designed to feel like Ionicâs iOS swipe-back transition by driving animation progress from the pointer position, then finishing or cancelling based on distance and velocity.
## Keep going from @capgo/capacitor-transitions
[Section titled âKeep going from @capgo/capacitor-transitionsâ](#keep-going-from-capgocapacitor-transitions)
If you are using **@capgo/capacitor-transitions** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-transitions](/plugins/capacitor-transitions/) for the native capability in Using @capgo/capacitor-transitions, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-transitions and add Ionic-style route transitions to a Capacitor app.
## Installation
[Section titled âInstallationâ](#installation)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-transitions` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
1. **Install the package**
```bash
npm install @capgo/capacitor-transitions
```
2. **Register the web components**
```ts
import '@capgo/capacitor-transitions';
```
3. **Wrap routed pages**
```html
Inbox
Open message
Tabs
```
4. **Set the direction before your router changes route**
```ts
import { setDirection } from '@capgo/capacitor-transitions/react';
setDirection('forward');
router.push('/message/42');
setDirection('back');
router.back();
```
Note
There is no native sync step for this package. It runs in the web layer of your Capacitor app.
## React Setup
[Section titled âReact Setupâ](#react-setup)
```tsx
import { useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { initTransitions, setDirection, setupPage, setupRouterOutlet } from '@capgo/capacitor-transitions/react';
import '@capgo/capacitor-transitions';
initTransitions({ platform: 'auto' });
export function AppShell() {
const outletRef = useRef(null);
useEffect(() => {
if (!outletRef.current) return;
setupRouterOutlet(outletRef.current, {
platform: 'auto',
swipeGesture: 'auto',
});
}, []);
return (
{/* Your router renders cap-page children here. */}
);
}
export function InboxPage() {
const navigate = useNavigate();
const pageRef = useRef(null);
useEffect(() => {
if (!pageRef.current) return;
return setupPage(pageRef.current, {
onDidEnter: () => console.log('Inbox visible'),
});
}, []);
return (
Inbox
{
setDirection('forward');
navigate('/message/42');
}}
>
Open message
);
}
```
### React JSX TypeScript
[Section titled âReact JSX TypeScriptâ](#react-jsx-typescript)
Importing from `@capgo/capacitor-transitions/react` includes JSX typings for `cap-router-outlet`, `cap-page`, `cap-header`, `cap-content`, and `cap-footer`. In most React projects, that import makes the custom elements valid in TSX automatically.
If TypeScript still reports `Property 'cap-router-outlet' does not exist on type 'JSX.IntrinsicElements'`, add a project declaration file:
src/capgo-transitions.d.ts
```ts
import '@capgo/capacitor-transitions/react';
```
For Vite, Create React App, and most webpack React apps, placing that file inside `src/` is enough. For Next.js, put it in `src/` or the project root and make sure `tsconfig.json` includes it:
```json
{
"include": ["src", "src/capgo-transitions.d.ts"]
}
```
For custom TypeScript or webpack setups that use a separate `types/` folder, include that folder instead:
```json
{
"include": ["src", "types"]
}
```
## Swipe Back
[Section titled âSwipe Backâ](#swipe-back)
Enable or disable the iOS edge gesture from markup:
```html
```
Or from JavaScript:
```ts
const outlet = document.querySelector('cap-router-outlet');
outlet?.setSwipeGesture('auto');
outlet?.setSwipeGesture(true);
outlet?.setSwipeGesture(false);
```
`auto` enables the gesture only when Capacitor reports a native iOS runtime. During the gesture, the page transition follows the finger. When the user releases, the transition either completes and asks the browser history to go back, or cancels and restores the current page.
To keep an element from starting the gesture, add `data-swipe-gesture-ignore`:
```html
Open drawer
```
## With Native Navigation
[Section titled âWith Native Navigationâ](#with-native-navigation)
Use `@capgo/capacitor-transitions` with `@capgo/native-navigation` when native should own the top and bottom bars while web content keeps Ionic-style page motion.
1. Install and sync the native navigation package:
```bash
npm install @capgo/native-navigation
npx cap sync
```
2. Configure native chrome:
```ts
import { NativeNavigation } from '@capgo/native-navigation';
await NativeNavigation.configure({
contentInsetMode: 'css',
});
await NativeNavigation.setNavbar({
title: 'Inbox',
backButton: { visible: false },
});
```
3. Keep the web transition outlet responsible for pages only:
```html
Inbox content
```
```css
.native-page {
padding-top: var(--cap-native-navigation-top);
padding-bottom: var(--cap-native-navigation-bottom);
}
```
4. Drive both systems from the same router events:
```ts
import { NativeNavigation } from '@capgo/native-navigation';
import { setDirection } from '@capgo/capacitor-transitions/react';
import { router } from './router';
await NativeNavigation.addListener('navbarBack', () => {
setDirection('back');
router.back();
});
async function openMessage(id: string) {
setDirection('forward');
router.push(`/message/${id}`);
await NativeNavigation.setNavbar({
title: 'Message',
backButton: { visible: true, title: 'Inbox' },
});
}
```
Do not render the native top bar again as a moving ``. Let `@capgo/native-navigation` keep the bar native and use `@capgo/capacitor-transitions` for the WebView page content underneath it.
## Components
[Section titled âComponentsâ](#components)
### ``
[Section titled â\â](#cap-router-outlet)
| Attribute | Type | Default | Description |
| --------------- | ------------------------------ | ---------------- | ------------------------------------------------------ |
| `platform` | `'ios' \| 'android' \| 'auto'` | `'auto'` | Animation style |
| `duration` | `number` | Platform default | Animation duration in milliseconds |
| `keep-in-dom` | `boolean` | `true` | Keep inactive pages in the DOM |
| `max-cached` | `number` | `10` | Maximum cached pages |
| `swipe-gesture` | `boolean \| 'auto'` | `'auto'` | Enable, disable, or native-detect the iOS edge gesture |
Methods:
* `push(element, config?)`
* `pop(config?)`
* `setRoot(element, config?)`
* `setSwipeGesture(true | false | 'auto')`
### ``
[Section titled â\â](#cap-page)
Wraps one page and emits lifecycle events:
* `cap-will-enter`
* `cap-did-enter`
* `cap-will-leave`
* `cap-did-leave`
### ``
[Section titled â\â](#cap-content)
| Attribute | Type | Default | Description |
| ------------ | --------- | ------- | ------------------------------------ |
| `fullscreen` | `boolean` | `false` | Let content scroll behind the header |
| `scroll-x` | `boolean` | `true` | Enable horizontal scrolling |
| `scroll-y` | `boolean` | `true` | Enable vertical scrolling |
## Framework Helpers
[Section titled âFramework Helpersâ](#framework-helpers)
The framework entrypoints expose the same core helpers:
```ts
import { initTransitions, setDirection, setupPage, setupRouterOutlet } from '@capgo/capacitor-transitions/react';
initTransitions({ platform: 'auto' });
setDirection('forward');
setupRouterOutlet(element, { platform: 'auto', swipeGesture: 'auto' });
setupPage(element, { onWillEnter, onDidEnter, onWillLeave, onDidLeave });
```
Available entrypoints:
* `@capgo/capacitor-transitions/react`
* `@capgo/capacitor-transitions/vue`
* `@capgo/capacitor-transitions/angular`
* `@capgo/capacitor-transitions/svelte`
* `@capgo/capacitor-transitions/solid`
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan migration and enterprise operations, connect it with [Using @capgo/capacitor-transitions](/plugins/capacitor-transitions/) for the native capability in Using @capgo/capacitor-transitions, [Capgo Enterprise](/enterprise/) for the product workflow in Capgo Enterprise, [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives, [Capgo Alternatives](/alternatives/) for the product workflow in Capgo Alternatives, and [Capgo Consulting](/consulting/) for the product workflow in Capgo Consulting.
# @capgo/capacitor-twilio-video
> Capacitor API for joining Twilio Video rooms with a native in-app call surface.
## Overview
[Section titled âOverviewâ](#overview)
Capacitor API for joining Twilio Video rooms with a native in-app call surface.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `login` - Store and validate a Twilio Video access token minted by your backend.
* `logout` - Clear the cached access token and leave the active room.
* `isLoggedIn` - Check whether a valid Twilio token is currently cached on the device.
* `joinRoom` - Join a Twilio room and present the pluginâs native in-app call overlay.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ----------------------------- | ----------------------------------------------------------------------- |
| `login` | Store and validate a Twilio Video access token minted by your backend. |
| `logout` | Clear the cached access token and leave the active room. |
| `isLoggedIn` | Check whether a valid Twilio token is currently cached on the device. |
| `joinRoom` | Join a Twilio room and present the pluginâs native in-app call overlay. |
| `leaveRoom` | Leave the current room if connected. |
| `setMicrophoneEnabled` | Enable/disable local microphone publishing. |
| `setCameraEnabled` | Enable/disable local camera publishing. |
| `getCallStatus` | Return the current room name, media state, and participant count. |
| `checkMicrophonePermission` | Check microphone permission state. |
| `requestMicrophonePermission` | Request microphone permission. |
| `checkCameraPermission` | Check camera permission state. |
| `requestCameraPermission` | Request camera permission. |
| `addListener` | Listen for room connected events. |
| `addListener` | Listen for room disconnected events. |
| `addListener` | Listen for participant connected events. |
| `addListener` | Listen for participant disconnected events. |
| `addListener` | Listen for reconnection start events. |
| `addListener` | Listen for reconnection success events. |
| `removeAllListeners` | Remove every listener registered through this plugin instance. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-twilio-video](https://github.com/Cap-go/capacitor-twilio-video/).
## Keep going from @capgo/capacitor-twilio-video
[Section titled âKeep going from @capgo/capacitor-twilio-videoâ](#keep-going-from-capgocapacitor-twilio-video)
If you are using **@capgo/capacitor-twilio-video** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-twilio-video](/plugins/capacitor-twilio-video/) for the native capability in Using @capgo/capacitor-twilio-video, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# Getting Started
> Install @capgo/capacitor-twilio-video and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-twilio-video` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-twilio-video
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `login`
[Section titled âloginâ](#login)
Store and validate a Twilio Video access token minted by your backend.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.login({} as { accessToken: string });
```
### `logout`
[Section titled âlogoutâ](#logout)
Clear the cached access token and leave the active room.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.logout();
```
### `isLoggedIn`
[Section titled âisLoggedInâ](#isloggedin)
Check whether a valid Twilio token is currently cached on the device.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.isLoggedIn();
```
### `joinRoom`
[Section titled âjoinRoomâ](#joinroom)
Join a Twilio room and present the pluginâs native in-app call overlay.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.joinRoom({} as { roomName: string; enableAudio?: boolean; enableVideo?: boolean });
```
### `leaveRoom`
[Section titled âleaveRoomâ](#leaveroom)
Leave the current room if connected.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.leaveRoom();
```
### `setMicrophoneEnabled`
[Section titled âsetMicrophoneEnabledâ](#setmicrophoneenabled)
Enable/disable local microphone publishing.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.setMicrophoneEnabled({} as { enabled: boolean });
```
### `setCameraEnabled`
[Section titled âsetCameraEnabledâ](#setcameraenabled)
Enable/disable local camera publishing.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.setCameraEnabled({} as { enabled: boolean });
```
### `getCallStatus`
[Section titled âgetCallStatusâ](#getcallstatus)
Return the current room name, media state, and participant count.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.getCallStatus();
```
### `checkMicrophonePermission`
[Section titled âcheckMicrophonePermissionâ](#checkmicrophonepermission)
Check microphone permission state.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.checkMicrophonePermission();
```
### `requestMicrophonePermission`
[Section titled ârequestMicrophonePermissionâ](#requestmicrophonepermission)
Request microphone permission.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.requestMicrophonePermission();
```
### `checkCameraPermission`
[Section titled âcheckCameraPermissionâ](#checkcamerapermission)
Check camera permission state.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.checkCameraPermission();
```
### `requestCameraPermission`
[Section titled ârequestCameraPermissionâ](#requestcamerapermission)
Request camera permission.
```typescript
import { CapacitorTwilioVideo } from '@capgo/capacitor-twilio-video';
await CapacitorTwilioVideo.requestCameraPermission();
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan native media and interface behavior, connect it with [Using @capgo/capacitor-twilio-video](/plugins/capacitor-twilio-video/) for the native capability in Using @capgo/capacitor-twilio-video, [Using @capgo/capacitor-live-activities](/plugins/capacitor-live-activities/) for the native capability in Using @capgo/capacitor-live-activities, [@capgo/capacitor-live-activities](/docs/plugins/live-activities/) for the implementation detail in @capgo/capacitor-live-activities, [Using @capgo/capacitor-video-player](/plugins/capacitor-video-player/) for the native capability in Using @capgo/capacitor-video-player, and [@capgo/capacitor-video-player](/docs/plugins/video-player/) for the implementation detail in @capgo/capacitor-video-player.
# @capgo/capacitor-twilio-voice
> Integrates the Twilio Voice SDK into Capacitor.
## Overview
[Section titled âOverviewâ](#overview)
Integrates the Twilio Voice SDK into Capacitor.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `login` - Authenticate the user with Twilio Voice using an access token.
* `logout` - Log out the current user and unregister from Twilio Voice.
* `isLoggedIn` - Check if the user is currently logged in and has a valid access token.
* `makeCall` - Initiate an outgoing call to a phone number or client.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ----------------------------- | ---------------------------------------------------------------------- |
| `login` | Authenticate the user with Twilio Voice using an access token. |
| `logout` | Log out the current user and unregister from Twilio Voice. |
| `isLoggedIn` | Check if the user is currently logged in and has a valid access token. |
| `makeCall` | Initiate an outgoing call to a phone number or client. |
| `acceptCall` | Accept an incoming call. |
| `rejectCall` | Reject an incoming call. |
| `endCall` | End an active call. |
| `muteCall` | Mute or unmute the microphone during an active call. |
| `setSpeaker` | Enable or disable speakerphone mode. |
| `getCallStatus` | Get the current status of the active call. |
| `checkMicrophonePermission` | Check if microphone permission has been granted. |
| `requestMicrophonePermission` | Request microphone permission from the user. |
| `addListener` | Listen for incoming call invitations. |
| `addListener` | Listen for call connected events. |
| `addListener` | Listen for call invite cancellation events. |
| `addListener` | Listen for outgoing call initiation events. |
| `addListener` | Listen for outgoing call failure events. |
| `addListener` | Listen for call disconnection events. |
| `addListener` | Listen for call ringing events. |
| `addListener` | Listen for call reconnecting events. |
| `addListener` | Listen for call reconnected events. |
| `addListener` | Listen for call quality warning events. |
| `addListener` | Listen for successful registration events. |
| `addListener` | Listen for registration failure events. |
| `removeAllListeners` | Remove all registered event listeners. |
| `getPluginVersion` | Get the native Capacitor plugin version. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-twilio-voice](https://github.com/Cap-go/capacitor-twilio-voice/).
## Keep going from @capgo/capacitor-twilio-voice
[Section titled âKeep going from @capgo/capacitor-twilio-voiceâ](#keep-going-from-capgocapacitor-twilio-voice)
If you are using **@capgo/capacitor-twilio-voice** to plan native plugin work, connect it with [Using @capgo/capacitor-twilio-voice](/plugins/capacitor-twilio-voice/) for the native capability in Using @capgo/capacitor-twilio-voice, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Getting Started
> Install @capgo/capacitor-twilio-voice and start using its current Capacitor API.
## Install
[Section titled âInstallâ](#install)
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
```bash
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins
```
Then use the following prompt:
```text
Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-twilio-voice` plugin in my project.
```
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
```bash
bun add @capgo/capacitor-twilio-voice
bunx cap sync
```
## Import
[Section titled âImportâ](#import)
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
```
## API Overview
[Section titled âAPI Overviewâ](#api-overview)
### `login`
[Section titled âloginâ](#login)
Authenticate the user with Twilio Voice using an access token.
The access token should be generated on your backend server using your Twilio credentials. This token is required to make and receive calls through Twilio Voice.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
const result = await CapacitorTwilioVoice.login({
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
});
console.log('Login successful:', result.success);
```
### `logout`
[Section titled âlogoutâ](#logout)
Log out the current user and unregister from Twilio Voice.
This will disconnect any active calls and stop the device from receiving new incoming call notifications.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
const result = await CapacitorTwilioVoice.logout();
console.log('Logout successful:', result.success);
```
### `isLoggedIn`
[Section titled âisLoggedInâ](#isloggedin)
Check if the user is currently logged in and has a valid access token.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
const status = await CapacitorTwilioVoice.isLoggedIn();
if (status.isLoggedIn && status.hasValidToken) {
console.log('User identity:', status.identity);
} else {
// Re-authenticate the user
}
```
### `makeCall`
[Section titled âmakeCallâ](#makecall)
Initiate an outgoing call to a phone number or client.
The user must be logged in before making a call. The call will be routed through your Twilio backend configuration.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
// Call a phone number
const result = await CapacitorTwilioVoice.makeCall({
to: '+1234567890'
});
console.log('Call SID:', result.callSid);
// Call another Twilio client with a readable name for CallKit Recents
await CapacitorTwilioVoice.makeCall({
to: 'client:alice',
displayName: 'Alice Smith'
});
// Call a PSTN number using a specific caller ID
await CapacitorTwilioVoice.makeCall({
to: '+1234567890',
callerId: '+10987654321'
});
```
### `acceptCall`
[Section titled âacceptCallâ](#acceptcall)
Accept an incoming call.
This should be called in response to a âcallInviteReceivedâ event.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
CapacitorTwilioVoice.addListener('callInviteReceived', async (data) => {
console.log('Incoming call from:', data.from);
const result = await CapacitorTwilioVoice.acceptCall({
callSid: data.callSid
});
console.log('Call accepted:', result.success);
});
```
### `rejectCall`
[Section titled ârejectCallâ](#rejectcall)
Reject an incoming call.
This should be called in response to a âcallInviteReceivedâ event. The caller will hear a busy signal or be directed to voicemail.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
CapacitorTwilioVoice.addListener('callInviteReceived', async (data) => {
if (shouldRejectCall(data.from)) {
await CapacitorTwilioVoice.rejectCall({
callSid: data.callSid
});
}
});
```
### `endCall`
[Section titled âendCallâ](#endcall)
End an active call.
If callSid is not provided, this will end the currently active call.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
// End the current active call
await CapacitorTwilioVoice.endCall({});
// End a specific call
await CapacitorTwilioVoice.endCall({
callSid: 'CA1234567890abcdef'
});
```
### `muteCall`
[Section titled âmuteCallâ](#mutecall)
Mute or unmute the microphone during an active call.
When muted, the other party will not hear audio from your microphone.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
// Mute the microphone
await CapacitorTwilioVoice.muteCall({
muted: true
});
// Unmute the microphone
await CapacitorTwilioVoice.muteCall({
muted: false
});
```
### `setSpeaker`
[Section titled âsetSpeakerâ](#setspeaker)
Enable or disable speakerphone mode.
When enabled, audio will be routed through the deviceâs speaker instead of the earpiece.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
// Enable speakerphone
await CapacitorTwilioVoice.setSpeaker({
enabled: true
});
// Disable speakerphone
await CapacitorTwilioVoice.setSpeaker({
enabled: false
});
```
### `getCallStatus`
[Section titled âgetCallStatusâ](#getcallstatus)
Get the current status of the active call.
This provides real-time information about the call state, mute status, hold status, and call identifiers.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
const status = await CapacitorTwilioVoice.getCallStatus();
if (status.hasActiveCall) {
console.log('Call SID:', status.callSid);
console.log('Call State:', status.callState);
console.log('Is Muted:', status.isMuted);
console.log('Is On Hold:', status.isOnHold);
}
```
### `checkMicrophonePermission`
[Section titled âcheckMicrophonePermissionâ](#checkmicrophonepermission)
Check if microphone permission has been granted.
This does not request permission, only checks the current permission status.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
const result = await CapacitorTwilioVoice.checkMicrophonePermission();
if (!result.granted) {
console.log('Microphone permission not granted');
}
```
### `requestMicrophonePermission`
[Section titled ârequestMicrophonePermissionâ](#requestmicrophonepermission)
Request microphone permission from the user.
On iOS and Android, this will show the system permission dialog if permission has not been granted yet. If permission was previously denied, the user may need to grant it in system settings.
```typescript
import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
const result = await CapacitorTwilioVoice.requestMicrophonePermission();
if (result.granted) {
console.log('Microphone permission granted');
} else {
console.log('Microphone permission denied');
}
```
## Type Reference
[Section titled âType Referenceâ](#type-reference)
### `CallInvite`
[Section titled âCallInviteâ](#callinvite)
Capacitor plugin for integrating Twilio Voice functionality into mobile applications.
```typescript
export interface CallInvite {
/** Unique identifier for the incoming call invitation */
callSid: string;
/** Phone number or client identifier of the caller (may include custom caller name) */
from: string;
/** Phone number or client identifier being called */
to: string;
/** Custom parameters passed with the call invitation */
customParams: Record;
}
```
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This page is generated from the pluginâs `src/definitions.ts`. Re-run the sync when the public API changes upstream.
## Keep going from Getting Started
[Section titled âKeep going from Getting Startedâ](#keep-going-from-getting-started)
If you are using **Getting Started** to plan dashboard and API operations, connect it with [Using @capgo/capacitor-twilio-voice](/plugins/capacitor-twilio-voice/) for the native capability in Using @capgo/capacitor-twilio-voice, [API Overview](/docs/public-api/) for the implementation detail in API Overview, [Introduction](/docs/webapp/) for the implementation detail in Introduction, [API Keys](/docs/public-api/api-keys/) for the implementation detail in API Keys, and [Devices](/docs/public-api/devices/) for the implementation detail in Devices.
# @capgo/capacitor-updater
> Live update for capacitor apps.
## Also available for Cordova and Electron
[Section titled âAlso available for Cordova and Electronâ](#also-available-for-cordova-and-electron)
Capgo live updates are not limited to Capacitor. Use the same backend and CLI with sibling client plugins:
[Cordova updater ](/docs/plugins/cordova-updater/)OTA for Cordova iOS 7+ and Android 13+.
[Electron updater ](/docs/plugins/electron-updater/)Desktop live updates with the same API.
## Overview
[Section titled âOverviewâ](#overview)
Live update for capacitor apps.
## Core Capabilities
[Section titled âCore Capabilitiesâ](#core-capabilities)
* `notifyAppReady` - Notify the native layer that JavaScript initialized successfully.
* `setUpdateUrl` - Set the update URL for the app dynamically at runtime.
* `setStatsUrl` - Set the statistics URL for the app dynamically at runtime.
* `setChannelUrl` - Set the channel URL for the app dynamically at runtime.
## Public API
[Section titled âPublic APIâ](#public-api)
| Method | Description |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `notifyAppReady` | Notify the native layer that JavaScript initialized successfully. |
| `setUpdateUrl` | Set the update URL for the app dynamically at runtime. |
| `setStatsUrl` | Set the statistics URL for the app dynamically at runtime. |
| `setChannelUrl` | Set the channel URL for the app dynamically at runtime. |
| `download` | Download a new bundle from the provided URL for later installation. |
| `next` | Set the next bundle to be activated when the app backgrounds or restarts. |
| `set` | Set the current bundle and immediately reloads the app. |
| `delete` | Delete a bundle from local storage to free up disk space. |
| `setBundleError` | Manually mark a bundle as failed/errored in manual update mode. |
| `list` | Get all locally downloaded bundles stored in your app. |
| `reset` | Reset the app to a known good bundle. |
| `current` | Get information about the currently active bundle. |
| `reload` | Manually reload the app to apply a pending update. |
| `setMultiDelay` | Configure conditions that must be met before a pending update is applied. |
| `cancelDelay` | Cancel all delay conditions and apply the pending update immediately. |
| `getLatest` | Check the update server for the latest available bundle version. |
| `setChannel` | Set a local runtime update channel for this device. It does not create a dashboard/API device override. |
| `unsetChannel` | Remove the deviceâs channel assignment and return to the default channel. |
| `getChannel` | Get the current channel assigned to this device. |
| `listChannels` | Get a list of all channels available for this device to self-assign to. |
| `setCustomId` | Set a custom identifier for this device. |
| `getBuiltinVersion` | Get the builtin bundle version (the original version shipped with your native app). |
| `getDeviceId` | Get the unique, privacy-friendly identifier for this device. |
| `getPluginVersion` | Get the version of the Capacitor Updater plugin installed in your app. |
| `isAutoUpdateEnabled` | Check if automatic updates are currently enabled. |
| `removeAllListeners` | Remove all event listeners registered for this plugin. |
| `addListener` | Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished. This will return you all download percent during the download. |
| `addListener` | Listen for no need to update event, useful when you want force check every time the app is launched. |
| `addListener` | Listen for available update event, useful when you want to force check every time the app is launched. |
| `addListener` | Listen for downloadComplete events. |
| `addListener` | Listen for breaking update events when the backend flags an update as incompatible with the current app. Emits the same payload as the legacy `majorAvailable` listener. |
| `addListener` | Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking. |
| `addListener` | Listen for update fail event in the App, let you know when update has fail to install at next app start. |
| `addListener` | Listen for set event in the App, let you know when a bundle has been applied successfully. This event is retained natively until JavaScript consumes it, so if the app reloads before your listener is attached, the last pending `set` event is delivered once the listener subscribes. |
| `addListener` | Listen for set next event in the App, let you know when a bundle is queued as the next bundle to install. |
| `addListener` | Listen for download fail event in the App, let you know when a bundle download has failed. |
| `addListener` | Listen for reload event in the App, let you know when reload has happened. |
| `addListener` | Listen for app ready event in the App, let you know when app is ready to use. This event is retained natively until JavaScript consumes it, so it can still be delivered after a reload even if the listener is attached later in app startup. |
| `addListener` | Listen for channel private event, fired when attempting to set a channel that doesnât allow device self-assignment. |
| `addListener` | Listen for flexible update state changes on Android. |
| `isAutoUpdateAvailable` | Check if the auto-update feature is available (not disabled by custom server configuration). |
| `getNextBundle` | Get information about the bundle queued to be activated on next reload. |
| `getFailedUpdate` | Retrieve information about the most recent bundle that failed to load. |
| `setShakeMenu` | Enable or disable the shake gesture menu for debugging and testing. |
| `isShakeMenuEnabled` | Check if the shake gesture debug menu is currently enabled. |
| `setShakeChannelSelector` | Enable or disable the shake channel selector at runtime. |
| `isShakeChannelSelectorEnabled` | Check if the shake channel selector is currently enabled. |
| `getAppId` | Get the currently configured App ID used for update server communication. |
| `setAppId` | Dynamically change the App ID used for update server communication. |
| `getAppUpdateInfo` | Get information about the appâs availability in the App Store or Play Store. |
| `openAppStore` | Open the appâs page in the App Store or Play Store. |
| `performImmediateUpdate` | Perform an immediate in-app update on Android. |
| `startFlexibleUpdate` | Start a flexible in-app update on Android. |
| `completeFlexibleUpdate` | Complete a flexible in-app update on Android. |
## Source Of Truth
[Section titled âSource Of Truthâ](#source-of-truth)
This reference is synced from `src/definitions.ts` in [capacitor-updater](https://github.com/Cap-go/capacitor-updater/).
## Keep going from @capgo/capacitor-updater
[Section titled âKeep going from @capgo/capacitor-updaterâ](#keep-going-from-capgocapacitor-updater)
If you are using **@capgo/capacitor-updater** to plan native plugin work, connect it with [Using @capgo/capacitor-updater](/plugins/capacitor-updater/) for the native capability in Using @capgo/capacitor-updater, [Capgo Plugin Directory](/plugins/) for the product workflow in Capgo Plugin Directory, [Capacitor Plugins by Capgo](/docs/plugins/) for the implementation detail in Capacitor Plugins by Capgo, [Adding or Updating Plugins](/docs/contributing/adding-plugins/) for the implementation detail in Adding or Updating Plugins, and [Ionic Enterprise Plugin Alternatives](/ionic-enterprise-plugins/) for the product workflow in Ionic Enterprise Plugin Alternatives.
# Functions and settings
> All available method and settings of the plugin
# Updater Plugin Config
[Section titled âUpdater Plugin Configâ](#updater-plugin-config)
See the Github [Readme](https://github.com/Cap-go/capacitor-updater) for more information.
CapacitorUpdater can be configured with these options:
| Prop | Type | Description | Default | Since |
| ---------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ------- |
| **`appReadyTimeout`** | `number` | Configure the number of milliseconds the native plugin should wait before considering an update âfailedâ. Available on Android, iOS, and Electron. | `10000 // (10 seconds)` | |
| **`responseTimeout`** | `number` | Configure the number of milliseconds the native plugin should wait before considering API timeout. Available on Android, iOS, and Electron. | `20000 // (20 seconds)` | |
| **`autoDeleteFailed`** | `boolean` | Configure whether the plugin should use automatically delete failed bundles. Available on Android, iOS, and Electron. | `true` | |
| **`autoDeletePrevious`** | `boolean` | Configure whether the plugin should use automatically delete previous bundles after a successful update. Available on Android, iOS, and Electron. | `true` | |
| **`autoUpdate`** | `boolean \| âoffâ \| âatBackgroundâ \| âatInstallâ \| âonLaunchâ \| âalwaysâ \| âonlyDownloadâ` | Configure how the plugin should use Auto Update via an update server. true is the same as âatBackgroundâ; false is the same as âoffâ. - off: Disable Auto Update - atBackground: Check and download automatically, then apply when the app moves to the background - atInstall: Apply immediately only after a fresh install or native app update, otherwise use atBackground - onLaunch: Apply immediately on launch, otherwise use atBackground after the launch check - always: Apply immediately whenever Auto Update runs - onlyDownload: Check and download automatically, emit updateAvailable, and never set the next bundle automatically. Available on Android, iOS, and Electron. | `âatBackgroundâ // true is still accepted` | |
| **`resetWhenUpdate`** | `boolean` | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Available on Android, iOS, and Electron. | `true` | |
| **`updateUrl`** | `string` | Configure the URL / endpoint to which update checks are sent. Available on Android, iOS, and Electron. | `https://plugin.capgo.app/updates` | |
| **`channelUrl`** | `string` | Configure the URL / endpoint for channel operations. Available on Android, iOS, and Electron. | `https://plugin.capgo.app/channel_self` | |
| **`statsUrl`** | `string` | Configure the URL / endpoint to which update statistics are sent. Available on Android, iOS, and Electron. Set to "" to disable stats reporting. | `https://plugin.capgo.app/stats` | |
| **`publicKey`** | `string` | Configure the public key for end to end live update encryption Version 2. Available on Android, iOS, and Electron. | `undefined` | 6.2.0 |
| **`version`** | `string` | Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Available on Android, iOS, and Electron. | `undefined` | 4.17.48 |
| **`directUpdate`** | `boolean \| âalwaysâ \| âatInstallâ \| âonLaunchâ` | Deprecated. Use autoUpdate string modes instead: âatInstallâ, âonLaunchâ, or âalwaysâ. This option remains supported for existing apps. - false: Never do direct updates - atInstall: Same as autoUpdate: âatInstallâ - onLaunch: Same as autoUpdate: âonLaunchâ - always: Same as autoUpdate: âalwaysâ - true: Same as âalwaysâ for backward compatibility. Available on Android, iOS, and Electron. | `false` | 5.1.0 |
| **`autoSplashscreen`** | `boolean` | Automatically handle splashscreen hiding when using instant apply modes. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when autoUpdate is set to âatInstallâ, âonLaunchâ, or âalwaysâ. Legacy directUpdate values are still supported for backward compatibility. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires Auto Update to be enabled. Available on Android and iOS. | `false` | 7.6.0 |
| **`periodCheckDelay`** | `number` | Configure the delay period for period update check. the unit is in seconds. Available on Android, iOS, and Electron. Cannot be less than 600 seconds (10 minutes). | `600 // (10 minutes)` | |
| **`localS3`** | `boolean` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 |
| **`localHost`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 |
| **`localWebHost`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 |
| **`localSupa`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 |
| **`localSupaAnon`** | `string` | Configure the CLI to use a local server for testing. | `undefined` | 4.17.48 |
| **`localApi`** | `string` | Configure the CLI to use a local api for testing. | `undefined` | 6.3.3 |
| **`localApiFiles`** | `string` | Configure the CLI to use a local file api for testing. | `undefined` | 6.3.3 |
| **`allowModifyUrl`** | `boolean` | Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. | `false` | 5.4.0 |
| **`defaultChannel`** | `string` | Set the default channel for the app in the config. Case sensitive. This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. | `undefined` | 5.5.0 |
| **`appId`** | `string` | Configure the app id for the app in the config. | `undefined` | 6.0.0 |
| **`keepUrlPathAfterReload`** | `boolean` | Configure the plugin to keep the URL path after a reload. WARNING: When a reload is triggered, âwindow\.historyâ will be cleared. | `false` | 6.8.0 |
| **`disableJSLogging`** | `boolean` | Disable the JavaScript logging of the plugin. if true, the plugin will not log to the JavaScript console. only the native log will be done | `false` | 7.3.0 |
| **`shakeMenu`** | `boolean` | Enable shake gesture to show update menu for debugging/testing purposes | `false` | 7.5.0 |
## Examples
[Section titled âExamplesâ](#examples)
In `capacitor.config.json`:
```json
{
"plugins": {
"CapacitorUpdater": {
"appReadyTimeout": 1000 // (1 second),
"responseTimeout": 10 // (10 second),
"autoDeleteFailed": false,
"autoDeletePrevious": false,
"autoUpdate": "onlyDownload",
"resetWhenUpdate": false,
"updateUrl": https://example.com/api/auto_update,
"channelUrl": https://example.com/api/channel,
"statsUrl": https://example.com/api/stats,
"publicKey": undefined,
"version": undefined,
"directUpdate": undefined,
"autoSplashscreen": undefined,
"periodCheckDelay": undefined,
"localS3": undefined,
"localHost": undefined,
"localWebHost": undefined,
"localSupa": undefined,
"localSupaAnon": undefined,
"localApi": undefined,
"localApiFiles": undefined,
"allowModifyUrl": undefined,
"defaultChannel": undefined,
"appId": undefined,
"keepUrlPathAfterReload": undefined,
"disableJSLogging": undefined,
"shakeMenu": undefined
}
}
}
```
In `capacitor.config.ts`:
```ts
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
plugins: {
CapacitorUpdater: {
appReadyTimeout: 1000 // (1 second),
responseTimeout: 10 // (10 second),
autoDeleteFailed: false,
autoDeletePrevious: false,
autoUpdate: 'onlyDownload',
resetWhenUpdate: false,
updateUrl: https://example.com/api/auto_update,
channelUrl: https://example.com/api/channel,
statsUrl: https://example.com/api/stats,
publicKey: undefined,
version: undefined,
directUpdate: undefined,
autoSplashscreen: undefined,
periodCheckDelay: undefined,
localS3: undefined,
localHost: undefined,
localWebHost: undefined,
localSupa: undefined,
localSupaAnon: undefined,
localApi: undefined,
localApiFiles: undefined,
allowModifyUrl: undefined,
defaultChannel: undefined,
appId: undefined,
keepUrlPathAfterReload: undefined,
disableJSLogging: undefined,
shakeMenu: undefined,
},
},
};
export default config;
```
* [`notifyAppReady()`](#notifyappready)
* [`setUpdateUrl(...)`](#setupdateurl)
* [`setStatsUrl(...)`](#setstatsurl)
* [`setChannelUrl(...)`](#setchannelurl)
* [`download(...)`](#download)
* [`next(...)`](#next)
* [`set(...)`](#set)
* [`delete(...)`](#delete)
* [`list(...)`](#list)
* [`reset(...)`](#reset)
* [`current()`](#current)
* [`reload()`](#reload)
* [`setMultiDelay(...)`](#setmultidelay)
* [`cancelDelay()`](#canceldelay)
* [`getLatest(...)`](#getlatest)
* [`setChannel(...)`](#setchannel)
* [`unsetChannel(...)`](#unsetchannel)
* [`getChannel()`](#getchannel)
* [`listChannels()`](#listchannels)
* [`setCustomId(...)`](#setcustomid)
* [`getBuiltinVersion()`](#getbuiltinversion)
* [`getDeviceId()`](#getdeviceid)
* [`getPluginVersion()`](#getpluginversion)
* [`isAutoUpdateEnabled()`](#isautoupdateenabled)
* [`removeAllListeners()`](#removealllisteners)
* [`addListener('download', ...)`](#addlistenerdownload-)
* [`addListener('noNeedUpdate', ...)`](#addlistenernoneedupdate-)
* [`addListener('updateAvailable', ...)`](#addlistenerupdateavailable-)
* [`addListener('downloadComplete', ...)`](#addlistenerdownloadcomplete-)
* [`addListener('majorAvailable', ...)`](#addlistenermajoravailable-)
* [`addListener('updateFailed', ...)`](#addlistenerupdatefailed-)
* [`addListener('downloadFailed', ...)`](#addlistenerdownloadfailed-)
* [`addListener('appReloaded', ...)`](#addlistenerappreloaded-)
* [`addListener('appReady', ...)`](#addlistenerappready-)
* [`isAutoUpdateAvailable()`](#isautoupdateavailable)
* [`getNextBundle()`](#getnextbundle)
* [`setShakeMenu(...)`](#setshakemenu)
* [`isShakeMenuEnabled()`](#isshakemenuenabled)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)
# Methods
[Section titled âMethodsâ](#methods)
## notifyAppReady()
[Section titled ânotifyAppReady()â](#notifyappready)
```typescript
notifyAppReady() => Promise
```
Notify Capacitor Updater that the current bundle is working (a rollback will occur if this method is not called on every app launch) By default this method should be called in the first 10 sec after app launch, otherwise a rollback will occur. Change this behaviour with {@link appReadyTimeout}
**Returns:** `Promise`
***
## setUpdateUrl(âŠ)
[Section titled âsetUpdateUrl(âŠ)â](#setupdateurl)
```typescript
setUpdateUrl(options: UpdateUrl) => Promise
```
Set the updateUrl for the app, this will be used to check for updates.
| Param | Type | Description |
| ------------- | ----------- | ------------------------------------------------- |
| **`options`** | `UpdateUrl` | contains the URL to use for checking for updates. |
**Since:** 5.4.0
***
## setStatsUrl(âŠ)
[Section titled âsetStatsUrl(âŠ)â](#setstatsurl)
```typescript
setStatsUrl(options: StatsUrl) => Promise
```
Set the statsUrl for the app, this will be used to send statistics. Passing an empty string will disable statistics gathering.
| Param | Type | Description |
| ------------- | ---------- | ----------------------------------------------- |
| **`options`** | `StatsUrl` | contains the URL to use for sending statistics. |
**Since:** 5.4.0
***
## setChannelUrl(âŠ)
[Section titled âsetChannelUrl(âŠ)â](#setchannelurl)
```typescript
setChannelUrl(options: ChannelUrl) => Promise
```
Set the channelUrl for the app, this will be used to set the channel.
| Param | Type | Description |
| ------------- | ------------ | ------------------------------------------------ |
| **`options`** | `ChannelUrl` | contains the URL to use for setting the channel. |
**Since:** 5.4.0
***
## download(âŠ)
[Section titled âdownload(âŠ)â](#download)
```typescript
download(options: DownloadOptions) => Promise
```
Download a new bundle from the provided URL, it should be a zip file, with files inside or with a unique id inside with all your files
| Param | Type | Description |
| ------------- | ----------------- | --------------------------------------------------------------------------------- |
| **`options`** | `DownloadOptions` | The {@link [DownloadOptions](#downloadoptions)} for downloading a new bundle zip. |
**Returns:** `Promise`
***
## next(âŠ)
[Section titled ânext(âŠ)â](#next)
```typescript
next(options: BundleId) => Promise
```
Set the next bundle to be used when the app is reloaded.
| Param | Type | Description |
| ------------- | ---------- | -------------------------------------------------------------------------------------------------- |
| **`options`** | `BundleId` | Contains the ID of the next Bundle to set on next app launch. {@link [BundleInfo.id](#bundleinfo)} |
**Returns:** `Promise`
***
## set(âŠ)
[Section titled âset(âŠ)â](#set)
```typescript
set(options: BundleId) => Promise
```
Set the current bundle and immediately reloads the app.
| Param | Type | Description |
| ------------- | ---------- | -------------------------------------------------------------------------------------- |
| **`options`** | `BundleId` | A {@link [BundleId](#bundleid)} object containing the new bundle id to set as current. |
***
## delete(âŠ)
[Section titled âdelete(âŠ)â](#delete)
```typescript
delete(options: BundleId) => Promise
```
Deletes the specified bundle from the native app storage. Use with {@link list} to get the stored Bundle IDs.
| Param | Type | Description |
| ------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **`options`** | `BundleId` | A {@link [BundleId](#bundleid)} object containing the ID of a bundle to delete (note, this is the bundle id, NOT the version name) |
***
## list(âŠ)
[Section titled âlist(âŠ)â](#list)
```typescript
list(options?: ListOptions | undefined) => Promise
```
Get all locally downloaded bundles in your app
| Param | Type | Description |
| ------------- | ------------- | ----------------------------------------------------------- |
| **`options`** | `ListOptions` | The {@link [ListOptions](#listoptions)} for listing bundles |
**Returns:** `Promise`
***
## reset(âŠ)
[Section titled âreset(âŠ)â](#reset)
```typescript
reset(options?: ResetOptions | undefined) => Promise
```
Reset the app to the `builtin` bundle (the one sent to Apple App Store / Google Play Store ) or the last successfully loaded bundle.
| Param | Type | Description |
| ------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`options`** | `ResetOptions` | Containing {@link [ResetOptions.toLastSuccessful](#resetoptions)}, `true` resets to the builtin bundle and `false` will reset to the last successfully loaded bundle. |
***
## current()
[Section titled âcurrent()â](#current)
```typescript
current() => Promise
```
Get the current bundle, if none are set it returns `builtin`. currentNative is the original bundle installed on the device
**Returns:** `Promise`
***
## reload()
[Section titled âreload()â](#reload)
```typescript
reload() => Promise
```
Reload the view
***
## setMultiDelay(âŠ)
[Section titled âsetMultiDelay(âŠ)â](#setmultidelay)
```typescript
setMultiDelay(options: MultiDelayConditions) => Promise
```
Sets a {@link [DelayCondition](#delaycondition)} array containing conditions that the Plugin will use to delay the update. After all conditions are met, the update process will run start again as usual, so update will be installed after a backgrounding or killing the app. For the `date` kind, the value should be an iso8601 date string. For the `background` kind, the value should be a number in milliseconds. For the `nativeVersion` kind, the value should be the version number. For the `kill` kind, the value is not used. The function has inconsistent behavior the option kill do trigger the update after the first kill and not after the next background like other options. This will be fixed in a future major release.
| Param | Type | Description |
| ------------- | ---------------------- | ----------------------------------------------------------------------------------------------- |
| **`options`** | `MultiDelayConditions` | Containing the {@link [MultiDelayConditions](#multidelayconditions)} array of conditions to set |
**Since:** 4.3.0
***
## cancelDelay()
[Section titled âcancelDelay()â](#canceldelay)
```typescript
cancelDelay() => Promise
```
Cancels a {@link [DelayCondition](#delaycondition)} to process an update immediately.
**Since:** 4.0.0
***
## getLatest(âŠ)
[Section titled âgetLatest(âŠ)â](#getlatest)
```typescript
getLatest(options?: GetLatestOptions | undefined) => Promise
```
Get Latest bundle available from update Url
| Param | Type |
| ------------- | ------------------ |
| **`options`** | `GetLatestOptions` |
**Returns:** `Promise`
**Since:** 4.0.0
***
## setChannel(âŠ)
[Section titled âsetChannel(âŠ)â](#setchannel)
```typescript
setChannel(options: SetChannelOptions) => Promise
```
Sets the plugin-managed local channel for this device. The channel must have `allow_device_self_set` enabled for this to work.
`setChannel()` validates the channel with the backend, then stores the selected channel locally on the device. It does not create or update a backend Device Override, so the device will not appear as overridden in the Capgo dashboard. Only assignments created from the dashboard or the Public API are shown in the Device Override UI.
**Important notes:**
* Do not use this method to set the channel at boot. Use the `defaultChannel` in your Capacitor config instead.
* This method is intended for use after the app is ready and the user has interacted (e.g., opting into a beta program).
* **Public channels cannot be self-assigned.** If a channel is marked as `public`, calling `setChannel()` will return an error. To use a public channel, call `unsetChannel()` instead - the device will automatically fall back to the matching public channel.
* Use `listChannels()` to discover which channels are available and whether they allow self-assignment.
| Param | Type | Description |
| ------------- | ------------------- | --------------------------------------------------------------------- |
| **`options`** | `SetChannelOptions` | Is the {@link [SetChannelOptions](#setchanneloptions)} channel to set |
**Returns:** `Promise`
**Since:** 4.7.0
***
## unsetChannel(âŠ)
[Section titled âunsetChannel(âŠ)â](#unsetchannel)
```typescript
unsetChannel(options: UnsetChannelOptions) => Promise
```
Unset the plugin-managed local channel for this device. This clears only the channel stored locally by `setChannel()`; it does not delete Dashboard or Public API Device Override records.
After calling this method, normal channel precedence applies: an existing Dashboard or Public API Device Override still wins; otherwise the device can fall back to the matching public/default channel for its conditions (platform, device type, build type).
This is useful when:
* You want to move a device back to the default update track
* You want to use a public channel (since public channels cannot be self-assigned via `setChannel()`)
| Param | Type |
| ------------- | --------------------- |
| **`options`** | `UnsetChannelOptions` |
**Since:** 4.7.0
***
## getChannel()
[Section titled âgetChannel()â](#getchannel)
```typescript
getChannel() => Promise
```
Get the channel for this device
**Returns:** `Promise`
**Since:** 4.8.0
***
## listChannels()
[Section titled âlistChannels()â](#listchannels)
```typescript
listChannels() => Promise
```
List all channels available for this device. Returns channels that are compatible with the deviceâs current environment (platform, emulator/real device, dev/prod build) and are either public or allow self-assignment.
Each channel in the result includes:
* `public`: If `true`, this is a **default channel**. You cannot self-assign to it using `setChannel()`. Instead, if you remove your channel assignment using `unsetChannel()`, the device will automatically receive updates from this public channel.
* `allow_self_set`: If `true`, this is a **self-assignable channel**. You can explicitly assign the device to this channel using `setChannel()`.
**Returns:** `Promise`
**Since:** 7.5.0
***
## setCustomId(âŠ)
[Section titled âsetCustomId(âŠ)â](#setcustomid)
```typescript
setCustomId(options: SetCustomIdOptions) => Promise
```
Set a custom ID for this device
| Param | Type | Description |
| ------------- | -------------------- | ------------------------------------------------------------------------ |
| **`options`** | `SetCustomIdOptions` | is the {@link [SetCustomIdOptions](#setcustomidoptions)} customId to set |
**Since:** 4.9.0
***
## getBuiltinVersion()
[Section titled âgetBuiltinVersion()â](#getbuiltinversion)
```typescript
getBuiltinVersion() => Promise
```
Get the native app version or the builtin version if set in config
**Returns:** `Promise`
**Since:** 5.2.0
***
## getDeviceId()
[Section titled âgetDeviceId()â](#getdeviceid)
```typescript
getDeviceId() => Promise
```
Get unique ID used to identify device (sent to auto update server)
**Returns:** `Promise`
***
## getPluginVersion()
[Section titled âgetPluginVersion()â](#getpluginversion)
```typescript
getPluginVersion() => Promise
```
Get the native Capacitor Updater plugin version (sent to auto update server)
**Returns:** `Promise`
***
## isAutoUpdateEnabled()
[Section titled âisAutoUpdateEnabled()â](#isautoupdateenabled)
```typescript
isAutoUpdateEnabled() => Promise
```
Get the state of auto update config.
**Returns:** `Promise`
***
## removeAllListeners()
[Section titled âremoveAllListeners()â](#removealllisteners)
```typescript
removeAllListeners() => Promise
```
Remove all listeners for this plugin.
**Since:** 1.0.0
***
## addListener(âdownloadâ, âŠ)
[Section titled âaddListener(âdownloadâ, âŠ)â](#addlistenerdownload-)
```typescript
addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void) => Promise
```
Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished. This will return you all download percent during the download
| Param | Type |
| ------------------ | -------------------------------- |
| **`eventName`** | `âdownloadâ` |
| **`listenerFunc`** | `(state: DownloadEvent) => void` |
**Returns:** `Promise`
**Since:** 2.0.11
***
## addListener(ânoNeedUpdateâ, âŠ)
[Section titled âaddListener(ânoNeedUpdateâ, âŠ)â](#addlistenernoneedupdate-)
```typescript
addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void) => Promise
```
Listen for no need to update event, useful when you want force check every time the app is launched
| Param | Type |
| ------------------ | ------------------------------ |
| **`eventName`** | `ânoNeedUpdateâ` |
| **`listenerFunc`** | `(state: NoNeedEvent) => void` |
**Returns:** `Promise`
**Since:** 4.0.0
***
## addListener(âupdateAvailableâ, âŠ)
[Section titled âaddListener(âupdateAvailableâ, âŠ)â](#addlistenerupdateavailable-)
```typescript
addListener(eventName: 'updateAvailable', listenerFunc: (state: UpdateAvailableEvent) => void) => Promise