Skip to main content
The tracenow browser snippet collects fingerprint components from the browser, sends them to /identify, and returns a short-lived device token (dt_...). You forward that token to your server, which validates it and gets automation signals — headless browser detection, canvas randomization, timezone mismatch, replay protection.

How it works

Browser                         Your server              tracenow
  │                                 │                        │
  ├─ load snippet ──────────────────┤                        │
  ├─ tracenow.identify() ──────────────────────────────────► │ POST /identify
  │◄─ device_token (dt_...) ───────────────────────────────── │
  ├─ send token with your request ─► │                        │
  │                                 ├─ POST /validate ──────► │
  │                                 │◄─ automation signals ── │
  │                                 ├─ POST /trace ─────────► │
  │                                 │◄─ verdict ─────────────  │

1. Load the snippet

Add the snippet to your HTML. Use your publishable key (pk_live_...) — it’s safe to expose in the browser.
<script src="https://cdn.tracenow.io/trace.min.js"></script>
For Next.js, load it in your root layout:
app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <Script src="https://cdn.tracenow.io/trace.min.js" strategy="afterInteractive" />
      </body>
    </html>
  );
}

2. Collect a device token

Call tracenow.identify() before the user submits a form. The call is async and typically resolves in under 200ms.
TypeScript
const tn = tracenow("pk_live_xxxxxxxxxxxxxxxxxxxx");
const { device_token } = await tn.identify();
Include the token in your request payload:
TypeScript (login form)
async function handleLogin(email: string, password: string) {
  const tn = tracenow("pk_live_xxxxxxxxxxxxxxxxxxxx");
  const { device_token } = await tn.identify();

  const res = await fetch("/api/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password, device_token }),
  });
}

3. Validate the token server-side

On your server, pass the token to /validate before you act on it. Tokens are single-use — validating the same token twice returns an error, which catches replay attacks.
TypeScript (Next.js API route)
export async function POST(req: Request) {
  const { email, password, device_token } = await req.json();

  const validation = await fetch("https://api.tracenow.io/validate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      publishable_key: process.env.NEXT_PUBLIC_TRACENOW_PUBLISHABLE_KEY,
      device_token,
    }),
  }).then((r) => r.json());

  if (!validation.valid) {
    return Response.json({ error: "Invalid device token" }, { status: 400 });
  }

  // automation signals
  const { is_headless, automation_detected, canvas_randomized, timezone_ip_mismatch } = validation;
}

4. Pass the token to /trace

For a full risk evaluation, include the device_token in your /trace call alongside other signals. This gives the trace endpoint device-level context for your policies.
TypeScript
const trace = await fetch("https://api.tracenow.io/trace", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TRACENOW_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    event: "login",
    status: "attempted",
    ip: clientIp,
    email,
    device_token, // from the browser
  }),
}).then((r) => r.json());

switch (trace.verdict) {
  case "allow":   return issueSession(user);
  case "challenge": return requireMfa(user);
  case "deny":    return Response.json({ error: "Blocked" }, { status: 403 });
}

Token lifecycle

  • Tokens expire after 5 minutes (expires_in: 300).
  • Each token is single-use/validate marks it consumed. A replayed token returns 409 token_replayed.
  • If identify() fails (network error, blocked by browser extension), handle it gracefully — don’t block the user action, just omit the token from the request and proceed without device signals.
TypeScript
let device_token: string | null = null;
try {
  const tn = tracenow("pk_live_xxxxxxxxxxxxxxxxxxxx");
  ({ device_token } = await tn.identify());
} catch {
  // snippet unavailable — continue without device signals
}