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
{
"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"
}| Field | Meaning |
|---|---|
sessionId | Identifier for this verification session, for lookups and support. |
identityKey | Pseudonymous, stable-per-account identifier for the verified person. Your primary key for them. |
claims | The verified attributes released to your account. |
availableClaims | Claims the provider could return for your account — what you could request, not what was sent. |
assuranceLevel | Strength of the authentication that produced this result, as reported by the provider. |
livenessEvidence | Liveness signal where the provider supplies one; may be absent. |
provider | Which identity provider verified this — currently FAYDA. |
source | The specific service within that provider. |
verifiedAt | When 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.
// 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)
);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:
| Claim | Example | Notes |
|---|---|---|
name | Yigermal Fantaye | Full name as held in the national register. |
birthdate | 1996-04-11 | ISO-8601 date. Use it to derive age; do not store an age. |
gender | male | As recorded by the provider. |
address | — | Requires an expanded entitlement. |
phone | — | Requires an expanded entitlement. |
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.
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.
// 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.