Claims & identity keys

What a verified identity actually contains, why there is no national identifier in it, and how to use the identity key.

The VerifiedIdentity object

A completed verification
{
  "sessionId": 4711,
  "identityKey": "3f9c1a0b7e5d4c2a9b8e7f6d5c4b3a291f0e8d7c6b5a4938271605f4e3d2c1b0",
  "claims": {
    "name": "Yigermal Fantaye",
    "birthdate": "1996-04-11",
    "gender": "male"
  },
  "availableClaims": ["name", "birthdate", "gender", "address", "phone"],
  "assuranceLevel": "L2",
  "livenessEvidence": "PRESENTED",
  "provider": "FAYDA",
  "source": "esignet",
  "verifiedAt": "2026-07-26T09:14:22Z"
}
FieldMeaning
sessionIdIdentifier for this verification session, for lookups and support.
identityKeyPseudonymous, stable-per-account identifier for the verified person. Your primary key for them.
claimsThe verified attributes released to your account.
availableClaimsClaims the provider could return for your account — what you could request, not what was sent.
assuranceLevelStrength of the authentication that produced this result, as reported by the provider.
livenessEvidenceLiveness signal where the provider supplies one; may be absent.
providerWhich identity provider verified this — currently FAYDA.
sourceThe specific service within that provider.
verifiedAtWhen the verification completed, ISO-8601 UTC. Your audit timestamp.

The identity key

Emnet does not return the person’s national identifier. Instead you get an identityKey: a value derived for your account alone, with three properties that make it more useful to you than a raw identifier would be, and far less dangerous to hold.

  • Stable. The same person verifying again next year produces the same key. Use it to recognise returning users and to detect a second signup by someone already registered.
  • Unlinkable. The same person verifying at another Emnet customer produces a completely different key. No two customers can join their records to profile someone.
  • Non-reversible. It cannot be turned back into a national identifier — not by you, not by an attacker who takes your database, and not by us on request.
Using it as your foreign key
// Index this column. It is how you find this person again.
String key = identity.identityKey();

users.findByEmnetIdentityKey(key).ifPresentOrElse(
    existing -> reject("This person already has an account"),
    ()       -> users.linkVerification(currentUserId, key, identity)
);
Treat the identity key as personal data

It is pseudonymous, not anonymous: within your own systems it identifies a specific person. Protect it the way you protect a user ID — access-controlled, not in URLs, not in logs you ship to third parties, and deleted when you delete the user.

Claims you receive

claims is a string-to-string map. Which keys appear depends on two things: what the person consented to release, and what your account is entitled to receive. Common claims:

ClaimExampleNotes
nameYigermal FantayeFull name as held in the national register.
birthdate1996-04-11ISO-8601 date. Use it to derive age; do not store an age.
gendermaleAs recorded by the provider.
address—Requires an expanded entitlement.
phone—Requires an expanded entitlement.
Read claims defensively

Never assume a claim is present. A person may decline to release one, and entitlements differ per account. Check for the key and degrade gracefully rather than throwing on a missing value.

Defensive reads
Map<String, String> claims = identity.claims();

String name = claims.get("name");                       // may be null
if (name == null) {
    return incomplete("name was not released");
}

Optional.ofNullable(claims.get("birthdate"))
        .map(LocalDate::parse)
        .filter(dob -> dob.isBefore(LocalDate.now().minusYears(18)))
        .orElseThrow(() -> new UnderageException());

Entitlements

Claim release is default-deny. Your account is provisioned with a specific set of claims, and anything outside it is refused rather than quietly dropped — so you always know whether you received less than you asked for.

availableClaims tells you what the provider could return for your account, so you can see what an expanded entitlement would give you without that data ever being sent first. Expanding an entitlement is a contractual step, not a settings toggle: write to sales@emnetid.com with what you need and why, and expect a data-processing agreement to be part of it.

Assurance level

assuranceLevel describes how strongly the person was authenticated. Treat it as a signal your own policy interprets — a higher level might let a user skip a manual review, a lower one might route them to it.

Your policy, your code
// Emnet reports the assurance level. What it entitles a user to is your decision.
boolean strong = "L2".equals(identity.assuranceLevel()) || "L3".equals(identity.assuranceLevel());
accounts.setReviewRequired(userId, !strong);

Why there is no decision in the response

Emnet deliberately returns no KYC tier, approval, risk score or limit. Three reasons:

  • Your rules are not ours. The same verification means different things to a bank, a betting platform and a remittance company. Any tier we invented would be wrong for most of them.
  • Your regulator audits your systems. Compliance decisions should be recorded where your auditors can see them, with your reasoning attached — not in a vendor’s database.
  • Policies change more often than facts. A birth date verified in 2026 is still verified in 2030. Your age policy might change three times in between. Storing the fact and applying the policy at read time keeps those independent.

Next: the API reference.