Capacitor SDK Usage & Integration
All 2.2.2 calls use the DoorstepAIDropoffSDK export from @doorstepai/dropoff-sdk-capacitor. The plugin is native-only, so call it after Capacitor boots and guard code that may run during web rendering.
In-SDK Route Geofencing is the recommended way to integrate: hand the SDK your stops and it starts and stops tracking automatically as the driver enters and exits each one. Prefer to drive geofence enter/exit yourself? See Manual Geofencing below.
In-SDK Route Geofencing
Hand the SDK your stops and it auto-starts and stops a tracking session as the driver enters and exits each one.
This flow has four touchpoints in your driver app (Initialize is shared with the manual flow below):
| # | When this happens in the driver app | What you call |
|---|---|---|
| 1 | Driver app launches | Initialize |
| 2 | Start geofencing and tracking all stops | Start Route Geofencing |
| 3 | Driver confirms drop-off or takes proof of delivery | Mark Drop-off |
| 4 | Stop geofencing and tracking all stops | Stop Route Geofencing |
If the driver's route changes mid-shift, call Update Stops as well.
Request foreground buckets first, then request background/Always location separately. The exact signatures, buckets, states, and staged flow are in Capacitor Permissions.
1. Initialize
Call init and then setApiKey once during app startup. init configures Android's foreground-service notification and is a no-op on iOS.
import { Capacitor } from '@capacitor/core';
import { DoorstepAIDropoffSDK } from '@doorstepai/dropoff-sdk-capacitor';
export async function initializeDoorstepAI(): Promise<void> {
if (!Capacitor.isNativePlatform()) return;
await DoorstepAIDropoffSDK.init({
notificationTitle: 'Tracking...',
notificationText: 'Tracking your delivery',
});
await DoorstepAIDropoffSDK.setApiKey({ key: 'your-api-key' });
// Restores a persisted route, or does nothing when no route is active.
await DoorstepAIDropoffSDK.resumeRouteGeofencingIfNeeded();
}
Store the API key in secure application configuration. The field passed to setApiKey is named key.
2. Start Route Geofencing
Call startRouteGeofencing once you have the driver's stops for the shift. Pass every stop up front, not one at a time. ENTER starts a delivery; EXIT stops it only after markDropoff, otherwise timeoutSeconds is the backstop.
import type { DeliveryStop } from '@doorstepai/dropoff-sdk-capacitor';
const stops: DeliveryStop[] = [
{
deliveryId: 'delivery_12345',
address: '123 Main St, San Francisco, CA',
latitude: 37.7749,
longitude: -122.4194,
customerId: 'customer_42',
driverId: 'driver_7',
},
];
await DoorstepAIDropoffSDK.startRouteGeofencing({
stops,
options: {
defaultRadiusMeters: 250,
timeoutSeconds: 1800,
manualForeground: false, // Android only
},
});
const routeListener = await DoorstepAIDropoffSDK.addListener(
'geofenceSessionEvent',
(event) => {
console.log(event.deliveryId, event.type, event.reason);
console.log('Android server id:', event.serverSessionId);
},
);
Each DeliveryStop has deliveryId, address, latitude, longitude, and optional radiusMeters, customerId, and driverId. Each route event has deliveryId, type, and reason; Android STARTED events may also include serverSessionId. Remove the listener when its owner is disposed.
3. Mark Drop-off
DropoffType is a TypeScript string union, not a runtime enum. Pass 'pod' or 'non_pod' directly. The call is identical for both flows: with In-SDK Route Geofencing, marking drop-off also gates the automatic EXIT stop for that delivery.
await DoorstepAIDropoffSDK.markDropoff({
deliveryId: 'delivery_12345',
dropoffType: 'pod',
});
Custom Events
await DoorstepAIDropoffSDK.newEvent({
eventName: 'taking_pod',
deliveryId: 'delivery_12345',
timestamp: 1720000000, // optional epoch seconds
});
4. Stop Route Geofencing
Call stopRouteGeofencing when the shift ends. It clears the route and stops active route sessions:
await DoorstepAIDropoffSDK.stopRouteGeofencing();
Update Stops
Call updateRouteStops when the driver's route changes mid-shift (a stop added, removed, or reordered). It replaces the stop set without restarting stops that remain:
await DoorstepAIDropoffSDK.updateRouteStops({ stops: updatedStops });
Resuming After Relaunch
Route state is persisted, but the host still needs to call setApiKey and resumeRouteGeofencingIfNeeded() on every launch to restore it without re-supplying the stop list. It's a no-op if no route is active, so it's safe to call unconditionally — see Initialize above.
Getting Monitored Stops
getMonitoredStops() resolves to { stops: DeliveryStop[] }, the stops currently registered.
Background route events require locationAlways === 'granted'; see Capacitor Permissions.
Manual Geofencing
If you'd rather drive geofence enter/exit yourself instead of using In-SDK Route Geofencing above, wire up these four touchpoints:
| # | When this happens in the driver app | What you call |
|---|---|---|
| 1 | Driver app launches | Initialize |
| 2 | Driver enters the delivery geofence | Start Tracking |
| 3 | Driver confirms drop-off or takes proof of delivery | Mark Drop-off |
| 4 | Driver exits the delivery geofence | Stop Tracking |
1. Initialize
Initialize the SDK the same way as described in Initialize above. This step is identical for both flows.
2. Start Tracking
Choose the start method matching the destination data you have. Every Capacitor method receives one options object.
startDeliveryByAddress is an exact alias for startDeliveryByAddressType; both accept
StartDeliveryByAddressOptions.
import {
DoorstepAIDropoffSDK,
type AddressType,
} from '@doorstepai/dropoff-sdk-capacitor';
const byPlace = await DoorstepAIDropoffSDK.startDeliveryByPlaceID({
placeID: 'some_place_id',
deliveryId: 'delivery_12345',
timeoutSeconds: 1800,
customerId: 'customer_42',
driverId: 'driver_7',
});
const address: AddressType = {
streetNumber: '123',
route: 'Main Street',
subPremise: 'Apt 4B',
locality: 'San Francisco',
administrativeAreaLevel1: 'CA',
postalCode: '94102',
};
await DoorstepAIDropoffSDK.startDeliveryByAddress({
address,
deliveryId: 'delivery_12345',
coordinates: { lat: 37.7749, lng: -122.4194 },
manualForeground: false, // Android only
});
await DoorstepAIDropoffSDK.startDeliveryByAddressString({
address: '123 Main St, Apt 4B, San Francisco, CA 94102',
deliveryId: 'delivery_12345',
});
Every start resolves to { serverSessionId?: string }. Android includes the server id when the call creates a network-backed session. On iOS it arrives asynchronously through the sessionServerIdAssigned listener:
const idListener = await DoorstepAIDropoffSDK.addListener(
'sessionServerIdAssigned',
({ deliveryId, serverSessionId }) => {
console.log(deliveryId, serverSessionId);
},
);
// Later, when the listener's owner is disposed:
await idListener.remove();
Shared optional start fields are timeoutSeconds, customerId, and driverId. Address-based
methods also accept coordinates; supported Android starts accept manualForeground.
startDeliveryByPlusCode and startDeliveryByLatLng remain for compatibility but are deprecated.
On Android, those two legacy starts do not support manualForeground.
3. Mark Drop-off
Mark the drop-off the same way as described in Mark Drop-off above. This step is identical for both flows.
4. Stop Tracking
await DoorstepAIDropoffSDK.stopDelivery({
deliveryId: 'delivery_12345',
});
Framework notes
- Guard calls with
Capacitor.isNativePlatform()when code may run on the web or during SSR. - Next.js calls belong in
'use client'modules after mount. - Angular and Vue calls belong in their post-bootstrap lifecycle hooks.
- Rebuild and run
npx cap syncwhenever changed web code must ship in the native shell. - Wrap SDK calls in
try/catch; bad input, denied permissions, and native failures reject their promises. removeAllListeners()removes both DoorstepAI listener types at once; prefer each handle'sremove()method when ownership is scoped to a component.