Web quickstart

From nothing to a verified verdict. Every snippet below runs as written.

  1. 1

    Install the client

    The npm publish is still pending, so today the working install is from a clone:

    bash
    git clone https://github.com/neetixai/liveliness-sdks
    npm i ./liveliness-sdks/packages/web

    dist/ is not committed — the package's prepare script compiles it during install. Once the registry publish lands this becomes npm i @neetix/liveliness with no other change.

    Zero runtime dependencies — it uses the platform fetch. Node 18+ or any current browser.

  2. 2

    Get a test key

    bash
    curl -X POST https://liveliness.neetix.in/v1/signup \
      -H 'Content-Type: application/json' \
      -d '{"email":"you@company.com"}'

    Keep the test_api_key (lv_test_…). It is unmetered, so nothing below costs you credits while you build.

  3. 3

    Create the client

    ts
    import { NeetixClient } from "@neetix/liveliness";
    
    const client = new NeetixClient({ apiKey: process.env.NEETIX_API_KEY });

    baseUrl defaults to https://liveliness.neetix.in. Pass it only when you self-host.

    Server-side only for a live key

    A live key in browser JavaScript is readable by anyone on the page. From the browser, either use a test key for a demo, or proxy through your own backend and keep the live key there. For user-facing capture in production, use hosted verification links or the on-device edge build instead of shipping a key.

  4. 4

    Make the call

    Face match takes the selfie and the ID photo, and costs 2 credits:

    ts
    const res = await client.faceMatch(selfieBlob, idPhotoBlob, {
      clientRefId: "application-4821",
    });
    
    console.log(res.verdict);              // "approved" | "review" | "declined"
    console.log(res.warnings);             // ["LOW_LIGHT", ...]
    console.log(res.result.is_same_face);
    console.log(res.result.liveness_score);
    console.log(res.mode);                 // "test" — proves which key ran

    In a browser, selfieBlob is whatever your <input type="file"> or canvas gives you. In Node, read the file into a Blob:

    ts
    import { readFile } from "node:fs/promises";
    
    const selfie = new Blob([await readFile("selfie.jpg")], { type: "image/jpeg" });
    const idPhoto = new Blob([await readFile("id.jpg")], { type: "image/jpeg" });
  5. 5

    Branch on the verdict

    ts
    switch (res.verdict) {
      case "approved":
        return continueJourney();
      case "review":
        return queueForManualReview(res.warnings);
      case "declined":
        return stop(res.warnings);
    }

    Do not re-derive a decision from liveness_score — the verdict already applied the thresholds, and a hand-rolled cutoff is the most common way these integrations go wrong.

Handle errors as errors

Failures throw NeetixApiError, which carries the HTTP status and the server's message_code:

ts
import { NeetixApiError } from "@neetix/liveliness";

try {
  const res = await client.faceMatch(selfie, idPhoto);
} catch (err) {
  if (err instanceof NeetixApiError) {
    if (err.isInsufficientCredits) {
      // HTTP 402 — the prepaid balance is gone. Every call fails until top-up.
      return haltAndAlert();
    }
    console.error(err.status, err.messageCode, err.message);
  }
  throw err;
}

The codes you will actually meet: no_face_detected (422), insufficient_credits (402), unauthorized (401).

Liveliness on its own

When you already hold a selfie and only need the liveliness check — 1 credit:

ts
const live = await client.liveness(selfieBlob);
console.log(live.status);          // "Approved" | "Declined" | "Not Finished"
console.log(live.liveness_score);

Next

  • Get started — verdicts, warnings and credits in depth.
  • API reference — the full REST surface, generated from the OpenAPI document the server serves.