Netrik/DocsSign in

Developer docs

Add a live face check to sign-up, login or withdrawals. One REST call starts it, the player does it in their browser, and your backend gets the verdict by signed webhook.

How it works

  1. Your backend creates a session for one player: register the first time, verify after that.
  2. The player does the check at the session's link (redirect, iframe or popup) or on your page with the web SDK: they fit their face to a mask, follow random head turns and blink.
  3. Netrik decides: liveness, anti-spoof and, for verify, a match against the registered face. Your backend gets the result by webhook, or asks for it.

A session is one check for one player. It lasts 15 minutes and allows 5 attempts for failures a better try can fix (light, the moves, liveness). A failure no retry can fix ends it at once (the face is already registered, no such user, the face doesn't match). The player only ever sees passed or failed; the reasons and scores go to you.

Quickstart

  1. Sign in to the dashboard and open Integration: copy your API key and set your webhook URL.
  2. If you'll use an iframe, a popup or the web SDK, add your site under Allowed origins.
  3. Create a session from your backend, send the player to its url, and handle the session.completed webhook.
  4. Use Run a test on the Integration page to try the whole flow yourself.

Authentication

Every API call sends your key as a Bearer token: Authorization: Bearer fk_live_…. The base URL is https://verify.netrik.online.

Keep the key on your server, never in a web page or app. You can see it again on the Integration page after confirming it's you, and make a new one at any time (the old one stops working at once).

Create a session

POST /v1/sessions

FieldType
userIdstring, requiredYour id for the player: 1–64 of A–Z a–z 0–9 . _ @ -. Use a stable id, not an email you may change.
typestring, requiredregister (stores their face) or verify (checks it's the same person).
checkstring3d: head turns, then a blink. 2d: two blinks. Default: your Verification rules setting.
metadataobjectAnything up to 1 KB, returned with the result (an order id, the action being confirmed…).
curl https://verify.netrik.online/v1/sessions \
  -H "Authorization: Bearer fk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "user-123", "type": "register", "metadata": { "orderId": 42 } }'
{
  "sessionId": "ses_…",
  "token": "…",
  "url": "https://verify.netrik.online/?session=…",
  "expiresAt": "2026-11-01T12:15:00.000Z"
}

Every response carries your credits left in the x-netrik-credits header. Give url to the player, or token to your page for the web SDK.

Show the check

Send the player to the session's url in one of three ways. It works on any modern phone or laptop browser with a camera, in 8 languages, with nothing to install.

// your backend created the session; send the player there
res.redirect(session.url);

For an iframe or popup, add your site under Allowed origins: only those sites may embed the check, and they receive the outcome as a postMessage { source: "netrik", sessionId, passed, status }. The iframe needs allow="camera". Treat the message as a hint for your UI; the webhook is what you trust.

Web SDK

Runs the check on your own page, no iframe: it covers the page with the full-screen check and resolves when it's over. Add your site under Allowed origins first; requests from any other site are refused.

<script type="module">
  import { start } from "https://verify.netrik.online/sdk.js";

  // token: from POST /v1/sessions, passed to this page by your backend
  const result = await start({ session: token });
  // { sessionId, isPassed: true | false,
  //   status: "passed" | "failed" | "expired" | "error" | "cancelled", error? }
</script>

cancelled: the player closed it. error: no camera, or the session can't be used (error says why). The check runs in a shadow root, so your CSS and ours stay apart. Plain scripts can call window.Netrik.start once the module has loaded. Like the postMessage, the result is a UI hint: trust the webhook.

Get a session

GET /v1/sessions/:sessionId returns the same data object as the webhook. Use it if you'd rather poll, or to double-check a webhook.

curl https://verify.netrik.online/v1/sessions/ses_… \
  -H "Authorization: Bearer fk_live_…"
status
pendingNot finished yet.
passedThe check passed.
failedA failure no retry can fix, or all 5 attempts used.
expired15 minutes passed without a result.

Webhooks

Set your endpoint on the Integration page, where Send test sends a webhook.test. Answer 2xx quickly; anything else is retried after 1, 5 and 30 minutes and 2 hours. The URL must be https:// on a public address.

POST https://your-backend.example/netrik
x-netrik-signature: v1=5f2c…
x-netrik-timestamp: 1793472000
x-netrik-delivery: ses_…

{
  "id": "ses_…",
  "type": "session.completed",
  "createdAt": "…",
  "data": {
    "sessionId": "ses_…", "userId": "user-123", "type": "register", "check": "3d",
    "status": "passed", "attempts": 1,
    "result": { "passed": true, "reason": "registered", "real": 0.82, "live": 0.82, "age": 31 },
    "metadata": { "orderId": 42 },
    "createdAt": "…", "expiresAt": "…", "completedAt": "…"
  }
}
EventWhendata
session.completedA session passed, failed or expired.The session, with result and metadata.
billing.low_creditsYour balance dropped to a fifth of your plan's credits.{ credits, plan }
billing.payment_receivedA USDT payment was confirmed on chain.{ paymentId, kind, item, amount, txHash }
billing.renewal_dueYour paid month ends soon.{ plan, paidUntil }
billing.plan_unpaidYour paid month ended unpaid.{ plan, paidUntil, blockedAt }
webhook.testYou pressed Send test.{ sessionId: null, message }

Use x-netrik-delivery (the event id) to ignore a retry you've already handled.

Verify signatures

Each delivery is signed with your webhook signing secret (Integration page). x-netrik-signature is v1= plus the hex HMAC-SHA256 of `${x-netrik-timestamp}.${rawBody}`. Compare in constant time, and reject old timestamps so a captured request can't be replayed.

import crypto from "node:crypto";

// rawBody: the request body exactly as received (not re-serialised JSON)
function fromNetrik(req, rawBody, secret) {
  const ts = String(req.headers["x-netrik-timestamp"] ?? "");
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // older than 5 minutes
  const expected = "v1=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  const got = String(req.headers["x-netrik-signature"] ?? "");
  return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got));
}

Results

Field
passedtrue or false: the decision.
reasonWhy, in words: registered, ok, face does not match the registered face, face already registered, username already registered, username not registered, account is inactive, liveness check failed, or which move wasn't done. For your logs and support, not for matching in code: use passed and status.
similarityverify only: how close the face is to the registered one, 0–1.
real, liveAnti-spoof and liveness scores, 0–1. Thresholds are set under Verification rules.
ageEstimated age in years from the selfie, or null. An estimate, off by a few years either way: a signal, not proof of age.

A face can only be registered once per business: registering the same face under a second userId fails with face already registered, which is how multi-accounting is caught.

Errors and limits

Errors are JSON: { "error": "…" }.

StatusMeaning
400A field is missing or invalid; error says which.
401Missing or wrong API key.
402Out of credits, or a register would pass your plan's stored users (verifies of existing users go on).
403Your business is disabled.
404No such session (sessions belong to the key that made them).
429Too many sessions: 60 a minute, plus your plan's daily cap (200 on Free). Wait for Retry-After seconds.

Credits

Each check costs credits ($0.01 each): a registration 10, a verification 5. A session is charged once, passed or failed, and each failed attempt the player retries after costs 1; a session that expires unused is free. You start with 500 free credits; plans and prices are on the pricing page.

Photos

Netrik keeps a small face photo from each registration and check, sealed with your public key: create a key pair (or upload your own public key) on the Integration page. Only your private key, in your browser, can open them; Netrik can't. Check photos are deleted after your plan's period (Free 7 days, Starter 30 days, Growth 90 days, Scale 180 days).

Stuck? Email hello@netrik.online.