Quickstart
Add an age check to a Node.js app. You'll create a session on your server, open it in the browser and read the verified result back.
1. Get an API key#
- Create a business account and click New app.
- Copy the secret key that's shown once. It starts with
lkp_sk_. - Store it as a server-side environment variable. Never ship it to browsers or commit it to git.
LKP_API_URL=https://lkp-verifier.onrender.com
LKP_SECRET_KEY=lkp_sk_...2. Create a verification session#
A session describes what you want to know. Create one per verification attempt, on your server, when the user clicks your “Verify” button.
app.post("/api/verify/start", async (req, res) => {
const r = await fetch(`${process.env.LKP_API_URL}/v1/verification-sessions`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LKP_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
requirements: [{ kind: "AGE_OVER", minAge: 18 }],
// "age_estimation" (fastest selfie check), "full_id" (passport/ID card), or "either" (user picks)
kycWorkflow: "age_estimation",
reference: req.session.cartId, // optional: your own ID, echoed back
}),
});
const session = await r.json();
req.session.lkpSessionId = session.id; // remember which session belongs to this user
res.json({ url: session.url });
});The response contains the session ID and the hosted URL where the user completes the check:
{
"id": "vs_3f9c2a…",
"object": "verification_session",
"status": "pending",
"claims": ["Age >= 18"],
"url": "https://lkp-three.vercel.app/verify/vs_3f9c2a…",
"expiresAt": "2026-11-06T15:30:00.000Z",
"result": null
}3. Present the verification flow#
You can open verification as an in-page modal dialog directly on your website, embed it into a custom page or checkout container, open a popup window, or redirect to LKP. See Modal, embed and hosted flow for all options.
<button id="verify">Verify my age (18+)</button>
<script src="https://lkp-three.vercel.app/sdk/lkp.js"></script>
<script>
document.getElementById("verify").onclick = async () => {
const { url } = await fetch("/api/verify/start", { method: "POST" }).then((r) => r.json());
// Opens a sleek in-page modal dialog right on your site (never blocked by popup blockers)
const { sessionId, status } = await LKP.verify(url, { mode: "modal" });
if (status === "verified") await fetch("/api/verify/complete", { method: "POST" });
};
</script>The user unlocks their LKP wallet with a passkey (or creates one in seconds), and their browser generates a zero-knowledge proof in ~40ms. First-time users take a quick 1-2s selfie or scan an ID document right there.
4. Confirm the result on your server#
The browser's status is a hint for your UI. Before granting anything, fetch the session with your secret key:
app.post("/api/verify/complete", async (req, res) => {
const session = await fetch(
`${process.env.LKP_API_URL}/v1/verification-sessions/${req.session.lkpSessionId}`,
{ headers: { Authorization: `Bearer ${process.env.LKP_SECRET_KEY}` } },
).then((r) => r.json());
if (session.status !== "verified") return res.status(403).json({ error: "Not verified" });
// Stable, anonymous, app-specific user ID
req.session.verifiedUser = session.result.nullifier;
res.json({ ok: true });
});Next steps#
- Add webhooks so your backend hears about verifications without polling.
- Use
mode: "ONE_TIME_ACTION"for votes, giveaways and anything a person should only do once. See the API reference. - Watch conversion and unique users on your app's overview in the console.