Docs menuAll pages, quickstarts and this page’s contents

JavaScript quickstart

Add Kaidn to a browser app and give every visitor a stable device id you can send to your backend. Scaffolded with Vite, and the example is the one most people start with: stopping the same person opening account after account.

Estimated time: under 10 minutes

Before you start

  • Node 20+ and npm, to run Vite.
  • A free Kaidn account. Register: 10,000 events a month, no card.

This is the frontend half, and on its own it blocks nothing. By the end you will have a device id in the console. That id only becomes fraud prevention when your server sends it to /v1/score and acts on the verdict, so finish with a backend quickstart: Python, PHP or Node.js. A page that stopped here would leave you believing you were protected while nothing was being scored.

01

Get your publishable key

The browser needs a different key from your server, and the distinction matters.

  1. Create an account if you do not have one.
  2. Go to Fraud Scoring API → Device trackers and create a tracker, listing the domains it may run on (include localhost while you build).
  3. Copy the publishable key. It starts pk_live_.

This key is meant to be visible in page source. It is domain-locked and can only send fingerprints: it cannot score anything, read your data, or be used from a site you did not authorise. Your kdn_live_ secret key is the opposite and must never reach a browser.

02

Set up your project

Scaffold a vanilla JavaScript app. Skip to step 3 if you have a project already.

Terminal
npm create vite@latest kaidn-js-quickstart -- --template vanilla
cd kaidn-js-quickstart
npm install

Run it and open http://localhost:5173, Vite's default. You should see the Vite welcome page.

Terminal
npm run dev
03

Build the signup form

Something to attach to. Replace the body of index.html:

index.html
<body>
  <main class="wrap">
    <h1>Create an account</h1>
    <form id="signup">
      <label for="email">Email</label>
      <input id="email" name="email" type="email" placeholder="you@example.com" required />

      <label for="password">Password</label>
      <input id="password" name="password" type="password" required />

      <!-- @kaidn/fp fills this in before the form submits -->
      <input id="kaidn_device_id" name="kaidn_device_id" type="hidden" />

      <button type="submit">Create account</button>
    </form>
    <pre id="out"></pre>
  </main>
  <script type="module" src="/src/main.js"></script>
</body>

And enough CSS to see it. Overwrite src/style.css:

src/style.css
html, body { margin: 0; font-family: system-ui, sans-serif; }

.wrap {
  max-width: 380px; min-height: 100vh; margin: 0 auto;
  display: flex; flex-direction: column; justify-content: center; gap: .75rem;
  padding: 1rem;
}

form { display: flex; flex-direction: column; gap: .5rem; }
input { padding: .6rem; border: 1px solid #ccc; border-radius: 6px; font: inherit; }
label { font-size: .85rem; color: #555; }

button {
  margin-top: .5rem; padding: .7rem 1.2rem; font: inherit; cursor: pointer;
  background: #111; color: #fff; border: 0; border-radius: 6px;
}

pre { font-size: .78rem; color: #444; white-space: pre-wrap; word-break: break-all; }
04

Install and initialise

Terminal
npm install @kaidn/fp

Then open src/main.js and replace it. beacon()does two things in one call: it fingerprints the browser, and it posts that to the Kaidn edge so your connection's TLS handshake is captured against this device. Your server never has to forward either.

src/main.js
import "./style.css";
import { beacon } from "@kaidn/fp";

// Domain-locked and safe in page source. Yours is in the dashboard under
// Fraud Scoring API -> Device trackers.
const PUBLISHABLE_KEY = "pk_live_your_key_here";
const ENDPOINT = "https://api.kaidn.io/v1/fp";

For production, put the key in an environment variable. Vite exposes anything prefixed VITE_, so import.meta.env.VITE_KAIDN_PK keeps it out of your repository. It is not a secret, but a key you can rotate without a code change is worth having.

05

Collect on submit

Fingerprint the visitor at the moment they act, rather than on every page load. It is one fewer thing running on pages that do not need it, and the signal is freshest exactly when you are about to use it.

src/main.js
const form = document.getElementById("signup");
const out = document.getElementById("out");

form.addEventListener("submit", async (e) => {
  e.preventDefault();

  let deviceId = null;
  try {
    // Fingerprints the browser AND beacons it to the edge, so Kaidn can
    // capture the TLS handshake for this device. Returns in ~200ms.
    const fp = await beacon(ENDPOINT, PUBLISHABLE_KEY);
    deviceId = fp.device_id;
  } catch {
    // FAIL OPEN. A blocked script, an offline visitor or an ad blocker must
    // never stop somebody signing up. Your backend scores the event either
    // way; it just has one signal fewer.
  }

  document.getElementById("kaidn_device_id").value = deviceId ?? "";
  out.textContent = "device_id: " + (deviceId ?? "(unavailable)");

  // Send it to YOUR server, which scores it and decides.
  // await fetch("/api/signup", {
  //   method: "POST",
  //   headers: { "content-type": "application/json" },
  //   body: JSON.stringify({
  //     email: form.email.value,
  //     kaidn_device_id: deviceId,
  //   }),
  // });
});

Note what is not here: no verdict, and no decision. The browser is an untrusted place to make one, so it never sees a score. It produces an id, your server produces the judgement.

06

Score it on your server

This is the step that turns a device id into fraud prevention. Everything before it collects; nothing before it decides.

C#/.NETsoonGosoonJavasoon

npm install @kaidn/sdk

api/signup.js
import { Kaidn } from "@kaidn/sdk";

// Your SECRET key. Server only, never the browser.
const kaidn = new Kaidn({ apiKey: process.env.KAIDN_API_KEY });

// Express, Fastify, Hono, a Next.js route handler: the call is the same.
app.post("/api/signup", async (req, res) => {
  let r;
  try {
    r = await kaidn.score({
      event: "signup",
      ip: req.ip,
      email: req.body.email,
      device_id: req.body.kaidn_device_id,   // the id the browser collected
    });
  } catch {
    // FAIL OPEN. An outage in your fraud vendor must never become an
    // outage in your signup form.
    return res.json({ ok: true });
  }

  if (r.verdict === "block") {
    // Do not name the signal. It teaches the next attempt.
    return res.status(403).json({ error: "We could not create that account." });
  }

  const user = await createUser(req.body);
  if (r.verdict === "review") await flagForReview(user.id, r.event_id, r.reason_text);

  res.json({ ok: true });
});

Three answers, and you decide what each one means. Kaidn never blocks anybody on your behalf.

Whichever you pick, the client covers every endpoint an API key can reach, not just scoring: check.email, batch.score, lists.add, config.set, label, forget, suppressions, events, stats. None of them belong in a browser, because all of them need your secret key. Full reference.

07

Test it

Terminal
npm run dev

Open http://localhost:5173, fill the form, submit. You should see the id on the page and in the hidden field:

Output
device_id: 8f1c2ae9d4b7c3e05a1f6b28d9074e3c

Turn ad blockers off on localhost while you test. Any fingerprinting script can be blocked by an extension, and when that happens you get nothing rather than a cautious answer. We measured what that does to a competitor and published the numbers, including the part where our own endpoint is only unblocked because nobody has listed it yet. This is why the code above fails open, and why the device id is one signal in a score rather than the whole thing.