faceping.aidocs

React Native reference

Every public type in the FacePing React Native package: setup, enrolment, verification, the camera component and results.

Package: @faceping/react-native (npm, preview, under the preview tag). Starter app: faceping/react-native-starter. Android 7 (API 24) or later, on 64-bit phones (arm64-v8a) and x86_64 emulators. iOS 15.1 or later; Xcode 26 or later.

The package is a thin bridge over the native Android and iOS SDKs, built with Expo modules: it works in any Expo app with a development build (not Expo Go), and in bare React Native apps with Expo modules installed. New and old architecture. Tested with Expo SDK 57 and React Native 0.86.

Install#

Expo#

npx expo install @faceping/react-native@preview

npx expo install adds the config plugin to app.json. Give it options as below, then rebuild (npx expo prebuild, npx expo run:ios, npx expo run:android, or EAS Build):

app.json
{
  "expo": {
    "plugins": [
      ["@faceping/react-native", { "cameraPermission": "We use the front camera to check it's you." }]
    ]
  }
}
Plugin option Default Notes
cameraPermission A generic sentence The iOS camera prompt, set as NSCameraUsageDescription. Without it, iOS stops the app when the camera starts.
androidAbis true Builds Android for arm64-v8a and x86_64 only, the ABIs FacePing runs on. false keeps your own list.

Bare React Native#

npx install-expo-modules@latest     # once, if your app doesn't use Expo modules yet
npm install @faceping/react-native@preview
cd ios && pod install

Then do by hand what the plugin does:

  • iOS: add NSCameraUsageDescription to Info.plist (Xcode: the target's Info tab, Privacy - Camera Usage Description), and make sure the deployment target is 15.1 or later.
  • Android: in android/gradle.properties, set reactNativeArchitectures=arm64-v8a,x86_64.

How the native SDKs get into your app#

  • Android: the module depends on ai.faceping:faceping-android from Maven Central. Gradle fetches it like any other dependency, with the CAMERA and INTERNET permissions.
  • iOS: pod install downloads FacePing.xcframework.zip once from the faceping-ios release, checks its SHA-256 against the checksum pinned in the package, and vendors it. Offline or behind a proxy, set FACEPING_IOS_ZIP=/path/to/FacePing.xcframework.zip (checked the same way) or FACEPING_IOS_URL to a mirror before pod install. The simulator has no front camera: test the live check on an iPhone.

Setup#

import { FacePing } from '@faceping/react-native';

// Once, early. Resolves at once and loads the face models in the background.
await FacePing.init({ sandboxKey: 'fp_test_…' });   // sandbox: faces enrolled on the device
// await FacePing.init({ deviceToken: 'fpd_…' });   // production: faces synced from your check-in list

// Activates the sandbox key now, so a key problem shows up before the first person arrives.
await FacePing.sync();

FacePing is a single object you can call from anywhere in the app. Calling init again with the same options is fine (for example after a fast refresh); with different options it throws (call close() first). It resolves to { isSandbox, nativeSdkVersion }.

A key in the wrong format throws a setup error from init. A revoked key, or a first run with no connection, shows up on the first call that needs the key, so call sync() at start-up.

Options#

Option Type Default Notes
sandboxKey string A fp_test_ key from Developers → Sandbox keys.
deviceToken string Production. A fpd_ token for one check-in list. Set this or sandboxKey, not both.
region 'eu' 'eu' Your account's region (sets the API address).
apiBaseUrl string Overrides the region's address, for example for the UK or US region.
passiveLiveness boolean true Passive liveness on every check. Turn it off only if your app runs its own liveness check first.
autoSyncInterval number \| null 300 Production only, in seconds. null turns automatic sync off; call sync() yourself.

FacePing#

Call Returns Notes
enrol(id, photo) EnrolResult Sandbox only. Production enrolment happens with consent on your server or through the FacePing widget. Replaces any face already stored under id.
verifyLive(id, cameraRef) VerifyResult A live check on the <FaceCamera>: match, a head turn in a random direction, match again. Shows the prompts and sets the guide. About 10 seconds at most.
verify(id, photo) VerifyResult One photo, passive liveness only.
forget(id) boolean Sandbox: deletes the face now; false if there was none. Production: throws invalidOperation (remove the person through your server).
forgetAll() Deletes every face on the device now. In production the next sync brings back the people still enrolled.
enrolledIds() string[] Who can be verified on this device, as of the last enrol, forget or sync.
sync() SyncResult Production: downloads the latest faces for the device's check-in list; a revoked device wipes its faces. Sandbox: activates the key, or renews its licence when due.
close() Stops automatic sync and frees the engine. Call init again to start over.

Every call returns a promise.

Photos#

A photo is any of:

From Example
The <FaceCamera> (takes a still from the live view) FacePing.enrol('me', cameraRef)
A file URI (file://…, or on Android content://…, for example from a photo picker) FacePing.enrol('me', uri) or { uri }
Base64 JPEG or PNG (HEIC too on iOS) { base64 }, or a data:image/jpeg;base64,… URI

Photos are turned upright from their EXIF orientation and scaled down to 1280 pixels on the device before anything else happens.

FaceCamera#

The native front-camera view, with the oval face guide and the liveness prompts ("Turn your head left", "Look back at the camera"). The SDK runs the camera, so you don't need another camera library, and it asks for camera permission the first time the view appears. Give it a size.

const camera = useRef<FaceCameraRef>(null);

<FaceCamera ref={camera} style={{ height: 420 }} onCameraFailed={({ message }) => console.warn(message)} />
Prop Type Notes
showGuide boolean The face outline. Default true.
onCameraFailed ({ message }) => void The camera can't start: permission refused or no front camera. The message says which.
style, testID As for any view.

The ref (FaceCameraRef) has setGuideState('ready' | 'working' | 'match' | 'noMatch') and startCamera() (call it if your app gets camera permission another way). verifyLive sets the guide for you; set it yourself for other steps, for example 'working' while enrolling.

To translate or reword the prompts, use the native SDKs' strings: on Android the string resources faceping_prompt_look_at_camera, faceping_prompt_turn_left, faceping_prompt_turn_right and faceping_prompt_look_back; on iOS a FacePing.strings table with the keys faceping.prompt.lookAtCamera, faceping.prompt.turnLeft, faceping.prompt.turnRight and faceping.prompt.lookBack.

Backups#

The face store's encryption keys stay in the phone's Keychain or Keystore. On iOS, FacePing's folder is already excluded from backups. On Android, exclude files/faceping and the shared preferences faceping.keys from Auto Backup, or turn backup off (Expo: "android": { "allowBackup": false }).

Results#

type EnrolResult = {
  outcome: 'enrolled' | 'noFace' | 'multipleFaces' | 'faceTooSmall' | 'notLive' | 'limitReached';
  id: string;
  isEnrolled: boolean;
};

type VerifyResult = {
  outcome: 'match' | 'noMatch' | 'notEnrolled' | 'noFace' | 'livenessFailed'
         | 'challengeFailed' | 'expired' | 'leaseExpired';
  similarity: number;           // cosine similarity; the match threshold is 0.363
  livenessScore: number | null; // passive liveness of the deciding frame
  elapsedMs: number;            // how long the check took on the device
  isSandbox: boolean;
  isMatch: boolean;
};

type SyncResult = {
  status: 'ok' | 'gone' | 'revoked' | 'unavailable';
  enrolled: number;
  leaseUntil: Date | null;      // production: verifies offline until then
  deleteAfter: Date | null;
};

The outcome types are exported as EnrolOutcome and VerifyOutcome. limitReached means the sandbox already holds 25 faces on this device: forget one first. leaseExpired (production) means the device hasn't synced for longer than its lease, 24 hours by default; it needs a connection before it verifies again.

Errors#

Set-up and device problems throw a FacePingError with a message ready to show and a code. isFacePingError(e, 'setup') checks for one.

Code When
setup The key or token is missing, invalid or revoked, the sandbox key couldn't be activated (the first run needs a connection), or init hasn't been called.
model The face models couldn't load on this device. Send people to your normal way in.
invalidOperation Not allowed in this mode or state, for example enrolling on the device in production.
invalidArgument A bad argument: an empty id, or a photo that isn't a readable image.
storage The device's secure storage failed.
camera The camera couldn't start or didn't deliver a picture, or the <FaceCamera> isn't on screen.
unknown Anything else. The message says what.

Everything else is a result, not an exception: a face check never throws because someone looked away.