---
description: Capacitor Background Geolocation plugin to track the device location on Android and iOS while the app is in the background, with native HTTP sync.
title: Capacitor Background Geolocation Plugin - Capawesome
image: https://capawesome.io/docs/assets/images/social/sdks/capacitor/background-geolocation.png
---

<!doctype html> 

[Skip to content ](#capacitor-background-geolocation-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/)
* [ 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)
* [ Background Task ](/docs/sdks/capacitor/background-task/)
* [ Badge ](/docs/sdks/capacitor/badge/)
* [ Barcode Scanner ](/docs/sdks/capacitor/barcode-scanner/)
* [ Barometer ](/docs/sdks/capacitor/barometer/)
* [ Battery ](/docs/sdks/capacitor/battery/)
* [ Biometrics ](/docs/sdks/capacitor/biometrics/)
* [ Bluetooth Low Energy ](/docs/sdks/capacitor/bluetooth-low-energy/)
* [ Calendar ](/docs/sdks/capacitor/calendar/)
* [ Clipboard ](/docs/sdks/capacitor/clipboard/)
* [ Cloudinary ](/docs/sdks/capacitor/cloudinary/)
* [ Compass ](/docs/sdks/capacitor/compass/)
* [ Contacts ](/docs/sdks/capacitor/contacts/)
* [ Crisp ](/docs/sdks/capacitor/crisp/)
* [ Datetime Picker ](/docs/sdks/capacitor/datetime-picker/)
* [ Device Info ](/docs/sdks/capacitor/device-info/)
* [ Dialog ](/docs/sdks/capacitor/dialog/)
* [ Document Scanner ](/docs/sdks/capacitor/document-scanner/)
* [ Electron ](/docs/sdks/capacitor/electron/)
* [ Exif ](/docs/sdks/capacitor/exif/)
* [ Facebook Sign-In ](/docs/sdks/capacitor/facebook-sign-in/)
* [ File Compressor ](/docs/sdks/capacitor/file-compressor/)
* [ File Manager ](/docs/sdks/capacitor/file-manager/)
* [ File Opener ](/docs/sdks/capacitor/file-opener/)
* [ File Picker ](/docs/sdks/capacitor/file-picker/)
* [ File Transfer ](/docs/sdks/capacitor/file-transfer/)
* [ Firebase ](/docs/sdks/capacitor/firebase/)
* [ Formbricks ](/docs/sdks/capacitor/formbricks/)
* [ Geocoder ](/docs/sdks/capacitor/geocoder/)
* [ Geofences ](/docs/sdks/capacitor/geofences/)
* [ 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 Background Geolocation Plugin[¶](#capacitor-background-geolocation-plugin "Permanent link")

Capacitor plugin for reliable background geolocation tracking on Android and iOS. Provides a first-class permissions API, a correctly plumbed Android foreground service and fine-grained tuning options.

[ ![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 Background Geolocation plugin provides everything you need for reliable location tracking, even while the app is in the background. Here are some of the key features:

* 🛰️ **Background Tracking**: Keeps receiving position updates while the app is in the background.
* 🔐 **Granular Permissions**: First-class permissions API including the two-step background location upgrade flow.
* 📍 **One-Shot Position**: Get the current position with configurable accuracy, timeout and maximum age.
* ☁️ **HTTP Sync**: Upload positions to your own server in batches with an on-device SQLite queue, automatic retries and at-least-once delivery.
* 🧪 **Sync Testing**: Test the sync engine without your own server using the free [Background Geolocation Playground](https://background-geolocation-playground.capawesome.io) webpage.
* 🛠️ **Real Tuning Knobs**: Configure accuracy, distance filter and update interval.
* 🤖 **Foreground Service**: Correct Android 14+ foreground service with a fully configurable notification.
* 📡 **Provider Choice**: Fused location provider by default with an escape hatch to the platform location manager for devices without Google Play services.
* 🎯 **Full Accuracy Requests**: Request temporary full accuracy from users with reduced location accuracy on iOS.
* 🕵️ **Mock Location Detection**: Every position reports whether it was delivered by a mock location provider on Android.
* 🔋 **Battery Friendly**: Optional automatic pausing of position updates on iOS when the device is stationary.
* 🔒 **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 [Geofences](https://capawesome.io/docs/sdks/capacitor/geofences/) and [Settings Launcher](https://capawesome.io/docs/sdks/capacitor/settings-launcher/) plugins.
* 📦 **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 Background Geolocation plugin is typically used whenever an app needs to know where the device is over a period of time, for example:

* **Fitness and activity tracking**: Record runs, rides and hikes even when the screen is off.
* **Fleet and workforce management**: Track delivery drivers or field workers during their shift.
* **Navigation**: Keep the position up to date while the user switches to another app.
* **Safety**: Share the live location with family members or emergency contacts.

## 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-background-geolocation` 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-background-geolocation
[](#%5F%5Fcodelineno-3-2)npx cap sync
`

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

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

The plugin already declares the location and foreground service permissions in its own manifest. If you want to receive position updates while the app is in the **background**, you must additionally declare the `ACCESS_BACKGROUND_LOCATION` permission in the `AndroidManifest.xml` file of your app before or after the `application` tag:

`[](#%5F%5Fcodelineno-4-1)<!-- Required if you want to receive position updates while the app is in the background. -->
[](#%5F%5Fcodelineno-4-2)<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
`

**Attention**: This permission is deliberately **not** declared by the plugin because Google Play requires a policy declaration and review for every app that requests background location access. Only declare it if your app really needs background tracking and make sure to complete the [location permissions declaration](https://support.google.com/googleplay/android-developer/answer/9799150) in the Google Play Console.

The `INTERNET` permission that the [HTTP Sync](#http-sync) feature requires is already declared by every Capacitor app template, so no additional configuration is needed for it.

#### 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 variable in your app’s `variables.gradle` file to change the default version of the dependency:

* `$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")

Add the `NSLocationWhenInUseUsageDescription` and `NSLocationAlwaysAndWhenInUseUsageDescription` keys to the `Info.plist` file of your app to explain 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 track your position.</string>
[](#%5F%5Fcodelineno-6-3)<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
[](#%5F%5Fcodelineno-6-4)<string>The app needs access to your location to track your position, even while the app is in the background.</string>
`

If you want to receive position updates while the app is in the **background**, you must also enable the `location` background mode in the `Info.plist` file of your app:

`[](#%5F%5Fcodelineno-7-1)<key>UIBackgroundModes</key>
[](#%5F%5Fcodelineno-7-2)<array>
[](#%5F%5Fcodelineno-7-3)  <string>location</string>
[](#%5F%5Fcodelineno-7-4)</array>
`

If you want to use the `requestTemporaryFullAccuracy(...)` method, you must also add the `NSLocationTemporaryUsageDescriptionDictionary` key to the `Info.plist` file of your app with one entry per purpose key:

`[](#%5F%5Fcodelineno-8-1)<key>NSLocationTemporaryUsageDescriptionDictionary</key>
[](#%5F%5Fcodelineno-8-2)<dict>
[](#%5F%5Fcodelineno-8-3)  <key>navigation</key>
[](#%5F%5Fcodelineno-8-4)  <string>The app needs your precise location to provide turn-by-turn navigation.</string>
[](#%5F%5Fcodelineno-8-5)</dict>
`

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

No configuration required for this plugin.

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

The following examples show how to get the current position, watch the position of the device, request temporary full accuracy, and check and request permissions.

### Get the current position[¶](#get-the-current-position "Permanent link")

Get a one-shot position of the device with a configurable accuracy, timeout and maximum age. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-9-1)import { Accuracy, BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-9-2)
[](#%5F%5Fcodelineno-9-3)const getCurrentPosition = async () => {
[](#%5F%5Fcodelineno-9-4)  const { position } = await BackgroundGeolocation.getCurrentPosition({
[](#%5F%5Fcodelineno-9-5)    accuracy: Accuracy.High,
[](#%5F%5Fcodelineno-9-6)    timeout: 10000,
[](#%5F%5Fcodelineno-9-7)  });
[](#%5F%5Fcodelineno-9-8)  return position;
[](#%5F%5Fcodelineno-9-9)};
`

### Watch the position[¶](#watch-the-position "Permanent link")

Start a watch session to receive position updates via the `positionChange` event, even while the app is in the background. Errors that occur during an active watch session are delivered via the `positionError` event. Only one watch session can be active at a time. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-10-1)import { Accuracy, ActivityType, BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-10-2)
[](#%5F%5Fcodelineno-10-3)const startWatching = async () => {
[](#%5F%5Fcodelineno-10-4)  await BackgroundGeolocation.addListener('positionChange', event => {
[](#%5F%5Fcodelineno-10-5)    console.log('New position: ', event.position);
[](#%5F%5Fcodelineno-10-6)  });
[](#%5F%5Fcodelineno-10-7)  await BackgroundGeolocation.addListener('positionError', event => {
[](#%5F%5Fcodelineno-10-8)    console.error('Position error: ', event.code, event.message);
[](#%5F%5Fcodelineno-10-9)  });
[](#%5F%5Fcodelineno-10-10)  await BackgroundGeolocation.startWatching({
[](#%5F%5Fcodelineno-10-11)    accuracy: Accuracy.High,
[](#%5F%5Fcodelineno-10-12)    distanceFilter: 10,
[](#%5F%5Fcodelineno-10-13)    androidInterval: 5000,
[](#%5F%5Fcodelineno-10-14)    androidNotification: {
[](#%5F%5Fcodelineno-10-15)      title: 'Location Tracking',
[](#%5F%5Fcodelineno-10-16)      text: 'Your location is being tracked.',
[](#%5F%5Fcodelineno-10-17)    },
[](#%5F%5Fcodelineno-10-18)    iosActivityType: ActivityType.Fitness,
[](#%5F%5Fcodelineno-10-19)  });
[](#%5F%5Fcodelineno-10-20)};
[](#%5F%5Fcodelineno-10-21)
[](#%5F%5Fcodelineno-10-22)const stopWatching = async () => {
[](#%5F%5Fcodelineno-10-23)  await BackgroundGeolocation.stopWatching();
[](#%5F%5Fcodelineno-10-24)};
[](#%5F%5Fcodelineno-10-25)
[](#%5F%5Fcodelineno-10-26)const isWatching = async () => {
[](#%5F%5Fcodelineno-10-27)  const { watching } = await BackgroundGeolocation.isWatching();
[](#%5F%5Fcodelineno-10-28)  return watching;
[](#%5F%5Fcodelineno-10-29)};
`

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

The plugin can upload every position of a watch session to your own server without involving JavaScript, buffered in a local queue so that no position is lost while the device is offline. Provide the `sync` option when you start the watch session to enable it. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-11-1)import { BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-11-2)
[](#%5F%5Fcodelineno-11-3)const startWatchingWithSync = async () => {
[](#%5F%5Fcodelineno-11-4)  await BackgroundGeolocation.startWatching({
[](#%5F%5Fcodelineno-11-5)    androidNotification: {
[](#%5F%5Fcodelineno-11-6)      title: 'Location Tracking',
[](#%5F%5Fcodelineno-11-7)      text: 'Your location is being tracked.',
[](#%5F%5Fcodelineno-11-8)    },
[](#%5F%5Fcodelineno-11-9)    sync: {
[](#%5F%5Fcodelineno-11-10)      url: 'https://api.example.com/positions',
[](#%5F%5Fcodelineno-11-11)      headers: {
[](#%5F%5Fcodelineno-11-12)        Authorization: 'Bearer eyJhbGciOi...',
[](#%5F%5Fcodelineno-11-13)      },
[](#%5F%5Fcodelineno-11-14)    },
[](#%5F%5Fcodelineno-11-15)  });
[](#%5F%5Fcodelineno-11-16)};
`

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

### Request temporary full accuracy[¶](#request-temporary-full-accuracy "Permanent link")

Ask users who have granted reduced location accuracy for full accuracy for the duration of the app session. The `NSLocationTemporaryUsageDescriptionDictionary` key must contain an entry for the given purpose key. Only available on iOS:

`[](#%5F%5Fcodelineno-12-1)import { BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-12-2)
[](#%5F%5Fcodelineno-12-3)const requestTemporaryFullAccuracy = async () => {
[](#%5F%5Fcodelineno-12-4)  await BackgroundGeolocation.requestTemporaryFullAccuracy({
[](#%5F%5Fcodelineno-12-5)    purposeKey: 'navigation',
[](#%5F%5Fcodelineno-12-6)  });
[](#%5F%5Fcodelineno-12-7)};
`

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

Check and request the permissions to access the location services. The background location permission must be requested in **two steps** on both platforms. Only available on Android and iOS:

1. Request the `location` (and optionally `notifications`) permission first. This displays the default system dialog.
2. Request the `backgroundLocation` permission in a **separate** call, ideally after explaining to the user why the app needs it:
3. On **Android 11+**, the user is taken to the location settings of the app where the `Allow all the time` option must be selected. Google recommends showing an in-app explanation before triggering this step.
4. On **iOS**, the operating system presents the upgrade prompt that asks the user to change the permission from `While Using the App` to `Always`.

`[](#%5F%5Fcodelineno-13-1)import { BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-13-2)
[](#%5F%5Fcodelineno-13-3)const checkPermissions = async () => {
[](#%5F%5Fcodelineno-13-4)  return BackgroundGeolocation.checkPermissions();
[](#%5F%5Fcodelineno-13-5)};
[](#%5F%5Fcodelineno-13-6)
[](#%5F%5Fcodelineno-13-7)const requestPermissions = async () => {
[](#%5F%5Fcodelineno-13-8)  return BackgroundGeolocation.requestPermissions({
[](#%5F%5Fcodelineno-13-9)    permissions: ['location', 'notifications'],
[](#%5F%5Fcodelineno-13-10)  });
[](#%5F%5Fcodelineno-13-11)};
[](#%5F%5Fcodelineno-13-12)
[](#%5F%5Fcodelineno-13-13)const requestBackgroundLocationPermission = async () => {
[](#%5F%5Fcodelineno-13-14)  return BackgroundGeolocation.requestPermissions({
[](#%5F%5Fcodelineno-13-15)    permissions: ['backgroundLocation'],
[](#%5F%5Fcodelineno-13-16)  });
[](#%5F%5Fcodelineno-13-17)};
`

If a permission was permanently denied, you can take the user to the app settings to change it:

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

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

* [checkPermissions()](#checkpermissions)
* [clearSyncQueue()](#clearsyncqueue)
* [getCurrentPosition(...)](#getcurrentposition)
* [getSyncStatus()](#getsyncstatus)
* [isWatching()](#iswatching)
* [openSettings()](#opensettings)
* [requestPermissions(...)](#requestpermissions)
* [requestTemporaryFullAccuracy(...)](#requesttemporaryfullaccuracy)
* [startWatching(...)](#startwatching)
* [stopWatching()](#stopwatching)
* [triggerSync()](#triggersync)
* [addListener('positionChange', ...)](#addlistenerpositionchange-)
* [addListener('positionError', ...)](#addlistenerpositionerror-)
* [addListener('syncFailed', ...)](#addlistenersyncfailed-)
* [removeAllListeners()](#removealllisteners)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)
* [Enums](#enums)

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

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

Check the current permission status.

Only available on Android and iOS.

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

**Since:** 0.0.1

---

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

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

Delete all buffered positions from the sync queue.

This method can be called with or without an active watch session, for example to discard pending positions when the user signs out.

Only available on Android and iOS.

**Since:** 0.0.1

---

### getCurrentPosition(...)[¶](#getcurrentposition "Permanent link")

`[](#%5F%5Fcodelineno-17-1)getCurrentPosition(options?: GetCurrentPositionOptions | undefined) => Promise<GetCurrentPositionResult>
`

Get the current position of the device.

On **Android**, the location permission is requested automatically if it has not been granted yet.

Only available on Android and iOS.

| Param       | Type                                                    |
| ----------- | ------------------------------------------------------- |
| **options** | [GetCurrentPositionOptions](#getcurrentpositionoptions) |

**Returns:** `Promise<[GetCurrentPositionResult](#getcurrentpositionresult)>`

**Since:** 0.0.1

---

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

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

Get the current status of the sync queue.

This method can be called with or without an active watch session.

Only available on Android and iOS.

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

**Since:** 0.0.1

---

### isWatching()[¶](#iswatching "Permanent link")

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

Check whether or not a watch session is currently active.

Only available on Android and iOS.

**Returns:** `Promise<[IsWatchingResult](#iswatchingresult)>`

**Since:** 0.0.1

---

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

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

Open 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

---

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

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

Request permissions.

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

Only available on Android and iOS.

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

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

**Since:** 0.0.1

---

### requestTemporaryFullAccuracy(...)[¶](#requesttemporaryfullaccuracy "Permanent link")

`[](#%5F%5Fcodelineno-22-1)requestTemporaryFullAccuracy(options: RequestTemporaryFullAccuracyOptions) => Promise<void>
`

Request temporary access to the full accuracy location of the device.

Call this method if the user has granted the app reduced location accuracy to ask for full accuracy for the duration of the app session.

The `NSLocationTemporaryUsageDescriptionDictionary` key must be defined in the `Info.plist` file of your app with an entry for the given `purposeKey`.

Only available on iOS.

| Param       | Type                                                                        |
| ----------- | --------------------------------------------------------------------------- |
| **options** | [RequestTemporaryFullAccuracyOptions](#requesttemporaryfullaccuracyoptions) |

**Since:** 0.0.1

---

### startWatching(...)[¶](#startwatching "Permanent link")

`[](#%5F%5Fcodelineno-23-1)startWatching(options?: StartWatchingOptions | undefined) => Promise<void>
`

Start watching the position of the device.

Position updates are delivered via the `positionChange` event. Errors that occur after the watch session has been started (e.g. the user disables the location services) are delivered via the `positionError`event.

Only one watch session can be active at a time. The promise rejects with the `ALREADY_WATCHING` error code if a watch session is already active.

On **Android**, a foreground service with a persistent notification is started so that the app keeps receiving position updates while it is in the background. For this reason, the `androidNotification` option must be provided. The location permission is requested automatically if it has not been granted yet.

On **iOS**, position updates are delivered while the app is in the background if the `location` background mode is enabled in the app.

If the `sync` option is provided, every position of the watch session is additionally buffered in a local queue and uploaded in batches to the configured server while the watch session is active.

Without the `backgroundLocation` permission, the watch session keeps working but position updates may be suspended while the app is in the background.

Only available on Android and iOS.

| Param       | Type                                          |
| ----------- | --------------------------------------------- |
| **options** | [StartWatchingOptions](#startwatchingoptions) |

**Since:** 0.0.1

---

### stopWatching()[¶](#stopwatching "Permanent link")

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

Stop the active watch session.

On **Android**, this also stops the foreground service and removes the associated notification.

Only available on Android and iOS.

**Since:** 0.0.1

---

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

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

Immediately attempt to upload all buffered positions.

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 positions have been delivered.

The promise rejects if no watch session with a `sync` configuration is active.

Only available on Android and iOS.

**Since:** 0.0.1

---

### addListener('positionChange', ...)[¶](#addlistenerpositionchange "Permanent link")

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

Called when a new position is available during an active watch session.

Only available on Android and iOS.

| Param            | Type                                                         |
| ---------------- | ------------------------------------------------------------ |
| **eventName**    | 'positionChange'                                             |
| **listenerFunc** | (event: [PositionChangeEvent](#positionchangeevent)) => void |

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

**Since:** 0.0.1

---

### addListener('positionError', ...)[¶](#addlistenerpositionerror "Permanent link")

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

Called when an error occurs during an active watch session, for example when the user disables the location services or revokes the location permission.

Only available on Android and iOS.

| Param            | Type                                                       |
| ---------------- | ---------------------------------------------------------- |
| **eventName**    | 'positionError'                                            |
| **listenerFunc** | (event: [PositionErrorEvent](#positionerrorevent)) => 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 positions fails.

The affected positions 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")

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

| Prop                   | Type                                | Description                                                                                                                                                                                                                 | Since |
| ---------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **backgroundLocation** | [PermissionState](#permissionstate) | Permission state for using the location services while the app is in the background. On **Android 9 and older**, this always mirrors the location permission state since no separate background location permission exists. | 0.0.1 |
| **location**           | [PermissionState](#permissionstate) | Permission state for using the location services while the app is in use.                                                                                                                                                   | 0.0.1 |
| **notifications**      | [PermissionState](#permissionstate) | Permission state for posting notifications. On **Android 12 and older** and on **iOS**, this is always granted since no notification permission is required.                                                                | 0.0.1 |

#### GetCurrentPositionResult[¶](#getcurrentpositionresult "Permanent link")

| Prop         | Type                  | Description                         | Since |
| ------------ | --------------------- | ----------------------------------- | ----- |
| **position** | [Position](#position) | The current position of the device. | 0.0.1 |

#### Position[¶](#position "Permanent link")

| Prop                 | Type            | Description                                                                                                                                                          | Since |
| -------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **accuracy**         | number          | The estimated horizontal accuracy radius of the position in meters.                                                                                                  | 0.0.1 |
| **altitude**         | number \| null  | The altitude of the position in meters or null if the altitude is not available.                                                                                     | 0.0.1 |
| **altitudeAccuracy** | number \| null  | The estimated accuracy of the altitude in meters or null if the altitude accuracy is not available.                                                                  | 0.0.1 |
| **bearing**          | number \| null  | The direction in which the device is traveling in degrees relative to north or null if the bearing is not available.                                                 | 0.0.1 |
| **latitude**         | number          | The latitude of the position in degrees.                                                                                                                             | 0.0.1 |
| **longitude**        | number          | The longitude of the position in degrees.                                                                                                                            | 0.0.1 |
| **simulated**        | boolean \| null | Whether or not the position was delivered by a mock location provider. On **iOS**, this is always null since the operating system does not provide this information. | 0.0.1 |
| **speed**            | number \| null  | The speed of the device in meters per second or null if the speed is not available.                                                                                  | 0.0.1 |
| **timestamp**        | number          | The time at which the position was determined in milliseconds since the Unix epoch.                                                                                  | 0.0.1 |

#### GetCurrentPositionOptions[¶](#getcurrentpositionoptions "Permanent link")

| Prop           | Type                  | Description                                                                                                                                            | Default       | Since |
| -------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | ----- |
| **accuracy**   | [Accuracy](#accuracy) | The desired accuracy of the position.                                                                                                                  | Accuracy.High | 0.0.1 |
| **maximumAge** | number                | The maximum age in milliseconds of a cached position that is accepted as the current position. If set to 0, a fresh position is always fetched.        | 0             | 0.0.1 |
| **timeout**    | number                | The maximum time in milliseconds to wait for a position. The promise rejects with the TIMEOUT error code if no position is available within this time. | 10000         | 0.0.1 |

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

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

#### IsWatchingResult[¶](#iswatchingresult "Permanent link")

| Prop         | Type    | Description                                         | Since |
| ------------ | ------- | --------------------------------------------------- | ----- |
| **watching** | boolean | Whether or not a watch session is currently active. | 0.0.1 |

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

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

#### RequestTemporaryFullAccuracyOptions[¶](#requesttemporaryfullaccuracyoptions "Permanent link")

| Prop           | Type   | Description                                                                                                                                                                          | Since |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
| **purposeKey** | string | The key of the entry in the NSLocationTemporaryUsageDescriptionDictionary dictionary of the Info.plist file of your app that describes why the app needs the full accuracy location. | 0.0.1 |

#### StartWatchingOptions[¶](#startwatchingoptions "Permanent link")

| Prop                            | Type                                                      | Description                                                                                                                                                                                                                                                                   | Default            | Since |
| ------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
| **accuracy**                    | [Accuracy](#accuracy)                                     | The desired accuracy of the position updates.                                                                                                                                                                                                                                 | Accuracy.High      | 0.0.1 |
| **androidForceLocationManager** | boolean                                                   | Whether or not to force the use of the platform location manager instead of the fused location provider, even if Google Play services is available. Set this option to true on devices without Google Play services (e.g. certain Huawei devices). Only available on Android. | false              | 0.0.1 |
| **androidInterval**             | number                                                    | The interval in milliseconds at which position updates are requested. Only available on Android.                                                                                                                                                                              | 1000               | 0.0.1 |
| **androidNotification**         | [AndroidNotificationOptions](#androidnotificationoptions) | The configuration of the notification that is displayed while the watch session is active. This option must be provided on Android. Only available on Android.                                                                                                                |                    | 0.0.1 |
| **distanceFilter**              | number                                                    | The minimum distance in meters that the device must move before a new position update is delivered.                                                                                                                                                                           | 0                  | 0.0.1 |
| **iosActivityType**             | [ActivityType](#activitytype)                             | The type of activity for which the position updates are used. This helps the operating system to decide when position updates may be paused automatically. Only available on iOS.                                                                                             | ActivityType.Other | 0.0.1 |
| **iosPausesAutomatically**      | boolean                                                   | Whether or not the operating system is allowed to pause position updates automatically when the device is unlikely to move (e.g. when the user is stationary for a longer period of time). This can significantly improve battery life. Only available on iOS.                | false              | 0.0.1 |
| **iosShowBackgroundIndicator**  | boolean                                                   | Whether or not the status bar indicator is displayed when the app uses the location services in the background. Only available on iOS.                                                                                                                                        | true               | 0.0.1 |
| **sync**                        | [SyncOptions](#syncoptions)                               | The configuration for uploading positions to a server. If provided, every position is buffered in a local queue and uploaded in batches to the configured URL while the watch session is active.                                                                              |                    | 0.0.1 |

#### AndroidNotificationOptions[¶](#androidnotificationoptions "Permanent link")

| Prop            | Type   | Description                                                                                                                                                                 | Default                  | Since |
| --------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----- |
| **channelName** | string | The name of the notification channel in which the notification is displayed.                                                                                                | 'Background Geolocation' | 0.0.1 |
| **color**       | string | The color of the notification as a hex color code (e.g. #42A5F5).                                                                                                           |                          | 0.0.1 |
| **icon**        | string | The name of the drawable resource that is displayed as the small icon of the notification (e.g. ic\_stat\_location). If not provided, the launcher icon of the app is used. |                          | 0.0.1 |
| **text**        | string | The body text of the notification.                                                                                                                                          |                          | 0.0.1 |
| **title**       | string | The title of the notification.                                                                                                                                              |                          | 0.0.1 |

#### SyncOptions[¶](#syncoptions "Permanent link")

| Prop              | Type                                | Description                                                                                                                                                                                                                              | Default                                                                                              | Since |       |
| ----------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----- | ----- |
| **batchSize**     | number                              | The maximum number of positions that are uploaded in a single request. Set this option to 1 to upload each position immediately.                                                                                                         | 100                                                                                                  | 0.0.1 |       |
| **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 |
| **flushInterval** | number                              | The maximum time in milliseconds before buffered positions are uploaded, even if the batch size has not been reached yet.                                                                                                                | 60000                                                                                                | 0.0.1 |       |
| **headers**       | { \[key: string\]: string; }        | Static HTTP headers that are sent with every upload request, for example for authorization.                                                                                                                                              |                                                                                                      | 0.0.1 |       |
| **maxAge**        | number                              | The maximum age in milliseconds of a buffered position. Older positions are deleted from the queue without being uploaded. Must be positive. If not provided, positions are kept until they are uploaded or evicted from the full queue. |                                                                                                      | 0.0.1 |       |
| **maxQueueSize**  | number                              | The maximum number of buffered positions. When the queue is full, the oldest positions are dropped first.                                                                                                                                | 10000                                                                                                | 0.0.1 |       |
| **url**           | string                              | The URL the positions are uploaded to via HTTP POST.                                                                                                                                                                                     |                                                                                                      | 0.0.1 |       |

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

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

#### PositionChangeEvent[¶](#positionchangeevent "Permanent link")

| Prop         | Type                  | Description                     | Since |
| ------------ | --------------------- | ------------------------------- | ----- |
| **position** | [Position](#position) | The new position of the device. | 0.0.1 |

#### PositionErrorEvent[¶](#positionerrorevent "Permanent link")

| Prop        | Type                    | Description        | Since |
| ----------- | ----------------------- | ------------------ | ----- |
| **code**    | [ErrorCode](#errorcode) | The error code.    | 0.0.1 |
| **message** | string                  | The error message. | 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")

The permissions that can be requested.

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

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

#### Accuracy[¶](#accuracy "Permanent link")

| Members      | Value      | Description                                                                                                                         | Since |
| ------------ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **Balanced** | 'BALANCED' | A balance between accuracy and power consumption. The position is accurate to within about a hundred meters.                        | 0.0.1 |
| **High**     | 'HIGH'     | The most accurate position that is available. This uses the most power and should only be used when a precise position is required. | 0.0.1 |
| **Low**      | 'LOW'      | A low accuracy position with minimal power consumption. The position is accurate to within about a kilometer.                       | 0.0.1 |

#### ActivityType[¶](#activitytype "Permanent link")

| Members                  | Value                    | Description                                                                                    | Since |
| ------------------------ | ------------------------ | ---------------------------------------------------------------------------------------------- | ----- |
| **Airborne**             | 'AIRBORNE'               | [Position](#position) updates for activities in the air (e.g. flying).                         | 0.0.1 |
| **AutomotiveNavigation** | 'AUTOMOTIVE\_NAVIGATION' | [Position](#position) updates for automotive navigation.                                       | 0.0.1 |
| **Fitness**              | 'FITNESS'                | [Position](#position) updates for fitness activities (e.g. walking, running or cycling).       | 0.0.1 |
| **Other**                | 'OTHER'                  | [Position](#position) updates for activities that are not covered by the other activity types. | 0.0.1 |
| **OtherNavigation**      | 'OTHER\_NAVIGATION'      | [Position](#position) updates for vehicular navigation that is not automotive (e.g. boating).  | 0.0.1 |

#### ErrorCode[¶](#errorcode "Permanent link")

| Members                      | Value                          | Description                                         | Since |
| ---------------------------- | ------------------------------ | --------------------------------------------------- | ----- |
| **AlreadyWatching**          | 'ALREADY\_WATCHING'            | A watch session is already active.                  | 0.0.1 |
| **LocationServicesDisabled** | 'LOCATION\_SERVICES\_DISABLED' | The location services are disabled on the device.   | 0.0.1 |
| **PermissionDenied**         | 'PERMISSION\_DENIED'           | The location permission has not been granted.       | 0.0.1 |
| **PositionUnavailable**      | 'POSITION\_UNAVAILABLE'        | The position is currently not available.            | 0.0.1 |
| **Timeout**                  | 'TIMEOUT'                      | No position was available within the given timeout. | 0.0.1 |

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

The plugin can upload every position of a watch session to your own server without involving JavaScript. Positions are buffered in a local SQLite queue, uploaded in batches 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.

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

Provide the `sync` option when you start the watch session to enable the upload pipeline for that session. Failed upload attempts are reported via the `syncFailed` event:

`[](#%5F%5Fcodelineno-30-1)import { Accuracy, BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-30-2)
[](#%5F%5Fcodelineno-30-3)const startWatchingWithSync = async () => {
[](#%5F%5Fcodelineno-30-4)  await BackgroundGeolocation.addListener('syncFailed', event => {
[](#%5F%5Fcodelineno-30-5)    console.error('Upload failed: ', event.statusCode, event.message);
[](#%5F%5Fcodelineno-30-6)  });
[](#%5F%5Fcodelineno-30-7)  await BackgroundGeolocation.startWatching({
[](#%5F%5Fcodelineno-30-8)    accuracy: Accuracy.High,
[](#%5F%5Fcodelineno-30-9)    distanceFilter: 10,
[](#%5F%5Fcodelineno-30-10)    androidNotification: {
[](#%5F%5Fcodelineno-30-11)      title: 'Location Tracking',
[](#%5F%5Fcodelineno-30-12)      text: 'Your location is being tracked.',
[](#%5F%5Fcodelineno-30-13)    },
[](#%5F%5Fcodelineno-30-14)    sync: {
[](#%5F%5Fcodelineno-30-15)      url: 'https://api.example.com/positions',
[](#%5F%5Fcodelineno-30-16)      batchSize: 100,
[](#%5F%5Fcodelineno-30-17)      flushInterval: 60000,
[](#%5F%5Fcodelineno-30-18)      maxAge: 86400000,
[](#%5F%5Fcodelineno-30-19)      maxQueueSize: 10000,
[](#%5F%5Fcodelineno-30-20)      headers: {
[](#%5F%5Fcodelineno-30-21)        Authorization: 'Bearer eyJhbGciOi...',
[](#%5F%5Fcodelineno-30-22)      },
[](#%5F%5Fcodelineno-30-23)      extras: {
[](#%5F%5Fcodelineno-30-24)        userId: 'abc',
[](#%5F%5Fcodelineno-30-25)      },
[](#%5F%5Fcodelineno-30-26)    },
[](#%5F%5Fcodelineno-30-27)  });
[](#%5F%5Fcodelineno-30-28)};
`

The queue itself can be inspected and controlled with or without an active watch session:

`` [](#%5F%5Fcodelineno-31-1)import { BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';
[](#%5F%5Fcodelineno-31-2)
[](#%5F%5Fcodelineno-31-3)const getSyncStatus = async () => {
[](#%5F%5Fcodelineno-31-4)  const { pendingCount, droppedCount, lastSyncedAt } = await BackgroundGeolocation.getSyncStatus();
[](#%5F%5Fcodelineno-31-5)  console.log(`${pendingCount} positions 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 BackgroundGeolocation.triggerSync();
[](#%5F%5Fcodelineno-31-10)};
[](#%5F%5Fcodelineno-31-11)
[](#%5F%5Fcodelineno-31-12)const clearSyncQueue = async () => {
[](#%5F%5Fcodelineno-31-13)  await BackgroundGeolocation.clearSyncQueue();
[](#%5F%5Fcodelineno-31-14)};
 ``

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

Positions 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)  "positions": [
[](#%5F%5Fcodelineno-32-3)    {
[](#%5F%5Fcodelineno-32-4)      "id": 4711,
[](#%5F%5Fcodelineno-32-5)      "latitude": 52.52,
[](#%5F%5Fcodelineno-32-6)      "longitude": 13.405,
[](#%5F%5Fcodelineno-32-7)      "accuracy": 5,
[](#%5F%5Fcodelineno-32-8)      "altitude": null,
[](#%5F%5Fcodelineno-32-9)      "altitudeAccuracy": null,
[](#%5F%5Fcodelineno-32-10)      "bearing": null,
[](#%5F%5Fcodelineno-32-11)      "speed": null,
[](#%5F%5Fcodelineno-32-12)      "simulated": false,
[](#%5F%5Fcodelineno-32-13)      "timestamp": 1723291200000
[](#%5F%5Fcodelineno-32-14)    }
[](#%5F%5Fcodelineno-32-15)  ],
[](#%5F%5Fcodelineno-32-16)  "extras": { "userId": "abc" }
[](#%5F%5Fcodelineno-32-17)}
`

Every entry of the `positions` array is a [Position](#position) object with an additional `id` property. The `extras` property is omitted entirely if the `extras` option was not provided.

**Idempotency**: The `id` is unique per app installation and strictly increasing. Positions are delivered **at least once**, so the same position may be uploaded more than once, for example if the acknowledgment of the server is lost on the way back. Deduplicate the positions on the server by `id` per device 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 batch:

| Status Code                           | Behavior                                                                                                                                       |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 2xx                                   | The batch is acknowledged and deleted from the queue. The next batch is uploaded immediately if more positions are pending.                    |
| 408, 429, 5xx, network error, timeout | The batch stays at the head of the queue and is retried with an exponential backoff. A syncFailed event is emitted.                            |
| Any other status code                 | The batch is **dropped permanently without being uploaded again** and a syncFailed event is emitted. The upload continues with the next batch. |

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

The request timeout is 30 seconds. Retries start with a delay of 5 seconds and double after every attempt up to a maximum of 10 minutes. The backoff is reset after every successful upload, whenever a new watch session is started and whenever `triggerSync()` is called.

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

* **Persistence**: The queue is stored in a local SQLite database and survives app restarts and force-quits. Positions are only deleted after the server has acknowledged them, they expired or they were evicted.
* **Capacity**: The queue holds at most `maxQueueSize` positions (default: `10000`). If the queue is full, the **oldest** positions are dropped first.
* **Expiry**: If the `maxAge` option is provided, positions older than this age are deleted from the queue without being uploaded. Without the option, positions are kept until they are uploaded or evicted.
* **Observability**: The `droppedCount` property of `getSyncStatus()` counts the positions that were dropped since the queue was last empty or cleared. The counter is kept in memory and is reset when the queue becomes empty, when `clearSyncQueue()` is called or when the app is restarted.
* **Delivery window**: Positions are only uploaded while a watch session **with** a `sync` configuration is active. Starting such a session first deletes expired positions and then immediately uploads everything that is left over from previous sessions.
* **Without a sync configuration**: If you start a watch session without the `sync` option, the queue is left untouched. It is neither uploaded nor cleared.
* **After `stopWatching()`**: The flush timer and any pending retry are cancelled and no further requests are started. A request that is already in flight is allowed to finish, so its positions may still be acknowledged and deleted. Everything else stays in the queue until the next watch session with a `sync` configuration.

### Instant Upload[¶](#instant-upload "Permanent link")

There is no dedicated mode for uploading each position on its own. Set the `batchSize` option to `1` instead to upload every position as soon as it arrives:

`[](#%5F%5Fcodelineno-33-1)sync: {
[](#%5F%5Fcodelineno-33-2)  url: 'https://api.example.com/positions',
[](#%5F%5Fcodelineno-33-3)  batchSize: 1,
[](#%5F%5Fcodelineno-33-4)},
`

### Battery Consumption[¶](#battery-consumption "Permanent link")

Every upload wakes up the cellular radio, which is one of the most expensive operations on a mobile device. Uploading one position at a time (see [Instant Upload](#instant-upload)) therefore has a noticeable impact on the battery life. Prefer a larger `batchSize` and a longer `flushInterval` whenever your use case allows it, so that many positions share a single radio wake-up.

### Testing[¶](#testing "Permanent link")

The free [Background Geolocation Playground](https://background-geolocation-playground.capawesome.io) webpage is a ready-to-use sync target, so you can verify the upload pipeline before your own endpoint exists. Generate a session key on the webpage, use it as bearer token and every uploaded position appears in a table and on a map:

`[](#%5F%5Fcodelineno-34-1)sync: {
[](#%5F%5Fcodelineno-34-2)  url: 'https://background-geolocation-playground.capawesome.io/v1/positions',
[](#%5F%5Fcodelineno-34-3)  headers: {
[](#%5F%5Fcodelineno-34-4)    Authorization: 'Bearer <YOUR_SESSION_KEY>',
[](#%5F%5Fcodelineno-34-5)  },
[](#%5F%5Fcodelineno-34-6)},
`

![Background Geolocation Playground webpage showing the synced positions of a session in a table and on a map](../../../assets/external/raw.githubusercontent.com/capawesome-team/capacitor-plugins/main/packages/background-geolocation/assets/background-geolocation-playground.png)

**Attention**: Session keys and recorded positions are deleted after 14 days and everyone who knows the session key can view its positions, so use the webpage for debugging and testing only and never in production.

### 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.
* **Upload after a force-quit**: Buffered positions are not uploaded while the app is terminated. They are delivered with the next watch session that has a `sync` configuration.
* **Encryption at rest**: The queue database is not encrypted. It is stored in the sandboxed app storage of the operating system.

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

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

It is built for reliable background location tracking with a correctly plumbed Android 14+ foreground service, a first-class permissions API that handles the two-step background upgrade flow, and real tuning knobs for accuracy, distance filtering and update interval. It also reports mock locations on Android, supports temporary full-accuracy requests on iOS, and falls back to the platform location manager on devices without Google Play services — all through one fully typed, actively maintained API with dedicated support. If you only need a single foreground position, a simpler geolocation setup may suffice; if you need dependable tracking that keeps running in the background, this plugin is made for it.

### Does the tracking continue when the user force-quits the app?[¶](#does-the-tracking-continue-when-the-user-force-quits-the-app "Permanent link")

No. On both platforms, the watch session ends when the user force-quits (terminates) the app. This is an operating system restriction. Only operating-system-managed APIs like geofencing can relaunch a terminated app. Take a look at the [Geofences](https://capawesome.io/docs/sdks/capacitor/geofences/) plugin if you need this capability.

### Why is the `androidNotification` option required?[¶](#why-is-the-androidnotification-option-required "Permanent link")

On Android, receiving position updates while the app is in the background requires a foreground service, and every foreground service must display a persistent notification. The plugin builds this notification from the `androidNotification` option so that you have full control over its content.

### Does the plugin work on devices without Google Play services?[¶](#does-the-plugin-work-on-devices-without-google-play-services "Permanent link")

Yes. The plugin automatically falls back to the platform location manager if Google Play services is not available on the device (e.g. on certain Huawei devices). You can also force this behavior with the `androidForceLocationManager` option.

### Why does the plugin not declare the `ACCESS_BACKGROUND_LOCATION` permission?[¶](#why-does-the-plugin-not-declare-the-access%5Fbackground%5Flocation-permission "Permanent link")

Google Play requires a policy declaration and review for every app that requests background location access. If the plugin declared this permission, every app using the plugin would be subject to this review, even if it only needs foreground location access. Therefore, the permission must be declared at the app level (see [Installation](#installation)).

### Can I start multiple watch sessions at the same time?[¶](#can-i-start-multiple-watch-sessions-at-the-same-time "Permanent link")

No. Only one watch session can be active at a time. The native location engine delivers one stream of position updates, so multiple concurrent watchers would only be an illusion with a shared configuration. Call `stopWatching()` before starting a new watch session with different options.

### How can I reduce the battery consumption?[¶](#how-can-i-reduce-the-battery-consumption "Permanent link")

The plugin deliberately does not include a motion-detection state machine that turns the GPS on and off based on accelerometer data. Instead, it exposes real tuning knobs: use a lower `accuracy` (e.g. `Accuracy.Balanced` instead of `Accuracy.High`), use a `distanceFilter` to reduce the number of position updates and increase the `androidInterval` option on Android.

On iOS, you can additionally enable the `iosPausesAutomatically` option so that the operating system pauses the position updates when the device is unlikely to move. Be aware of the trade-off: the operating system decides on its own when to pause and only resumes the position updates once the device has moved significantly again, so a watch session can stay silent for a long time. The plugin logs both the pause and the resume, but keeps the watch session active, which means that `isWatching()` still returns `true` while the position updates are paused.

### What happens if the user grants only approximate location?[¶](#what-happens-if-the-user-grants-only-approximate-location "Permanent link")

On Android, the user can grant approximate instead of precise location. This is not reported separately but is reflected in the `accuracy` property of each position. On iOS, the user can grant reduced accuracy, which you can upgrade for the duration of the app session using `requestTemporaryFullAccuracy(...)`.

### Why are queued positions not uploaded after force-quit?[¶](#why-are-queued-positions-not-uploaded-after-force-quit "Permanent link")

Uploading requires a running app process. When the user force-quits (terminates) the app, the watch session ends and no more requests can be sent. The buffered positions are **not** lost though: they stay in the local queue and are uploaded as soon as the next watch session with a `sync` configuration is started. See [Queue Behavior](#queue-behavior) for details.

### How do I secure the sync endpoint?[¶](#how-do-i-secure-the-sync-endpoint "Permanent link")

Use the `headers` option of the `sync` configuration to send a static credential (e.g. `Authorization: Bearer ...`) with every upload request and always use an `https://` URL, since the credential is otherwise sent in plain text. On iOS, App Transport Security blocks plain `http://` requests by default anyway. Since the headers are static for the duration of the watch session, use a long-lived token and restart the watch session whenever the token is rotated.

### How can I test the sync endpoint locally?[¶](#how-can-i-test-the-sync-endpoint-locally "Permanent link")

Point the `url` option to a local HTTP endpoint that logs the request body. A few lines of Node.js are enough:

`[](#%5F%5Fcodelineno-35-1)// server.js
[](#%5F%5Fcodelineno-35-2)import { createServer } from 'node:http';
[](#%5F%5Fcodelineno-35-3)
[](#%5F%5Fcodelineno-35-4)createServer((request, response) => {
[](#%5F%5Fcodelineno-35-5)  let body = '';
[](#%5F%5Fcodelineno-35-6)  request.on('data', chunk => (body += chunk));
[](#%5F%5Fcodelineno-35-7)  request.on('end', () => {
[](#%5F%5Fcodelineno-35-8)    console.log(JSON.parse(body));
[](#%5F%5Fcodelineno-35-9)    response.writeHead(200).end();
[](#%5F%5Fcodelineno-35-10)  });
[](#%5F%5Fcodelineno-35-11)}).listen(3000);
`

Start it with `node server.js` and use the IP address of your development machine in the `url` option (e.g. `http://192.168.1.10:3000`). On the Android emulator, use `http://10.0.2.2:3000` instead. Plain `http://` requires an App Transport Security exception on iOS and a network security configuration that allows cleartext traffic on Android, so it is often easier to expose the local server via an HTTPS tunnel. Answer with a `503` status code to observe the retry behavior and with a `400` status code to observe a permanently dropped batch.

### 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")

* [Geocoder](https://capawesome.io/docs/sdks/capacitor/geocoder/): Convert the tracked positions into human-readable addresses.
* [Geofences](https://capawesome.io/docs/sdks/capacitor/geofences/): Monitor circular regions with operating-system-managed geofencing, including delivery when the app is terminated.
* [Settings Launcher](https://capawesome.io/docs/sdks/capacitor/settings-launcher/): Take the user to the app settings, for example to grant the background location permission.

## 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/background-geolocation/CHANGELOG.md).

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

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

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

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

August 15, 2026 

Back to top

```json
{"@context": "https://schema.org", "@graph": [{"@type": "TechArticle", "@id": "https://capawesome.io/docs/sdks/capacitor/background-geolocation/#article", "headline": "Capacitor Background Geolocation Plugin", "name": "Capacitor Background Geolocation Plugin", "description": "Capacitor Background Geolocation plugin to track the device location on Android and iOS while the app is in the background, with native HTTP sync.", "inLanguage": "en", "url": "https://capawesome.io/docs/sdks/capacitor/background-geolocation/", "mainEntityOfPage": "https://capawesome.io/docs/sdks/capacitor/background-geolocation/", "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/background-geolocation/#software"}}, {"@type": "SoftwareSourceCode", "@id": "https://capawesome.io/docs/sdks/capacitor/background-geolocation/#software", "name": "Capacitor Background Geolocation Plugin", "description": "Capacitor Background Geolocation plugin to track the device location on Android and iOS while the app is in the background, with native HTTP sync.", "url": "https://capawesome.io/docs/sdks/capacitor/background-geolocation/", "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": "How is this plugin different from other similar plugins?", "acceptedAnswer": {"@type": "Answer", "text": "It is built for reliable background location tracking with a correctly plumbed Android 14+ foreground service, a first-class permissions API that handles the two-step background upgrade flow, and real tuning knobs for accuracy, distance filtering and update interval. It also reports mock locations on Android, supports temporary full-accuracy requests on iOS, and falls back to the platform location manager on devices without Google Play services — all through one fully typed, actively maintained API with dedicated support. If you only need a single foreground position, a simpler geolocation setup may suffice; if you need dependable tracking that keeps running in the background, this plugin is made for it."}}, {"@type": "Question", "name": "Does the tracking continue when the user force-quits the app?", "acceptedAnswer": {"@type": "Answer", "text": "No. On both platforms, the watch session ends when the user force-quits (terminates) the app. This is an operating system restriction. Only operating-system-managed APIs like geofencing can relaunch a terminated app. Take a look at the Geofences plugin if you need this capability."}}, {"@type": "Question", "name": "Why is the androidNotification option required?", "acceptedAnswer": {"@type": "Answer", "text": "On Android, receiving position updates while the app is in the background requires a foreground service, and every foreground service must display a persistent notification. The plugin builds this notification from the androidNotification option so that you have full control over its content."}}, {"@type": "Question", "name": "Does the plugin work on devices without Google Play services?", "acceptedAnswer": {"@type": "Answer", "text": "Yes. The plugin automatically falls back to the platform location manager if Google Play services is not available on the device (e.g. on certain Huawei devices). You can also force this behavior with the androidForceLocationManager option."}}, {"@type": "Question", "name": "Why does the plugin not declare the ACCESS_BACKGROUND_LOCATION permission?", "acceptedAnswer": {"@type": "Answer", "text": "Google Play requires a policy declaration and review for every app that requests background location access. If the plugin declared this permission, every app using the plugin would be subject to this review, even if it only needs foreground location access. Therefore, the permission must be declared at the app level (see Installation)."}}, {"@type": "Question", "name": "Can I start multiple watch sessions at the same time?", "acceptedAnswer": {"@type": "Answer", "text": "No. Only one watch session can be active at a time. The native location engine delivers one stream of position updates, so multiple concurrent watchers would only be an illusion with a shared configuration. Call stopWatching() before starting a new watch session with different options."}}, {"@type": "Question", "name": "How can I reduce the battery consumption?", "acceptedAnswer": {"@type": "Answer", "text": "The plugin deliberately does not include a motion-detection state machine that turns the GPS on and off based on accelerometer data. Instead, it exposes real tuning knobs: use a lower accuracy (e.g. Accuracy.Balanced instead of Accuracy.High), use a distanceFilter to reduce the number of position updates and increase the androidInterval option on Android. On iOS, you can additionally enable the iosPausesAutomatically option so that the operating system pauses the position updates when the device is unlikely to move. Be aware of the trade-off: the operating system decides on its own when to pause and only resumes the position updates once the device has moved significantly again, so a watch session can stay silent for a long time. The plugin logs both the pause and the resume, but keeps the watch session active, which means that isWatching() still returns true while the position updates are paused."}}, {"@type": "Question", "name": "What happens if the user grants only approximate location?", "acceptedAnswer": {"@type": "Answer", "text": "On Android, the user can grant approximate instead of precise location. This is not reported separately but is reflected in the accuracy property of each position. On iOS, the user can grant reduced accuracy, which you can upgrade for the duration of the app session using requestTemporaryFullAccuracy(...)."}}, {"@type": "Question", "name": "Why are queued positions not uploaded after force-quit?", "acceptedAnswer": {"@type": "Answer", "text": "Uploading requires a running app process. When the user force-quits (terminates) the app, the watch session ends and no more requests can be sent. The buffered positions are not lost though: they stay in the local queue and are uploaded as soon as the next watch session with a sync configuration is started. See Queue Behavior for details."}}, {"@type": "Question", "name": "How do I secure the sync endpoint?", "acceptedAnswer": {"@type": "Answer", "text": "Use the headers option of the sync configuration to send a static credential (e.g. Authorization: Bearer...) with every upload request and always use an https:// URL, since the credential is otherwise sent in plain text. On iOS, App Transport Security blocks plain http:// requests by default anyway. Since the headers are static for the duration of the watch session, use a long-lived token and restart the watch session whenever the token is rotated."}}, {"@type": "Question", "name": "How can I test the sync endpoint locally?", "acceptedAnswer": {"@type": "Answer", "text": "Point the url option to a local HTTP endpoint that logs the request body. A few lines of Node.js are enough: // server.js import { createServer } from 'node:http'; createServer (( request, response) => { let body = ''; request. on ( 'data', chunk => ( body += chunk)); request. on ( 'end', () => { console. log ( JSON. parse ( body)); response. writeHead ( 200). end (); }); }). listen ( 3000); Start it with node server.js and use the IP address of your development machine in the url option (e.g. http://192.168.1.10:3000). On the Android emulator, use http://10.0.2.2:3000 instead. Plain http:// requires an App Transport Security exception on iOS and a network security configuration that allows cleartext traffic on Android, so it is often easier to expose the local server via an HTTPS tunnel. Answer with a 503 status code to observe the retry behavior and with a 400 status code to observe a permanently dropped batch."}}, {"@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/background-geolocation/"}
```
