Capacitor integration
This guide describes SDK installation in your Capacitor application and the capture-then-upload workflow. See the overview for delivery, the SDK key and environments.
Prerequisites
- Capacitor 7, with its Android and/or iOS platforms and the build tools required by Capacitor.
- Xcode and CocoaPods for iOS; Android Studio for Android.
- The
certificall-mobile-sdk-capacitor-<version>.tgzpackage provided by Certificall. - A physical device to validate the production workflow.
Capture and upload work in the native iOS or Android application. These methods are unavailable in a browser, including with ionic serve.
1. Install and synchronize
Copy the delivered package to vendor/, then declare the dependency:
The package is now named @certificall/mobile-sdk-capacitor: rename the dependency key and your imports. The motion permission no longer exists: request ['camera', 'location']. The rest of the API is unchanged.
{
"dependencies": {
"@certificall/mobile-sdk-capacitor": "file:./vendor/certificall-mobile-sdk-capacitor-<version>.tgz"
}
}
npm install
npx cap sync
The package contains the native binaries. cap sync installs the plugin into your application's platforms.
2. Configure iOS permissions
Add to Info.plist:
<key>NSCameraUsageDescription</key>
<string>Required to capture certified photos.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Required to certify the photo's location.</string>
<key>NSMotionUsageDescription</key>
<string>Required to collect motion during capture.</string>
You do not need photo library permission. Configure your application's signing in Xcode before running it on a device.
3. Configure Android
Declare permissions in AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
The SDK includes its own file provider and the declarations needed to detect the camera. You do not need to add a FileProvider for the SDK.
Synchronize, then rebuild the native application:
npx cap sync android
npx cap open android
For iOS:
npx cap sync ios
npx cap open ios
4. Request permissions and initialize
Request permissions before initialization, which starts position and motion collection. This example prepares the workflow after location access and, on Android, camera access have been granted.
import { Capacitor } from '@capacitor/core';
import { CertificallTrust } from '@certificall/mobile-sdk-capacitor';
export async function prepareCapture(): Promise<boolean> {
const permissions = await CertificallTrust.requestPermissions({
permissions: ['camera', 'location'],
});
if (permissions.location !== 'granted') return false;
if (
Capacitor.getPlatform() === 'android' &&
permissions.camera !== 'granted'
) {
return false;
}
await CertificallTrust.initialize({
apiKey: '<your SDK API key>',
baseUrl: 'https://dev.certificall.app/certificall/',
appVersion: '1.0.0',
});
return true;
}
On iOS, requestPermissions() requests location access; camera authorization is handled when the camera opens. The camera field returned by checkPermissions() is not a check of the iOS system authorization.
Call preparation when your screen opens. appVersion is the version of your application.
5. Capture, then upload
import { CertificallTrust } from '@certificall/mobile-sdk-capacitor';
const photo = await CertificallTrust.takePhoto({
reportToken: 'JOB-2026-001',
metadata: { site: 'Paris', zone: 'A1' },
});
// Save this JSON to your storage before upload if the capture
// must survive the application being closed.
const serializedPhoto = JSON.stringify(photo);
const sent = await CertificallTrust.sendPhoto({ photo });
console.log('Case:', sent.caseId);
console.log('PDF certificate:', sent.pdfUrl);
console.log('Warnings:', sent.warnings);
The serializedPhoto variable is not persistent storage. Your application chooses how to save it, read it back and trigger upload. See Storage and retries.
With Capacitor, pass { photo } to sendPhoto(). With React Native, pass the capture directly.
API surface
| Method | Return value | Use |
|---|---|---|
initialize({ apiKey, baseUrl, appVersion }) | Promise<void> | Configuration; all three parameters are required. |
checkPermissions() | Promise<PermissionStatus> | Permission states reported by the plugin. |
requestPermissions({ permissions }) | Promise<PermissionStatus> | Request permissions. |
takePhoto({ reportToken?, metadata? }?) | Promise<CapturedPhoto> | Local capture with its signals. |
sendPhoto({ photo }) | Promise<SentPhoto> | Upload and certification. |
isDebugBuild() | Promise<{ debugBuild: boolean }> | Application build type. |
getNetworkStatus() | Promise<{ connected, connectionType }> | Current network status. |
startNetworkMonitoring() | Promise<void> | Enable networkStatusChange events. |
stopNetworkMonitoring() | Promise<void> | Stop network monitoring. |
getCurrentPosition() | Promise<LocationPosition> | Current position. |
watchPosition(callback) | Promise<string> | Position monitoring; returns an identifier. |
getCurrentMotion() | Promise<MotionData> | Current motion. |
watchMotion(callback) | Promise<string> | Motion monitoring; returns an identifier. |
clearWatch({ id }) | Promise<void> | Stop position or motion monitoring. |
getDeviceSnapshot({ includeLocation?, includeMotion? }) | Promise<DeviceSnapshot> | Device snapshot. |
For capture and upload errors, read error.code and additional data in error.data: reasons, status and reason. See the error reference.
Monitor connectivity
You can use network events to decide when to process your upload queue:
const listener = await CertificallTrust.addListener(
'networkStatusChange',
status => {
console.log(status.connected, status.connectionType);
// When connectivity returns, your application can process its queue.
},
);
await CertificallTrust.startNetworkMonitoring();
// When monitoring ends:
await CertificallTrust.stopNetworkMonitoring();
await listener.remove();
connectionType is wifi, cellular, none or unknown. A connected status does not guarantee that Certificall is reachable: also handle sendPhoto() errors.
Troubleshooting
| Symptom | Check |
|---|---|
| Plugin not detected | Run npm install and npx cap sync again, then rebuild the application. |
| Capture unavailable in a browser | Run the application on iOS or Android. |
| Android permission denied | Request permissions before initialize(). |
NOT_INITIALIZED | Wait for initialize() to resolve before capture or upload. |
DEVICE_NOT_TRUSTED | Read error.data.reasons and device checks. |
INVALID_PHOTO | Pass { photo } with the complete object returned by takePhoto(). |
SERVER_REJECTED | Check error.data.status, error.data.reason, the key and environment URL. |
| Development photo rejected | Use pre-production; for production, take a new photo with a distribution build. |