Client SDK
@facegate/client is the framework-free core of FaceGate. Pure JS/TS, zero runtime dependencies. It handles camera capture, the AWS liveness flow, and all HTTP communication with the FaceGate server.
Use this package directly if you are on Vue, Svelte, Angular, or plain JavaScript. If you are on React, use @facegate/react — it wraps this client with a drop-in <FaceGate /> component and saves you the camera and state plumbing.
Install
npm install @facegate/clientConstruct the client
FaceGateClient is the main class. Give it your API key. Everything else has defaults.
import { FaceGateClient } from '@facegate/client';
const fg = new FaceGateClient({
apiKey: 'fg_test_your_key_here',
});Constructor options
The constructor takes a FaceGateConfig.
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Required. fg_live_… or fg_test_…. |
baseUrl | string | https://api.facegate.ai | Server base URL. Trailing slash is stripped. |
confidenceThreshold | number | 95 | Face match confidence threshold (0–100). |
challengeTimeout | number | 30000 | Liveness challenge timeout in ms. |
provider | 'supabase' | 'auth0' | 'firebase' | null | Auth provider for Tier 2 session creation. Sent with createSession(). |
providerConfig | Record<string, unknown> | {} | Provider-specific config. |
The resolved config is exposed read-only on fg.config.
Methods
All network methods return promises and retry on retryable failures (GET retries 3×, mutations retry 2×, with exponential backoff). A 401 throws InvalidApiKey; non-retryable errors throw immediately.
| Method | Signature | Returns | Endpoint |
|---|---|---|---|
match | match({ image: string }) | Promise<MatchResult> | POST /v1/match |
createLivenessSession | createLivenessSession() | Promise<LivenessSession> | POST /v1/liveness/session |
verify | verify({ livenessSessionId: string }) | Promise<VerifyResult> | POST /v1/verify |
createSession | createSession(verificationToken: string) | Promise<FaceGateSession> | POST /v1/session |
enroll | enroll(params) | Promise<EnrollResult> | POST /v1/enroll |
health | health() | Promise<HealthStatus> | GET /v1/health |
on | on(listener) | () => void (unsubscribe) | — |
match({ image })
Match a face without liveness. A fast identity check — no anti-spoofing. image is a base64-encoded JPEG (a data: URL prefix is stripped automatically).
const result = await fg.match({ image: frame.imageData });
// result.matched — boolean
// result.confidence — 0–100
// result.user — MatchedUser | nullcreateLivenessSession()
Creates an AWS Face Liveness session. Returns the session_id, temporary AWS credentials, and region that the AWS FaceLivenessDetector UI component consumes. This is the start of a real, anti-spoofed verification.
const session = await fg.createLivenessSession();
// hand session.session_id / credentials / region to the AWS liveness UIverify({ livenessSessionId })
Verify a face against a completed AWS liveness session. Pass the session_id from createLivenessSession() once the AWS component finishes. On success the server returns a signed verification_token you exchange for a session, plus risk and device data. The client persists the returned device_id to localStorage and emits a verified event.
const result = await fg.verify({ livenessSessionId: session.session_id });
// result.matched — boolean
// result.confidence — 0–100
// result.verification_token — string | null (use with createSession)
// result.user — MatchedUser | null
// result.risk — RiskAssessment
// result.re_enrollment_required — booleancreateSession(verificationToken)
Exchange a verification_token from a successful verify() for an auth session. If a provider was set on the client, the returned FaceGateSession.provider_session carries that provider's tokens (e.g. Supabase). Emits a session_created event.
const fgSession = await fg.createSession(result.verification_token!);
// fgSession.token — signed JWT
// fgSession.refresh_token — string
// fgSession.expires_in — seconds
// fgSession.user — MatchedUser
// fgSession.provider_session — provider tokens | nullenroll(params)
Enroll a new face. Provide either a completed livenessSessionId (anti-spoofed enrollment) or a raw image, plus a name.
| Param | Type | Required | Description |
|---|---|---|---|
livenessSessionId | string | one of these two | Completed AWS liveness session. |
image | string | one of these two | Base64 face image. |
name | string | yes | Person's name. |
role | string | no | Role assignment. Defaults to 'user'. |
externalId | string | no | Your own ID for this user. |
email | string | no | User email. |
consentId | string | no | Biometric consent record ID. |
metadata | Record<string, unknown> | no | Arbitrary metadata. |
const enrolled = await fg.enroll({
livenessSessionId: session.session_id,
name: 'Ada Lovelace',
role: 'admin',
});
// enrolled.user_id — string
// enrolled.enrollments — { faceId, angle, confidence }[]health()
Server health check.
const status = await fg.health();
// status.status — 'healthy' | 'degraded' | 'unhealthy'
// status.version, status.provider, status.uptime_secondson(listener)
Subscribe to client events. Returns an unsubscribe function. Events are FaceGateEvent — including verified, session_created, camera_ready, and error.
const off = fg.on((event) => {
if (event.type === 'verified') console.log('confidence', event.data.confidence);
});
// later: off();The Camera class
Camera is the only module that touches navigator.mediaDevices. It opens a stream, attaches it to a <video> element, and grabs JPEG frames. Use it when you are building your own capture UI (the React SDK does this for you).
import { Camera } from '@facegate/client';Static methods
| Method | Returns | Description |
|---|---|---|
Camera.isAvailable() | boolean | Is the MediaDevices API present in this browser. |
Camera.listDevices() | Promise<CameraDevice[]> | List video input devices with inferred facing. |
Instance methods
| Member | Signature | Description |
|---|---|---|
open | open(deviceId?: string): Promise<MediaStream> | Request permission and start the stream. Throws CameraPermissionDenied / CameraUnavailable / CameraError. |
attachToVideo | attachToVideo(video: HTMLVideoElement): void | Bind the stream to a <video> element for rendering and capture. |
captureFrame | captureFrame(): CapturedFrame | Grab a single JPEG frame from the attached video. |
captureFrames | captureFrames(count: number, intervalMs: number): Promise<CapturedFrame[]> | Grab multiple frames over time. |
close | close(): void | Stop tracks and release the camera. |
stream | get stream(): MediaStream | null | The active stream, or null. |
isReady | get isReady(): boolean | Whether a video element is attached and ready for capture. |
A CapturedFrame is { imageData, timestamp, width, height }, where imageData is a base64-encoded JPEG data: URL.
const camera = new Camera();
await camera.open(); // permission + stream
camera.attachToVideo(videoElement); // <video> in your DOM
const frame = camera.captureFrame(); // CapturedFrame
camera.close(); // releaseEnd-to-end example
Camera → capture → match. This is the fast identity-check path (no liveness). For a full anti-spoofed flow, swap the match() call for the AWS liveness session + verify() + createSession() sequence shown in the REST API docs.
import { FaceGateClient, Camera, CameraPermissionDenied } from '@facegate/client';
const fg = new FaceGateClient({ apiKey: 'fg_test_your_key_here' });
async function signIn(videoEl: HTMLVideoElement) {
const camera = new Camera();
try {
// 1. Open the camera and render it
await camera.open();
camera.attachToVideo(videoEl);
// 2. Wait for the video to be ready, then capture a frame
await new Promise<void>((resolve) => {
if (camera.isReady) return resolve();
videoEl.onloadeddata = () => resolve();
});
const frame = camera.captureFrame();
// 3. Match the face
const result = await fg.match({ image: frame.imageData });
if (!result.matched || result.confidence < 95) {
console.log('Not recognized');
return null;
}
// 4. Handle the matched user
console.log('Welcome back,', result.user?.name);
return result.user;
} catch (err) {
if (err instanceof CameraPermissionDenied) {
console.error('Camera permission was denied');
} else {
console.error('Sign-in failed', err);
}
return null;
} finally {
camera.close();
}
}Full verification flow
For real authentication, run the liveness path. createLivenessSession() hands off to the AWS FaceLivenessDetector UI; once it completes you call verify() with the same session ID, then exchange the token for a session.
// 1. Start an AWS liveness session
const session = await fg.createLivenessSession();
// 2. Run the AWS FaceLivenessDetector component with session.credentials / region.
// When it completes successfully, you have session.session_id.
// 3. Verify the result
const result = await fg.verify({ livenessSessionId: session.session_id });
if (!result.matched) throw new Error('Face not recognized');
// 4. Exchange the token for a session
const fgSession = await fg.createSession(result.verification_token!);
console.log('JWT:', fgSession.token);Error types
Network and camera failures throw typed errors, all subclasses of FaceGateError: CameraError, CameraPermissionDenied, CameraUnavailable, LivenessTimeout, LivenessFailed, VerificationFailed, ApiError, NetworkError, HandoffExpired, and InvalidApiKey. Each carries a code and a retryable flag.