Create a session, show a QR code, and fetch the verified details your customer approves. The same API powers Login with IDfier and age verification.
/business/auth/requestCreate a sessionidfier.{referenceId}.{qrTime}.{qrAuthCode}Show a signed QR code/business/auth/resultFetch the resulthttps://api.idfier.com/api/v3No SDK to install. If your backend can make an HTTPS request and your page can show a QR code, you can integrate IDfier.
A client ID and client secret for your business account. Keep the secret on your server.
Any server that can make HTTPS requests. Both endpoints are called from your backend.
Your login, sign-up or checkout page, where the customer scans the code with the IDfier app.
Authenticate every request with your client ID and secret in the clientid and clientsecret headers.
Call this when the customer starts signing in. You get a referenceId for the session and a qrReferenceSecret to sign the QR code. The session is valid for five minutes.
curl -X POST https://api.idfier.com/api/v3/business/auth/request \
-H "clientid: $IDFIER_CLIENT_ID" \
-H "clientsecret: $IDFIER_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{}'{
"status": 100,
"data": {
"referenceId": "550e8400-e29b-41d4-a716-446655440000",
"qrReferenceSecret": "abc123",
"createdAt": "2026-03-12T10:00:00.000Z"
}
}Build the QR payload from the session and redraw it every ~2 seconds. Each payload is signed with HMAC-SHA256 and expires quickly, so an old screenshot of the code won't work.
On a phone, skip the QR code and open the IDfier app with the idfier://auth deep link instead.
import crypto from "node:crypto";
// Call every ~2 seconds and re-render the QR code
export function qrPayload({ referenceId, qrReferenceSecret, createdAt }) {
const qrTime = String(
Math.floor((Date.now() - new Date(createdAt).getTime()) / 1000)
);
const key = crypto.createHash("sha256").update(qrReferenceSecret).digest();
const qrAuthCode = crypto
.createHmac("sha256", key)
.update(qrTime)
.digest("hex");
return `idfier.${referenceId}.${qrTime}.${qrAuthCode}`;
}Poll until the customer has approved. The user object contains only the fields your business account is set up for, plus a stable id you can store.
curl -X POST https://api.idfier.com/api/v3/business/auth/result \
-H "clientid: $IDFIER_CLIENT_ID" \
-H "clientsecret: $IDFIER_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{"referenceId": "550e8400-e29b-41d4-a716-446655440000"}'{
"status": 100,
"data": {
"user": {
"id": "u_8f3k2…",
"fullname": "Anna Lindqvist",
"email": "anna.lindqvist@outlook.com",
"authLevel": "Gold"
},
"session": {
"referenceId": "550e8400-e29b-41d4-a716-446655440000",
"status": "approved",
"expiresAt": "2026-03-12T10:05:00.000Z",
"createdAt": "2026-03-12T10:00:00.000Z"
}
}
}Once approved, the customer is a verified person. Create or update their account in your system and sign them in as you normally would.
user.id to find or create the account, save the verified details you need, and start your own session or token. IDfier doesn't manage your sessions.Pick the fields you need and a language. Get the scopes to set up on your account, ready-to-copy code for every step, and the response you'll receive.
ageOver18,fullname,emailScopes are set on your business account when we set up your API access, not sent with each request. Every response then contains only these fields plus a stable id. Age limit and range scopes are Coming soon.
IDfierasync function waitForUser(referenceId) {
for (let delay = 2000; ; delay = Math.min(delay * 1.5, 10000)) {
const res = await fetch("https://api.idfier.com/api/v3/business/auth/result", {
method: "POST",
headers: {
clientid: process.env.IDFIER_CLIENT_ID,
clientsecret: process.env.IDFIER_CLIENT_SECRET,
"Content-Type": "application/json",
},
body: JSON.stringify({ referenceId }),
});
const { data } = await res.json();
const { status, expiresAt } = data.session;
if (status === "approved") return data.user;
if (status === "denied" || status === "error") throw new Error(status);
if (new Date(expiresAt) < new Date()) throw new Error("expired");
await new Promise((r) => setTimeout(r, delay));
}
}
const user = await waitForUser(referenceId);
const { id, ageOver18, fullname, email } = user;Replace $IDFIER_CLIENT_ID and $IDFIER_CLIENT_SECRET with your credentials. Keep the secret on your server.
| Status | Meaning | What to do |
|---|---|---|
pending | Waiting for the customer to scan the QR code | Keep polling |
started | The QR code has been scanned | Keep polling |
approved | The customer approved and verified data is available | Sign the customer in |
denied | The customer declined | Stop and offer another way in |
error | Authentication failed | Stop and show an error |
Scopes are configured on your business account. Each scope returns the field with the same name. id is always included.
| Scope | Type | Description |
|---|---|---|
fullname | string | Full name |
displayName | string | Preferred display name |
email | string | Email address |
handle | string | IDfier handle |
personalNo | string | Swedish personal identity number |
dateOfBirth | string | Date of birth |
age | number | Age, calculated from date of birth |
ageOver13 / 16 / 18 / 21 / 65Coming soon | boolean | Yes or no for the age limit, without the birth date |
ageBetween{min}And{max}Coming soon | boolean | Yes or no for an inclusive age range, e.g. ageBetween18And25, without the birth date |
country | string | Country |
nationality | string | Nationality from the ID document |
gender | string | Gender from the ID document or personal number |
documentType | string | Type of ID document used |
authLevel | string | Verification level: Bronze, Silver or Gold |
authMethodId | number | Verification method (1 = BankID, 2 = document scan) |
| HTTP | Meaning | Example body |
|---|---|---|
400 | Bad request, for example a missing referenceId | { "error": "Missing referenceId" } |
401 | Invalid client ID or client secret | { "success": false, "message": "Unauthorized: …" } |
404 | Session or business client not found | { "error": "Session not found" } |
500 | Server error; retry with backoff | { "error": "Database error" } |
Old payloads stop working after about 10 seconds, so screenshots can't be reused.
Start at 2–3 seconds and slow down over time to reduce load.
When expiresAt has passed, create a new session and a new QR code.
After approval, create or update the account in your system and sign the customer in as usual.
Choose the scopes for your use case. The flow above stays the same.
Tell us about your use case and we'll set up your business account and scopes.
Request API access