---
description: Capacitor Geofences plugin to monitor circular regions on Android and iOS and receive enter and exit events, even while the app is terminated.
title: Capacitor Geofences Plugin for Android & iOS - Capawesome
image: https://capawesome.io/docs/assets/images/social/sdks/capacitor/geofences.png
---

<!doctype html> 

[Skip to content ](#capacitor-geofences-plugin) 

[📲 Introducing **Build Sharing** — get your builds onto testers' devices with a link & QR code. No account required. ](/blog/share-mobile-app-builds-with-testers/) 

* [ SDKs ](/docs/sdks/)
* [ Formbricks ](/docs/sdks/capacitor/formbricks/)
* [ Geocoder ](/docs/sdks/capacitor/geocoder/)
* Geofences [ Geofences ](/docs/sdks/capacitor/geofences/)
* [ iOS ](#ios)
* [ Configuration ](#configuration)
* [ Usage ](#usage)
* [ API ](#api)
* [ Type Aliases ](#type-aliases)
* [ Enums ](#enums)
* [ HTTP Sync ](#http-sync)
* [ FAQ ](#faq)
* [ Related Plugins ](#related-plugins)
* [ Newsletter ](#newsletter)
* [ Changelog ](#changelog)
* [ Breaking Changes ](#breaking-changes)
* [ License ](#license)
* [ Google Sign-In ](/docs/sdks/capacitor/google-sign-in/)
* [ Grafana Faro ](/docs/sdks/capacitor/grafana-faro/)
* [ Gyroscope ](/docs/sdks/capacitor/gyroscope/)
* [ Haptics ](/docs/sdks/capacitor/haptics/)
* [ Health ](/docs/sdks/capacitor/health/)
* [ Home Indicator ](/docs/sdks/capacitor/home-indicator/)
* [ In-App Browser ](/docs/sdks/capacitor/in-app-browser/)
* [ Install Referrer ](/docs/sdks/capacitor/install-referrer/)
* [ Intercom ](/docs/sdks/capacitor/intercom/)
* [ Intune ](/docs/sdks/capacitor/intune/)
* [ Keep Awake ](/docs/sdks/capacitor/keep-awake/)
* [ libSQL ](/docs/sdks/capacitor/libsql/)
* [ Light Sensor ](/docs/sdks/capacitor/light-sensor/)
* [ Live Update ](/docs/sdks/capacitor/live-update/)
* [ LLM ](/docs/sdks/capacitor/llm/)
* [ Localization ](/docs/sdks/capacitor/localization/)
* [ Mail Composer ](/docs/sdks/capacitor/mail-composer/)
* [ Managed Configurations ](/docs/sdks/capacitor/managed-configurations/)
* [ MapLibre ](/docs/sdks/capacitor/maplibre/)
* [ Maps Launcher ](/docs/sdks/capacitor/maps-launcher/)
* [ Media Session ](/docs/sdks/capacitor/media-session/)
* [ ML Kit ](/docs/sdks/capacitor/mlkit/)
* [ Navigation Bar ](/docs/sdks/capacitor/navigation-bar/)
* [ Network ](/docs/sdks/capacitor/network/)
* [ NFC ](/docs/sdks/capacitor/nfc/)
* [ Node.js ](/docs/sdks/capacitor/nodejs/)
* [ OAuth ](/docs/sdks/capacitor/oauth/)
* [ Passkeys ](/docs/sdks/capacitor/passkeys/)
* [ Password Autofill ](/docs/sdks/capacitor/password-autofill/)
* [ PDF Generator ](/docs/sdks/capacitor/pdf-generator/)
* [ PDF Viewer ](/docs/sdks/capacitor/pdf-viewer/)
* [ Pedometer ](/docs/sdks/capacitor/pedometer/)
* [ Permissions ](/docs/sdks/capacitor/permissions/)
* [ Phone Dialer ](/docs/sdks/capacitor/phone-dialer/)
* [ Photo Editor ](/docs/sdks/capacitor/photo-editor/)
* [ Photo Manipulator ](/docs/sdks/capacitor/photo-manipulator/)
* [ PixLive ](/docs/sdks/capacitor/pixlive/)
* [ PostHog ](/docs/sdks/capacitor/posthog/)
* [ Printer ](/docs/sdks/capacitor/printer/)
* [ Privacy Screen ](/docs/sdks/capacitor/privacy-screen/)
* [ Proximity Sensor ](/docs/sdks/capacitor/proximity-sensor/)
* [ Purchases ](/docs/sdks/capacitor/purchases/)
* [ RealtimeKit ](/docs/sdks/capacitor/realtimekit/)
* [ Root Detection ](/docs/sdks/capacitor/root-detection/)
* [ Screen Brightness ](/docs/sdks/capacitor/screen-brightness/)
* [ Screen Orientation ](/docs/sdks/capacitor/screen-orientation/)
* [ Screen Reader ](/docs/sdks/capacitor/screen-reader/)
* [ Screenshot ](/docs/sdks/capacitor/screenshot/)
* [ Secure Preferences ](/docs/sdks/capacitor/secure-preferences/)
* [ Settings Launcher ](/docs/sdks/capacitor/settings-launcher/)
* [ Shake ](/docs/sdks/capacitor/shake/)
* [ Silent Mode ](/docs/sdks/capacitor/silent-mode/)
* [ SIM ](/docs/sdks/capacitor/sim/)
* [ SMS Composer ](/docs/sdks/capacitor/sms-composer/)
* [ Speech Recognition ](/docs/sdks/capacitor/speech-recognition/)
* [ Speech Synthesis ](/docs/sdks/capacitor/speech-synthesis/)
* [ Share Target ](/docs/sdks/capacitor/share-target/)
* [ Square Mobile Payments ](/docs/sdks/capacitor/square-mobile-payments/)
* [ SQLite ](/docs/sdks/capacitor/sqlite/)
* [ Superwall ](/docs/sdks/capacitor/superwall/)
* [ System WebView ](/docs/sdks/capacitor/system-webview/)
* [ Tauri ](/docs/sdks/capacitor/tauri/)
* [ Text Interaction ](/docs/sdks/capacitor/text-interaction/)
* [ Text Zoom ](/docs/sdks/capacitor/text-zoom/)
* [ Thermal State ](/docs/sdks/capacitor/thermal-state/)
* [ Toast ](/docs/sdks/capacitor/toast/)
* [ Torch ](/docs/sdks/capacitor/torch/)
* [ Vault ](/docs/sdks/capacitor/vault/)
* [ Volume ](/docs/sdks/capacitor/volume/)
* [ Wallet ](/docs/sdks/capacitor/wallet/)
* [ Watch ](/docs/sdks/capacitor/watch/)
* [ Wifi ](/docs/sdks/capacitor/wifi/)
* [ YouTube Player ](/docs/sdks/capacitor/youtube-player/)
* [ Zip ](/docs/sdks/capacitor/zip/)
* [ Cordova ](/docs/sdks/cordova/)
* [ Cloud ](/docs/cloud/)
* [ Integrations ](/docs/cloud/live-updates/integrations/)
* Concepts
* Reference
* [ Troubleshooting ](/docs/cloud/live-updates/troubleshooting/)
* [ FAQ ](/docs/cloud/live-updates/faq/)
* [ Native Builds ](/docs/cloud/native-builds/)
* [ Set Up Environments ](/docs/cloud/native-builds/environments/)
* [ Set Up Native Configurations ](/docs/cloud/native-builds/native-configurations/)
* [ Auto-Increment Build Numbers ](/docs/cloud/native-builds/auto-incrementing-build-numbers/)
* [ Configure the Web Build Script ](/docs/cloud/native-builds/web-build-script/)
* [ Build from a Monorepo ](/docs/cloud/native-builds/monorepo/)
* [ Use pnpm, Yarn, or bun ](/docs/cloud/native-builds/package-managers/)
* [ Install Private npm Packages ](/docs/cloud/native-builds/npm-private-registry/)
* [ Override the Java Version ](/docs/cloud/native-builds/override-java-version/)
* [ Custom iOS Provisioning Profiles ](/docs/cloud/native-builds/custom-ios-provisioning-profiles/)
* [ Build without Git ](/docs/cloud/native-builds/build-without-git/)
* [ Access Git Behind a Firewall ](/docs/cloud/native-builds/firewall-access/)
* [ Integrations ](/docs/cloud/native-builds/integrations/)
* Reference
* [ Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [ FAQ ](/docs/cloud/native-builds/faq/)
* [ App Store Publishing ](/docs/cloud/app-store-publishing/)
* [ Submit a Build ](/docs/cloud/app-store-publishing/submit-a-build/)
* [ Submit Automatically After a Build ](/docs/cloud/app-store-publishing/submit-automatically/)
* [ Troubleshooting ](/docs/cloud/app-store-publishing/troubleshooting/)
* [ FAQ ](/docs/cloud/app-store-publishing/faq/)
* [ Automations ](/docs/cloud/automations/)
* [ Reference ](/docs/cloud/automations/reference/)
* [ Troubleshooting ](/docs/cloud/automations/troubleshooting/)
* [ FAQ ](/docs/cloud/automations/faq/)
* [ Assist ](/docs/cloud/assist/)
* [ CLI ](/docs/cloud/cli/)
* APIs and SDKs
* [ Webhooks ](/docs/cloud/webhooks/)
* [ Integrations ](/docs/cloud/integrations/)
* Notifications
* Account
* [ Organization ](/docs/cloud/organizations/)
* [ Two-Factor Enforcement ](/docs/cloud/organizations/two-factor-authentication/)
* [ Network Restrictions ](/docs/cloud/organizations/network-restrictions/)
* [ Audit Logs ](/docs/cloud/organizations/audit-logs/)
* [ Billing ](/docs/cloud/organizations/billing/)
* [ License Keys ](/docs/cloud/license-keys/)
* [ AI ](/docs/ai/)
* [ Insiders ](/docs/insiders/)
* [ Billing & Plans ](/docs/insiders/billing-and-plans/)
* [ FAQ ](/docs/insiders/faq/)
* [ License ](https://capawesome.io/legal/eula/)
* [ Support ](/docs/support/)
* [ Contributing ](/docs/contributing/)
* Contributing code
* [ Code of Conduct ](/docs/contributing/code-of-conduct/)
* [ Questions ](https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/participating-in-a-discussion#creating-a-discussion)
* [ Blog ](/blog/)
* Categories

* [ iOS ](#ios)
* [ Configuration ](#configuration)
* [ Usage ](#usage)
* [ API ](#api)
* [ Type Aliases ](#type-aliases)
* [ Enums ](#enums)
* [ HTTP Sync ](#http-sync)
* [ FAQ ](#faq)
* [ Related Plugins ](#related-plugins)
* [ Newsletter ](#newsletter)
* [ Changelog ](#changelog)
* [ Breaking Changes ](#breaking-changes)
* [ License ](#license)

Build and Ship Mobile Apps Faster

Cloud builds, OTA live updates, and automated store releases — everything your mobile team needs in one platform.

[Start for free ](https://console.cloud.capawesome.io/?utm%5Fsource=docs&utm%5Fmedium=sidebar%5Fcta&utm%5Fcampaign=docs%5Fcta) [See our plans ](https://capawesome.io/pricing/?utm%5Fsource=docs&utm%5Fmedium=sidebar%5Fcta&utm%5Fcampaign=docs%5Fcta) 

# Capacitor Geofences Plugin[¶](#capacitor-geofences-plugin "Permanent link")

Capacitor plugin for monitoring OS-managed geofences (region monitoring) on Android and iOS. Detects enter, exit and dwell transitions even while the app is in the background or terminated.

[ ![Deliver Live Updates to your Capacitor app with Capawesome Cloud](../../../assets/external/cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.69628c3f.png) ](https://cloud.capawesome.io/) 

## Features[¶](#features "Permanent link")

The Capacitor Geofences plugin lets your app react when a device enters or leaves a geographic region, using the battery-efficient region monitoring built into the operating system. Here are some of the key features:

* 🌍 **OS-Managed Regions**: Uses `GeofencingClient` on Android and `CLLocationManager` region monitoring on iOS, so transitions are detected by the system with minimal battery impact.
* 🔔 **Transition Events**: Get notified about enter, exit and (on Android) dwell transitions.
* 💀 **Killed-App Delivery**: Transitions that occur while the app is terminated are queued and replayed on the next launch, and can trigger a native local notification.
* ☁️ **HTTP Sync**: Upload transitions to your own server with an on-device queue, automatic retries and at-least-once delivery — even while the app is in the background or terminated.
* 🔁 **Auto Re-Registration**: On Android, geofences are automatically re-registered after a device reboot or an app update.
* 🔒 **Public APIs Only**: Built exclusively on public platform APIs, so it is safe for App Review and resilient to OS updates.
* 🤝 **Compatibility**: Works hand in hand with the [Background Geolocation](https://capawesome.io/docs/sdks/capacitor/background-geolocation/) plugin for continuous location tracking.
* 📦 **CocoaPods & SPM**: Supports CocoaPods and Swift Package Manager for iOS.
* 🔁 **Up-to-date**: Always supports the latest Capacitor version.
* ⭐️ **Support**: Priority support from the Capawesome Team.
* ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated.

Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!

## Use Cases[¶](#use-cases "Permanent link")

The Geofences plugin is typically used whenever an app needs to react when a device enters or leaves a specific area, for example:

* **Location-based reminders**: Notify users when they arrive at or leave a place, for example a store, an office, or their home.
* **Attendance and check-ins**: Check users in or out automatically when they enter or leave a site, using dwell transitions on Android to confirm that they actually stayed.
* **Proximity marketing**: Display a native local notification with an offer when a customer walks near one of your branches, even while the app is terminated.
* **Field service and logistics**: Record arrivals at and departures from customer sites or depots without draining the battery through continuous tracking.
* **Safety zones**: Alert caregivers or fleet managers when a person or vehicle leaves a defined safe area.
* **Context-aware apps**: Adapt the app to the user's surroundings, for example by switching to a venue-specific view once the user is on site.

## Compatibility[¶](#compatibility "Permanent link")

| Plugin Version | Capacitor Version | Status         |
| -------------- | ----------------- | -------------- |
| 0.x.x          | \>=8.x.x          | Active support |

## Installation[¶](#installation "Permanent link")

This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands:

`[](#%5F%5Fcodelineno-0-1)npm config set @capawesome-team:registry https://npm.registry.capawesome.io
[](#%5F%5Fcodelineno-0-2)npm config set //npm.registry.capawesome.io/:_authToken <YOUR_LICENSE_KEY>
`

**Attention**: Replace `<YOUR_LICENSE_KEY>` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/).

Next, you can use our **AI-Assisted Setup** to install the plugin. Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:

`[](#%5F%5Fcodelineno-1-1)npx skills add capawesome-team/skills --skill capacitor-plugins
`

Then use the following prompt:

`` [](#%5F%5Fcodelineno-2-1)Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome-team/capacitor-geofences` plugin in my project.
 ``

If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:

`[](#%5F%5Fcodelineno-3-1)npm install @capawesome-team/capacitor-geofences
[](#%5F%5Fcodelineno-3-2)npx cap sync
`

### Android[¶](#android "Permanent link")

#### Permissions[¶](#permissions "Permanent link")

The plugin already declares the `ACCESS_FINE_LOCATION`, `POST_NOTIFICATIONS` and `RECEIVE_BOOT_COMPLETED` permissions in its manifest.

Geofencing additionally requires the **background location** permission. For [Google Play policy](https://support.google.com/googleplay/android-developer/answer/9799150) reasons, this permission is **not** declared by the plugin and must be added to your app's `AndroidManifest.xml` before or after the `application` tag:

`[](#%5F%5Fcodelineno-4-1)<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
`

Starting with Android 10 (API level 29), the background location permission cannot be requested together with the foreground location permission. You must first request the foreground location permission and only afterwards request the background location permission (see [Check and request permissions](#check-and-request-permissions)).

#### Proguard[¶](#proguard "Permanent link")

If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file:

`[](#%5F%5Fcodelineno-5-1)-keep class io.capawesome.capacitorjs.plugins.** { *; }
`

#### Variables[¶](#variables "Permanent link")

If needed, you can define the following project variables in your app's `variables.gradle` file to change the default versions of the dependencies:

* `$androidxWorkVersion` version of `androidx.work:work-runtime` (default: `2.11.2`)
* `$playServicesLocationVersion` version of `com.google.android.gms:play-services-location` (default: `21.4.0`)

This can be useful if you encounter dependency conflicts with other plugins in your project.

### iOS[¶](#ios "Permanent link")

#### Privacy Descriptions[¶](#privacy-descriptions "Permanent link")

Add the `NSLocationWhenInUseUsageDescription` and `NSLocationAlwaysAndWhenInUseUsageDescription` keys to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the location:

`[](#%5F%5Fcodelineno-6-1)<key>NSLocationWhenInUseUsageDescription</key>
[](#%5F%5Fcodelineno-6-2)<string>The app needs access to your location to monitor geofences.</string>
[](#%5F%5Fcodelineno-6-3)<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
[](#%5F%5Fcodelineno-6-4)<string>The app needs access to your location to monitor geofences while it is in the background.</string>
`

If the keys are missing, `addGeofences(...)` and `requestPermissions(...)` reject with a clear error message.

## Configuration[¶](#configuration "Permanent link")

No configuration required for this plugin.

## Usage[¶](#usage "Permanent link")

The following examples show how to add, retrieve, and remove geofences, listen for transitions, sync transitions to a server, and check and request permissions.

### Add geofences[¶](#add-geofences "Permanent link")

Add one or more circular regions to be monitored by the operating system. Optionally, provide a `notification` that is displayed natively when a transition is detected, which is especially useful while the app is terminated. On Android, an enter transition is triggered immediately if the device is already inside a geofence that was just added, while iOS only reports a transition once the device crosses the boundary. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-7-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-7-2)
[](#%5F%5Fcodelineno-7-3)const addGeofences = async () => {
[](#%5F%5Fcodelineno-7-4)  const { ids } = await Geofences.addGeofences({
[](#%5F%5Fcodelineno-7-5)    geofences: [
[](#%5F%5Fcodelineno-7-6)      {
[](#%5F%5Fcodelineno-7-7)        latitude: 37.33182,
[](#%5F%5Fcodelineno-7-8)        longitude: -122.03118,
[](#%5F%5Fcodelineno-7-9)        radius: 200,
[](#%5F%5Fcodelineno-7-10)        notification: {
[](#%5F%5Fcodelineno-7-11)          title: 'Welcome',
[](#%5F%5Fcodelineno-7-12)          text: 'You have entered the area.',
[](#%5F%5Fcodelineno-7-13)        },
[](#%5F%5Fcodelineno-7-14)      },
[](#%5F%5Fcodelineno-7-15)    ],
[](#%5F%5Fcodelineno-7-16)  });
[](#%5F%5Fcodelineno-7-17)  return ids;
[](#%5F%5Fcodelineno-7-18)};
`

### Retrieve geofences[¶](#retrieve-geofences "Permanent link")

Retrieve all geofences that are currently being monitored. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-8-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-8-2)
[](#%5F%5Fcodelineno-8-3)const getGeofences = async () => {
[](#%5F%5Fcodelineno-8-4)  const { geofences } = await Geofences.getGeofences();
[](#%5F%5Fcodelineno-8-5)  return geofences;
[](#%5F%5Fcodelineno-8-6)};
`

### Remove geofences[¶](#remove-geofences "Permanent link")

Remove specific geofences by their identifier or remove all of them at once. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-9-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-9-2)
[](#%5F%5Fcodelineno-9-3)const removeGeofences = async (ids: string[]) => {
[](#%5F%5Fcodelineno-9-4)  await Geofences.removeGeofences({ ids });
[](#%5F%5Fcodelineno-9-5)};
[](#%5F%5Fcodelineno-9-6)
[](#%5F%5Fcodelineno-9-7)const removeAllGeofences = async () => {
[](#%5F%5Fcodelineno-9-8)  await Geofences.removeAllGeofences();
[](#%5F%5Fcodelineno-9-9)};
`

### Listen for geofence transitions[¶](#listen-for-geofence-transitions "Permanent link")

Get notified when the device enters, exits, or (on Android) dwells inside a geofence. Transitions that occurred while the app was in the background or terminated are queued and replayed once the first listener is registered, so register it as early as possible. The replay buffer holds at most `100` transitions. Only available on Android and iOS:

`` [](#%5F%5Fcodelineno-10-1)import { Geofences, TransitionType } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-10-2)
[](#%5F%5Fcodelineno-10-3)const addListener = async () => {
[](#%5F%5Fcodelineno-10-4)  await Geofences.addListener('geofenceTransition', (event) => {
[](#%5F%5Fcodelineno-10-5)    if (event.transitionType === TransitionType.Enter) {
[](#%5F%5Fcodelineno-10-6)      console.log(`Entered the geofence ${event.id}.`);
[](#%5F%5Fcodelineno-10-7)    }
[](#%5F%5Fcodelineno-10-8)  });
[](#%5F%5Fcodelineno-10-9)};
 ``

### Sync transitions to a server[¶](#sync-transitions-to-a-server "Permanent link")

Configure the plugin to upload every transition to your own server, even while the app is in the background or terminated. The configuration is persisted natively, so it only needs to be set once (e.g. after sign-in). Failed upload attempts are reported via the `syncFailed` event. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-11-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-11-2)
[](#%5F%5Fcodelineno-11-3)const configureSync = async () => {
[](#%5F%5Fcodelineno-11-4)  await Geofences.addListener('syncFailed', (event) => {
[](#%5F%5Fcodelineno-11-5)    console.error('Upload failed: ', event.statusCode, event.message);
[](#%5F%5Fcodelineno-11-6)  });
[](#%5F%5Fcodelineno-11-7)  await Geofences.configureSync({
[](#%5F%5Fcodelineno-11-8)    url: 'https://api.example.com/transitions',
[](#%5F%5Fcodelineno-11-9)    headers: {
[](#%5F%5Fcodelineno-11-10)      Authorization: 'Bearer eyJhbGciOi...',
[](#%5F%5Fcodelineno-11-11)    },
[](#%5F%5Fcodelineno-11-12)    extras: {
[](#%5F%5Fcodelineno-11-13)      userId: 'abc',
[](#%5F%5Fcodelineno-11-14)    },
[](#%5F%5Fcodelineno-11-15)  });
[](#%5F%5Fcodelineno-11-16)};
[](#%5F%5Fcodelineno-11-17)
[](#%5F%5Fcodelineno-11-18)const disableSync = async () => {
[](#%5F%5Fcodelineno-11-19)  await Geofences.disableSync();
[](#%5F%5Fcodelineno-11-20)};
`

See [HTTP Sync](#http-sync) for the server contract, response handling and queue behavior.

### Check and request permissions[¶](#check-and-request-permissions "Permanent link")

Geofencing requires the **Always** location authorization on iOS and the **background location** permission on Android. Because of the platform restrictions described in the [Installation](#installation) section, the permissions must be requested in two steps:

`[](#%5F%5Fcodelineno-12-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-12-2)
[](#%5F%5Fcodelineno-12-3)const checkPermissions = async () => {
[](#%5F%5Fcodelineno-12-4)  return Geofences.checkPermissions();
[](#%5F%5Fcodelineno-12-5)};
[](#%5F%5Fcodelineno-12-6)
[](#%5F%5Fcodelineno-12-7)const requestPermissions = async () => {
[](#%5F%5Fcodelineno-12-8)  // Step 1: Request the foreground location permission.
[](#%5F%5Fcodelineno-12-9)  let status = await Geofences.requestPermissions({
[](#%5F%5Fcodelineno-12-10)    permissions: ['location'],
[](#%5F%5Fcodelineno-12-11)  });
[](#%5F%5Fcodelineno-12-12)  // Step 2: Request the background location permission.
[](#%5F%5Fcodelineno-12-13)  if (status.location === 'granted') {
[](#%5F%5Fcodelineno-12-14)    status = await Geofences.requestPermissions({
[](#%5F%5Fcodelineno-12-15)      permissions: ['backgroundLocation'],
[](#%5F%5Fcodelineno-12-16)    });
[](#%5F%5Fcodelineno-12-17)  }
[](#%5F%5Fcodelineno-12-18)  // Optionally: Request the notifications permission.
[](#%5F%5Fcodelineno-12-19)  await Geofences.requestPermissions({ permissions: ['notifications'] });
[](#%5F%5Fcodelineno-12-20)  return status;
[](#%5F%5Fcodelineno-12-21)};
`

If a permission was permanently denied, send the user to the native app settings:

`[](#%5F%5Fcodelineno-13-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-13-2)
[](#%5F%5Fcodelineno-13-3)const openSettings = async () => {
[](#%5F%5Fcodelineno-13-4)  await Geofences.openSettings();
[](#%5F%5Fcodelineno-13-5)};
`

### Remove all listeners[¶](#remove-all-listeners "Permanent link")

Remove all listeners for this plugin when they are no longer needed:

`[](#%5F%5Fcodelineno-14-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-14-2)
[](#%5F%5Fcodelineno-14-3)const removeAllListeners = async () => {
[](#%5F%5Fcodelineno-14-4)  await Geofences.removeAllListeners();
[](#%5F%5Fcodelineno-14-5)};
`

## API[¶](#api "Permanent link")

* [addGeofences(...)](#addgeofences)
* [checkPermissions()](#checkpermissions)
* [clearSyncQueue()](#clearsyncqueue)
* [configureSync(...)](#configuresync)
* [disableSync()](#disablesync)
* [getGeofences()](#getgeofences)
* [getSyncStatus()](#getsyncstatus)
* [openSettings()](#opensettings)
* [removeAllGeofences()](#removeallgeofences)
* [removeGeofences(...)](#removegeofences)
* [requestPermissions(...)](#requestpermissions)
* [triggerSync()](#triggersync)
* [addListener('geofenceTransition', ...)](#addlistenergeofencetransition-)
* [addListener('syncFailed', ...)](#addlistenersyncfailed-)
* [removeAllListeners()](#removealllisteners)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)
* [Enums](#enums)

### addGeofences(...)[¶](#addgeofences "Permanent link")

`[](#%5F%5Fcodelineno-15-1)addGeofences(options: AddGeofencesOptions) => Promise<AddGeofencesResult>
`

Add one or more geofences to be monitored.

On **Android**, an enter transition is triggered immediately if the device is already inside a geofence that was just added. On **iOS**, no transition is triggered until the device crosses the boundary of the geofence.

Only available on Android and iOS.

| Param       | Type                                        |
| ----------- | ------------------------------------------- |
| **options** | [AddGeofencesOptions](#addgeofencesoptions) |

**Returns:** `Promise<[AddGeofencesResult](#addgeofencesresult)>`

**Since:** 0.0.1

---

### checkPermissions()[¶](#checkpermissions "Permanent link")

`[](#%5F%5Fcodelineno-16-1)checkPermissions() => Promise<PermissionStatus>
`

Check permissions for the plugin.

**Returns:** `Promise<[PermissionStatus](#permissionstatus)>`

**Since:** 0.0.1

---

### clearSyncQueue()[¶](#clearsyncqueue "Permanent link")

`[](#%5F%5Fcodelineno-17-1)clearSyncQueue() => Promise<void>
`

Delete all buffered transitions from the sync queue.

This method can be called with or without a sync configuration, for example to discard pending transitions when the user signs out.

Only available on Android and iOS.

**Since:** 0.0.1

---

### configureSync(...)[¶](#configuresync "Permanent link")

`[](#%5F%5Fcodelineno-18-1)configureSync(options: ConfigureSyncOptions) => Promise<void>
`

Configure the upload of geofence transitions to a server.

The configuration is persisted natively. Once configured, every geofence transition is buffered in a local queue and uploaded to the configured URL, even while the app is in the background or terminated.

Call this method again to update the configuration, for example with a new authorization header, or `disableSync()` to stop uploading transitions.

Only available on Android and iOS.

| Param       | Type                                          |
| ----------- | --------------------------------------------- |
| **options** | [ConfigureSyncOptions](#configuresyncoptions) |

**Since:** 0.0.1

---

### disableSync()[¶](#disablesync "Permanent link")

`[](#%5F%5Fcodelineno-19-1)disableSync() => Promise<void>
`

Remove the persisted sync configuration so that no more transitions are buffered or uploaded.

Transitions that are already buffered remain in the sync queue until they are deleted via `clearSyncQueue()`.

Only available on Android and iOS.

**Since:** 0.0.1

---

### getGeofences()[¶](#getgeofences "Permanent link")

`[](#%5F%5Fcodelineno-20-1)getGeofences() => Promise<GetGeofencesResult>
`

Get all geofences that are currently being monitored.

Only available on Android and iOS.

**Returns:** `Promise<[GetGeofencesResult](#getgeofencesresult)>`

**Since:** 0.0.1

---

### getSyncStatus()[¶](#getsyncstatus "Permanent link")

`[](#%5F%5Fcodelineno-21-1)getSyncStatus() => Promise<GetSyncStatusResult>
`

Get the current status of the sync queue.

This method can be called with or without a sync configuration.

Only available on Android and iOS.

**Returns:** `Promise<[GetSyncStatusResult](#getsyncstatusresult)>`

**Since:** 0.0.1

---

### openSettings()[¶](#opensettings "Permanent link")

`[](#%5F%5Fcodelineno-22-1)openSettings() => Promise<void>
`

Opens the native app settings page to allow the user to grant the app the required permissions.

Only available on Android and iOS.

**Since:** 0.0.1

---

### removeAllGeofences()[¶](#removeallgeofences "Permanent link")

`[](#%5F%5Fcodelineno-23-1)removeAllGeofences() => Promise<void>
`

Remove all geofences that are currently being monitored.

Only available on Android and iOS.

**Since:** 0.0.1

---

### removeGeofences(...)[¶](#removegeofences "Permanent link")

`[](#%5F%5Fcodelineno-24-1)removeGeofences(options: RemoveGeofencesOptions) => Promise<void>
`

Remove one or more geofences by their identifier.

Only available on Android and iOS.

| Param       | Type                                              |
| ----------- | ------------------------------------------------- |
| **options** | [RemoveGeofencesOptions](#removegeofencesoptions) |

**Since:** 0.0.1

---

### requestPermissions(...)[¶](#requestpermissions "Permanent link")

`[](#%5F%5Fcodelineno-25-1)requestPermissions(options?: RequestPermissionsOptions | undefined) => Promise<PermissionStatus>
`

Request permissions for the plugin.

The `backgroundLocation` permission must be requested in a **second**, separate call after the `location` permission has been granted:

* On **Android 11+**, the user is taken to the location settings of the app where the `Allow all the time` option must be selected.
* On **iOS**, the operating system presents the upgrade prompt that asks the user to change the permission from `While Using the App` to `Always`.

| Param       | Type                                                    |
| ----------- | ------------------------------------------------------- |
| **options** | [RequestPermissionsOptions](#requestpermissionsoptions) |

**Returns:** `Promise<[PermissionStatus](#permissionstatus)>`

**Since:** 0.0.1

---

### triggerSync()[¶](#triggersync "Permanent link")

`[](#%5F%5Fcodelineno-26-1)triggerSync() => Promise<void>
`

Immediately attempt to upload all buffered transitions.

Any pending retry backoff is cancelled and a new upload attempt is started right away. The promise resolves as soon as the attempt has been scheduled, not when the transitions have been delivered.

The promise rejects if no sync configuration exists.

Only available on Android and iOS.

**Since:** 0.0.1

---

### addListener('geofenceTransition', ...)[¶](#addlistenergeofencetransition "Permanent link")

`[](#%5F%5Fcodelineno-27-1)addListener(eventName: 'geofenceTransition', listenerFunc: (event: GeofenceTransitionEvent) => void) => Promise<PluginListenerHandle>
`

Called when a geofence transition (enter, exit or dwell) is detected.

Transitions that occurred while the app was terminated are queued and replayed in order once the first listener for this event is registered. Register the listener as early as possible to avoid missing them.

Only available on Android and iOS.

| Param            | Type                                                                 |
| ---------------- | -------------------------------------------------------------------- |
| **eventName**    | 'geofenceTransition'                                                 |
| **listenerFunc** | (event: [GeofenceTransitionEvent](#geofencetransitionevent)) => void |

**Returns:** `Promise<[PluginListenerHandle](#pluginlistenerhandle)>`

**Since:** 0.0.1

---

### addListener('syncFailed', ...)[¶](#addlistenersyncfailed "Permanent link")

`[](#%5F%5Fcodelineno-28-1)addListener(eventName: 'syncFailed', listenerFunc: (event: SyncFailedEvent) => void) => Promise<PluginListenerHandle>
`

Called when an upload attempt of buffered transitions fails.

The affected transitions remain in the queue and are retried automatically unless the server rejected them permanently.

Only available on Android and iOS.

| Param            | Type                                                 |
| ---------------- | ---------------------------------------------------- |
| **eventName**    | 'syncFailed'                                         |
| **listenerFunc** | (event: [SyncFailedEvent](#syncfailedevent)) => void |

**Returns:** `Promise<[PluginListenerHandle](#pluginlistenerhandle)>`

**Since:** 0.0.1

---

### removeAllListeners()[¶](#removealllisteners "Permanent link")

`[](#%5F%5Fcodelineno-29-1)removeAllListeners() => Promise<void>
`

Remove all listeners for this plugin.

**Since:** 0.0.1

---

### Interfaces[¶](#interfaces "Permanent link")

#### AddGeofencesResult[¶](#addgeofencesresult "Permanent link")

| Prop    | Type       | Description                                                                                          | Since |
| ------- | ---------- | ---------------------------------------------------------------------------------------------------- | ----- |
| **ids** | string\[\] | The identifiers of the added geofences. The order matches the order of the geofences in the request. | 0.0.1 |

#### AddGeofencesOptions[¶](#addgeofencesoptions "Permanent link")

| Prop          | Type         | Description           | Since |
| ------------- | ------------ | --------------------- | ----- |
| **geofences** | Geofence\[\] | The geofences to add. | 0.0.1 |

#### Geofence[¶](#geofence "Permanent link")

| Prop                          | Type                                          | Description                                                                                                                                                         | Default | Since |
| ----------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- |
| **androidExpirationDuration** | number                                        | The time in milliseconds after which the geofence is automatically removed. Only available on Android.                                                              |         | 0.0.1 |
| **androidLoiteringDelay**     | number                                        | The time in milliseconds the device must dwell inside the geofence before a dwell transition event is triggered. Only available on Android.                         |         | 0.0.1 |
| **androidNotifyOnDwell**      | boolean                                       | Whether a transition event should be triggered when the device dwells inside the geofence. Only available on Android.                                               | false   | 0.0.1 |
| **id**                        | string                                        | A unique identifier for the geofence. If not provided, a random identifier (UUID) is generated and returned in the result of the addGeofences(...) method.          |         | 0.0.1 |
| **latitude**                  | number                                        | The latitude of the center of the geofence in degrees.                                                                                                              |         | 0.0.1 |
| **longitude**                 | number                                        | The longitude of the center of the geofence in degrees.                                                                                                             |         | 0.0.1 |
| **radius**                    | number                                        | The radius of the geofence in meters. Apple recommends a radius of at least 200 meters, as smaller radii may not trigger transitions reliably.                      |         | 0.0.1 |
| **notifyOnEnter**             | boolean                                       | Whether a transition event should be triggered when the device enters the geofence.                                                                                 | true    | 0.0.1 |
| **notifyOnExit**              | boolean                                       | Whether a transition event should be triggered when the device exits the geofence.                                                                                  | true    | 0.0.1 |
| **notification**              | [GeofenceNotification](#geofencenotification) | A local notification to display natively when a transition for this geofence is detected. This is especially useful to notify the user while the app is terminated. |         | 0.0.1 |

#### GeofenceNotification[¶](#geofencenotification "Permanent link")

| Prop      | Type   | Description                        | Since |
| --------- | ------ | ---------------------------------- | ----- |
| **title** | string | The title of the notification.     | 0.0.1 |
| **text**  | string | The body text of the notification. | 0.0.1 |

#### PermissionStatus[¶](#permissionstatus "Permanent link")

| Prop                   | Type                                | Description                                                                                                                                                                        | Since |
| ---------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **location**           | [PermissionState](#permissionstate) | The permission state for using the location while the app is in use.                                                                                                               | 0.0.1 |
| **backgroundLocation** | [PermissionState](#permissionstate) | The permission state for using the location while the app is in the background. This permission is required to monitor geofences while the app is in the background or terminated. | 0.0.1 |
| **notifications**      | [PermissionState](#permissionstate) | The permission state for displaying local notifications on a transition.                                                                                                           | 0.0.1 |

#### ConfigureSyncOptions[¶](#configuresyncoptions "Permanent link")

| Prop        | Type                                | Description                                                                                 | Since                                                                                                |       |
| ----------- | ----------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----- |
| **extras**  | { \[key: string\]: string \| number | boolean; }                                                                                  | Static metadata that is attached to every upload request as the extras property of the request body. | 0.0.1 |
| **headers** | { \[key: string\]: string; }        | Static HTTP headers that are sent with every upload request, for example for authorization. | 0.0.1                                                                                                |       |
| **url**     | string                              | The URL the transitions are uploaded to via HTTP POST.                                      | 0.0.1                                                                                                |       |

#### GetGeofencesResult[¶](#getgeofencesresult "Permanent link")

| Prop          | Type         | Description                                       | Since |
| ------------- | ------------ | ------------------------------------------------- | ----- |
| **geofences** | Geofence\[\] | The geofences that are currently being monitored. | 0.0.1 |

#### GetSyncStatusResult[¶](#getsyncstatusresult "Permanent link")

| Prop             | Type           | Description                                                                                                                                                                                                                                  | Since |
| ---------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **droppedCount** | number         | The number of transitions that were dropped without being uploaded since the queue was last empty or cleared, for example because the queue was full or the server rejected them permanently. This counter is persisted across app restarts. | 0.0.1 |
| **lastSyncedAt** | number \| null | The time at which the last batch of transitions was uploaded successfully in milliseconds since the Unix epoch or null if no batch has been uploaded successfully yet. This value is persisted across app restarts.                          | 0.0.1 |
| **pendingCount** | number         | The number of transitions that are currently buffered in the sync queue.                                                                                                                                                                     | 0.0.1 |

#### RemoveGeofencesOptions[¶](#removegeofencesoptions "Permanent link")

| Prop    | Type       | Description                                 | Since |
| ------- | ---------- | ------------------------------------------- | ----- |
| **ids** | string\[\] | The identifiers of the geofences to remove. | 0.0.1 |

#### RequestPermissionsOptions[¶](#requestpermissionsoptions "Permanent link")

| Prop            | Type               | Description                 | Default                         | Since |
| --------------- | ------------------ | --------------------------- | ------------------------------- | ----- |
| **permissions** | PermissionType\[\] | The permissions to request. | \['location', 'notifications'\] | 0.0.1 |

#### PluginListenerHandle[¶](#pluginlistenerhandle "Permanent link")

| Prop       | Type                |
| ---------- | ------------------- |
| **remove** | () => Promise<void> |

#### GeofenceTransitionEvent[¶](#geofencetransitionevent "Permanent link")

| Prop               | Type                              | Description                                                                                                                                              | Since |
| ------------------ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **id**             | string                            | The identifier of the geofence that triggered the transition.                                                                                            | 0.0.1 |
| **transitionType** | [TransitionType](#transitiontype) | The type of the transition.                                                                                                                              | 0.0.1 |
| **timestamp**      | number                            | The time the transition was detected in milliseconds since epoch.                                                                                        | 0.0.1 |
| **latitude**       | number \| null                    | The latitude of the location that triggered the transition in degrees. On **iOS**, this is always null because the triggering location is not provided.  | 0.0.1 |
| **longitude**      | number \| null                    | The longitude of the location that triggered the transition in degrees. On **iOS**, this is always null because the triggering location is not provided. | 0.0.1 |

#### SyncFailedEvent[¶](#syncfailedevent "Permanent link")

| Prop           | Type   | Description                                                       | Since |
| -------------- | ------ | ----------------------------------------------------------------- | ----- |
| **message**    | string | The error message.                                                | 0.0.1 |
| **statusCode** | number | The HTTP status code of the response, if a response was received. | 0.0.1 |

### Type Aliases[¶](#type-aliases "Permanent link")

#### PermissionState[¶](#permissionstate "Permanent link")

`'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'`

#### PermissionType[¶](#permissiontype "Permanent link")

`'location' | 'backgroundLocation' | 'notifications'`

### Enums[¶](#enums "Permanent link")

#### TransitionType[¶](#transitiontype "Permanent link")

| Members   | Value   | Description                                                            | Since |
| --------- | ------- | ---------------------------------------------------------------------- | ----- |
| **Dwell** | 'DWELL' | The device has dwelled inside the geofence. Only available on Android. | 0.0.1 |
| **Enter** | 'ENTER' | The device has entered the geofence.                                   | 0.0.1 |
| **Exit**  | 'EXIT'  | The device has exited the geofence.                                    | 0.0.1 |

## HTTP Sync[¶](#http-sync "Permanent link")

The plugin can upload every geofence transition to your own server without involving JavaScript. Transitions are buffered in a local queue, uploaded to the configured URL and only deleted from the queue after the server has acknowledged them. Since the whole pipeline runs natively, it keeps working while the web view is suspended — and, unlike a watch session of the [Background Geolocation](https://capawesome.io/docs/sdks/capacitor/background-geolocation/) plugin, even while the app is terminated.

### Options[¶](#options "Permanent link")

Call `configureSync(...)` once to enable the upload pipeline. The configuration is persisted natively and applies to every future transition until `disableSync()` is called. Failed upload attempts are reported via the `syncFailed` event:

`[](#%5F%5Fcodelineno-30-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-30-2)
[](#%5F%5Fcodelineno-30-3)const configureSync = async () => {
[](#%5F%5Fcodelineno-30-4)  await Geofences.configureSync({
[](#%5F%5Fcodelineno-30-5)    url: 'https://api.example.com/transitions',
[](#%5F%5Fcodelineno-30-6)    headers: {
[](#%5F%5Fcodelineno-30-7)      Authorization: 'Bearer eyJhbGciOi...',
[](#%5F%5Fcodelineno-30-8)    },
[](#%5F%5Fcodelineno-30-9)    extras: {
[](#%5F%5Fcodelineno-30-10)      userId: 'abc',
[](#%5F%5Fcodelineno-30-11)    },
[](#%5F%5Fcodelineno-30-12)  });
[](#%5F%5Fcodelineno-30-13)};
`

The queue itself can be inspected and controlled with or without a sync configuration:

`` [](#%5F%5Fcodelineno-31-1)import { Geofences } from '@capawesome-team/capacitor-geofences';
[](#%5F%5Fcodelineno-31-2)
[](#%5F%5Fcodelineno-31-3)const getSyncStatus = async () => {
[](#%5F%5Fcodelineno-31-4)  const { pendingCount, droppedCount, lastSyncedAt } = await Geofences.getSyncStatus();
[](#%5F%5Fcodelineno-31-5)  console.log(`${pendingCount} transitions pending, ${droppedCount} dropped, last upload: ${lastSyncedAt}`);
[](#%5F%5Fcodelineno-31-6)};
[](#%5F%5Fcodelineno-31-7)
[](#%5F%5Fcodelineno-31-8)const triggerSync = async () => {
[](#%5F%5Fcodelineno-31-9)  await Geofences.triggerSync();
[](#%5F%5Fcodelineno-31-10)};
[](#%5F%5Fcodelineno-31-11)
[](#%5F%5Fcodelineno-31-12)const clearSyncQueue = async () => {
[](#%5F%5Fcodelineno-31-13)  await Geofences.clearSyncQueue();
[](#%5F%5Fcodelineno-31-14)};
 ``

### Server Contract[¶](#server-contract "Permanent link")

Transitions are uploaded with an HTTP `POST` request and the `Content-Type: application/json; charset=utf-8` header. Your own `headers` are applied afterwards and may override it. The request body looks as follows:

`[](#%5F%5Fcodelineno-32-1){
[](#%5F%5Fcodelineno-32-2)  "transitions": [
[](#%5F%5Fcodelineno-32-3)    {
[](#%5F%5Fcodelineno-32-4)      "id": "1b8935d6-27b4-4a5c-9f0f-4a5c9f0f1b89",
[](#%5F%5Fcodelineno-32-5)      "geofenceId": "2ca23ff9-b95d-4962-b64f-3e1efe6f2e7d",
[](#%5F%5Fcodelineno-32-6)      "transitionType": "ENTER",
[](#%5F%5Fcodelineno-32-7)      "timestamp": 1723291200000,
[](#%5F%5Fcodelineno-32-8)      "latitude": 52.52,
[](#%5F%5Fcodelineno-32-9)      "longitude": 13.405
[](#%5F%5Fcodelineno-32-10)    }
[](#%5F%5Fcodelineno-32-11)  ],
[](#%5F%5Fcodelineno-32-12)  "extras": { "userId": "abc" }
[](#%5F%5Fcodelineno-32-13)}
`

Every entry of the `transitions` array is a [GeofenceTransitionEvent](#geofencetransitionevent) object whose `id` property is the identifier of the transition and whose `geofenceId` property is the identifier of the geofence. On iOS, `latitude` and `longitude` are always `null`. The `extras` property is omitted entirely if the `extras` option was not provided.

**Idempotency**: The `id` is unique per transition. Transitions are delivered **at least once**, so the same transition may be uploaded more than once, for example if the acknowledgment of the server is lost on the way back. Deduplicate the transitions on the server by `id` to make the upload idempotent.

### Response Handling[¶](#response-handling "Permanent link")

The response body is always ignored. Only the status code decides what happens to the uploaded transitions:

| Status Code                           | Behavior                                                                                                      |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| 2xx                                   | The transitions are acknowledged and deleted from the queue.                                                  |
| 408, 429, 5xx, network error, timeout | The transitions stay in the queue and are retried with an exponential backoff. A syncFailed event is emitted. |
| Any other status code                 | The transitions are **dropped permanently without being uploaded again** and a syncFailed event is emitted.   |

**Attention**: Transitions that the server rejects with any status code other than `2xx`, `408`, `429` or `5xx` (for example `400 Bad Request` or `422 Unprocessable Entity`) are deleted from the queue and **lost**. This is intentional: a permanently rejected upload must never block the queue forever. Make sure your endpoint answers with a retryable status code (e.g. `503`) if it is temporarily unable to accept transitions, and listen to the `syncFailed` event to detect such drops. Dropped transitions are also counted in the `droppedCount` property of `getSyncStatus()`.

The request timeout is 30 seconds. On Android, retries are scheduled via WorkManager with an exponential backoff starting at 10 seconds, so they survive a process death and even a device reboot. On iOS, retries start with a delay of 5 seconds and double after every attempt up to a maximum of 10 minutes while the app is alive; pending transitions are also uploaded on the next app launch and whenever a new transition is detected. The backoff is reset whenever `configureSync(...)` or `triggerSync()` is called.

### Queue Behavior[¶](#queue-behavior "Permanent link")

* **Persistence**: The queue is stored in the sandboxed app storage and survives app restarts, force-quits and device reboots. Transitions are only deleted after the server has acknowledged them or they were dropped.
* **Capacity**: The queue holds at most `1000` transitions. If the queue is full, the **oldest** transitions are dropped first.
* **Observability**: The `droppedCount` property of `getSyncStatus()` counts the transitions that were dropped since the queue was last empty or cleared. The counter is persisted across app restarts.
* **Delivery window**: As long as a sync configuration exists, transitions are uploaded as soon as they are detected — including while the app is in the background or terminated. On iOS, the upload while terminated happens during the short background wake-up in which the operating system delivers the region event.
* **After `disableSync()`**: No more transitions are buffered or uploaded. Transitions that are already buffered stay in the queue until they are deleted via `clearSyncQueue()` or a new sync configuration uploads them.

### Deliberately Not Supported[¶](#deliberately-not-supported "Permanent link")

The following features are intentionally not part of the sync pipeline:

* **Network constraints**: Uploads cannot be restricted to Wi-Fi or unmetered networks.
* **Response processing**: The response body of the server is always ignored, so the server cannot send commands back to the device.
* **Encryption at rest**: The queue is not encrypted. It is stored in the sandboxed app storage of the operating system.

## FAQ[¶](#faq "Permanent link")

### When should I use this plugin instead of the Background Geolocation plugin?[¶](#when-should-i-use-this-plugin-instead-of-the-background-geolocation-plugin "Permanent link")

Both plugins solve different problems and complement each other. The [Background Geolocation](https://capawesome.io/docs/sdks/capacitor/background-geolocation/) plugin continuously tracks the device's location and streams position updates to your app, which is what you need when you want the actual location trail. This plugin monitors circular regions and only notifies your app when the device crosses a region boundary, which is much cheaper on battery and can even deliver transitions while your app is terminated. Use Background Geolocation when you need the location trail, and Geofences when you only care about entering or leaving specific areas.

### How is this plugin different from other similar plugins?[¶](#how-is-this-plugin-different-from-other-similar-plugins "Permanent link")

It uses the operating system's own region monitoring — `GeofencingClient` on Android and Core Location on iOS — so enter, exit and dwell transitions are detected with minimal battery impact, even while your app is in the background or terminated. Transitions that occur while the app is killed are queued and replayed on the next launch and can trigger a native local notification, and on Android geofences are automatically re-registered after a reboot or app update. It's all exposed through one fully typed, actively maintained API with dedicated support; if you need the full location trail rather than boundary crossings, continuous tracking fits better, but for reacting to specific areas efficiently, this plugin is purpose-built.

### Is geofencing available on the web?[¶](#is-geofencing-available-on-the-web "Permanent link")

No. All methods are only available on Android and iOS. On the web, they reject with an unimplemented error.

### How many geofences can I register?[¶](#how-many-geofences-can-i-register "Permanent link")

Android allows up to 100 geofences per app, iOS up to 20 regions (a hard limit of the operating system). If you exceed the limit, `addGeofences(...)` rejects with the `GEOFENCE_LIMIT_EXCEEDED` error code. If your app needs more regions, register only the geofences closest to the user and update them as the user moves.

### What radius should I use for a geofence?[¶](#what-radius-should-i-use-for-a-geofence "Permanent link")

Apple recommends a radius of at least 200 meters, as smaller radii may not trigger transitions reliably. See the [Apple documentation](https://developer.apple.com/documentation/corelocation/monitoring-the-user-s-proximity-to-geographic-regions) for details. On Android, a radius of at least 100 meters is recommended. There is no fixed maximum radius on Android, while iOS clamps the radius to `maximumRegionMonitoringDistance`.

### Why does `addGeofences(...)` reject with a permission error?[¶](#why-does-addgeofences-reject-with-a-permission-error "Permanent link")

Geofencing requires the **Always** location authorization on iOS and the **background location** permission on Android. If only the "while in use" (foreground) permission is granted, `addGeofences(...)` rejects with the `PERMISSION_DENIED` error code. See [Check and request permissions](#check-and-request-permissions) for the required two-step request flow.

### What happens to transitions that occur while my app is in the background or terminated?[¶](#what-happens-to-transitions-that-occur-while-my-app-is-in-the-background-or-terminated "Permanent link")

Transitions detected while the app is in the background or terminated are queued and replayed in order once the first `geofenceTransition` listener is registered, so register the listener as early as possible after your app starts. The replay buffer holds at most `100` transitions. If more transitions are detected before your app registers a listener again, the **oldest** ones are dropped. If a geofence defines a `notification`, it is displayed natively regardless of the app state. If your server needs to know about transitions right away instead of on the next launch, configure [HTTP Sync](#http-sync), which uploads them natively at the moment they are detected. On Android, geofences are automatically re-registered after a device reboot or an app update, while on iOS the monitored regions are persisted by the operating system.

### Why don't I receive dwell transitions on iOS?[¶](#why-dont-i-receive-dwell-transitions-on-ios "Permanent link")

Dwell transitions are only supported on Android. On iOS, the `androidNotifyOnDwell` and `androidLoiteringDelay` options are ignored, and only enter and exit transitions are reported.

### Why are `latitude` and `longitude` `null` on iOS?[¶](#why-are-latitude-and-longitude-null-on-ios "Permanent link")

Core Location does not provide the triggering location for a region transition. If you need the geofence's coordinates, look them up by its `id` (you already know them because you added the geofence).

### Can I use this plugin with Ionic, React, Vue or Angular?[¶](#can-i-use-this-plugin-with-ionic-react-vue-or-angular "Permanent link")

Yes, the plugin is framework-agnostic. It works in any Capacitor app regardless of the web framework, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects.

## Related Plugins[¶](#related-plugins "Permanent link")

* [Background Geolocation](https://capawesome.io/docs/sdks/capacitor/background-geolocation/): Continuously track the device's location in the background.
* [Geocoder](https://capawesome.io/docs/sdks/capacitor/geocoder/): Convert between coordinates and human-readable addresses.

## Newsletter[¶](#newsletter "Permanent link")

Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).

## Changelog[¶](#changelog "Permanent link")

See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/geofences/CHANGELOG.md).

## Breaking Changes[¶](#breaking-changes "Permanent link")

See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/geofences/BREAKING.md).

## License[¶](#license "Permanent link")

See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/geofences/LICENSE).

August 15, 2026 

Back to top

```json
{"@context": "https://schema.org", "@graph": [{"@type": "TechArticle", "@id": "https://capawesome.io/docs/sdks/capacitor/geofences/#article", "headline": "Capacitor Geofences Plugin for Android & iOS", "name": "Capacitor Geofences Plugin for Android & iOS", "description": "Capacitor Geofences plugin to monitor circular regions on Android and iOS and receive enter and exit events, even while the app is terminated.", "inLanguage": "en", "url": "https://capawesome.io/docs/sdks/capacitor/geofences/", "mainEntityOfPage": "https://capawesome.io/docs/sdks/capacitor/geofences/", "author": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}, "publisher": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}, "about": {"@id": "https://capawesome.io/docs/sdks/capacitor/geofences/#software"}}, {"@type": "SoftwareSourceCode", "@id": "https://capawesome.io/docs/sdks/capacitor/geofences/#software", "name": "Capacitor Geofences Plugin for Android & iOS", "description": "Capacitor Geofences plugin to monitor circular regions on Android and iOS and receive enter and exit events, even while the app is terminated.", "url": "https://capawesome.io/docs/sdks/capacitor/geofences/", "programmingLanguage": "TypeScript", "runtimePlatform": "Capacitor", "codeRepository": "https://github.com/capawesome-team", "author": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}, "publisher": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}}]}
{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "When should I use this plugin instead of the Background Geolocation plugin?", "acceptedAnswer": {"@type": "Answer", "text": "Both plugins solve different problems and complement each other. The Background Geolocation plugin continuously tracks the device's location and streams position updates to your app, which is what you need when you want the actual location trail. This plugin monitors circular regions and only notifies your app when the device crosses a region boundary, which is much cheaper on battery and can even deliver transitions while your app is terminated. Use Background Geolocation when you need the location trail, and Geofences when you only care about entering or leaving specific areas."}}, {"@type": "Question", "name": "How is this plugin different from other similar plugins?", "acceptedAnswer": {"@type": "Answer", "text": "It uses the operating system's own region monitoring — GeofencingClient on Android and Core Location on iOS — so enter, exit and dwell transitions are detected with minimal battery impact, even while your app is in the background or terminated. Transitions that occur while the app is killed are queued and replayed on the next launch and can trigger a native local notification, and on Android geofences are automatically re-registered after a reboot or app update. It's all exposed through one fully typed, actively maintained API with dedicated support; if you need the full location trail rather than boundary crossings, continuous tracking fits better, but for reacting to specific areas efficiently, this plugin is purpose-built."}}, {"@type": "Question", "name": "Is geofencing available on the web?", "acceptedAnswer": {"@type": "Answer", "text": "No. All methods are only available on Android and iOS. On the web, they reject with an unimplemented error."}}, {"@type": "Question", "name": "How many geofences can I register?", "acceptedAnswer": {"@type": "Answer", "text": "Android allows up to 100 geofences per app, iOS up to 20 regions (a hard limit of the operating system). If you exceed the limit, addGeofences(...) rejects with the GEOFENCE_LIMIT_EXCEEDED error code. If your app needs more regions, register only the geofences closest to the user and update them as the user moves."}}, {"@type": "Question", "name": "What radius should I use for a geofence?", "acceptedAnswer": {"@type": "Answer", "text": "Apple recommends a radius of at least 200 meters, as smaller radii may not trigger transitions reliably. See the Apple documentation for details. On Android, a radius of at least 100 meters is recommended. There is no fixed maximum radius on Android, while iOS clamps the radius to maximumRegionMonitoringDistance."}}, {"@type": "Question", "name": "Why does addGeofences(...) reject with a permission error?", "acceptedAnswer": {"@type": "Answer", "text": "Geofencing requires the Always location authorization on iOS and the background location permission on Android. If only the \"while in use\" (foreground) permission is granted, addGeofences(...) rejects with the PERMISSION_DENIED error code. See Check and request permissions for the required two-step request flow."}}, {"@type": "Question", "name": "What happens to transitions that occur while my app is in the background or terminated?", "acceptedAnswer": {"@type": "Answer", "text": "Transitions detected while the app is in the background or terminated are queued and replayed in order once the first geofenceTransition listener is registered, so register the listener as early as possible after your app starts. The replay buffer holds at most 100 transitions. If more transitions are detected before your app registers a listener again, the oldest ones are dropped. If a geofence defines a notification, it is displayed natively regardless of the app state. If your server needs to know about transitions right away instead of on the next launch, configure HTTP Sync, which uploads them natively at the moment they are detected. On Android, geofences are automatically re-registered after a device reboot or an app update, while on iOS the monitored regions are persisted by the operating system."}}, {"@type": "Question", "name": "Why don't I receive dwell transitions on iOS?", "acceptedAnswer": {"@type": "Answer", "text": "Dwell transitions are only supported on Android. On iOS, the androidNotifyOnDwell and androidLoiteringDelay options are ignored, and only enter and exit transitions are reported."}}, {"@type": "Question", "name": "Why are latitude and longitude null on iOS?", "acceptedAnswer": {"@type": "Answer", "text": "Core Location does not provide the triggering location for a region transition. If you need the geofence's coordinates, look them up by its id (you already know them because you added the geofence)."}}, {"@type": "Question", "name": "Can I use this plugin with Ionic, React, Vue or Angular?", "acceptedAnswer": {"@type": "Answer", "text": "Yes, the plugin is framework-agnostic. It works in any Capacitor app regardless of the web framework, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects."}}], "url": "https://capawesome.io/docs/sdks/capacitor/geofences/"}
```
