Credentials

EU age verification attestation (eu.europa.ec.av.1)

The EU age verification attestation is a credential, doctype eu.europa.ec.av.1, that carries nothing but a set of age_over_N booleans. No name, no date of birth, no document number, no portrait. It exists so a service can check someone is old enough without learning anything else about them.

Also called: age attestation, AV attestation, eu.europa.ec.av.1, age_over_18 credential

At a glance

Doctype
eu.europa.ec.av.1
Format
mdoc (ISO 18013-5), as issued today
Contains
Thirteen age_over_N booleans
Does not contain
Name, date of birth, document number, portrait
Thresholds
13, 15, 16, 18, 21, 23, 25, 27, 28, 40, 60, 65, 67
Requested with
DCQL, over OpenID4VP 1.0

What is actually in it

Thirteen booleans, and that is the entire payload: age_over_13, 15, 16, 18, 21, 23, 25, 27, 28, 40, 60, 65 and 67. Those numbers are not arbitrary and they are not ours. They are the thresholds the attestation defines, covering the ages that laws around Europe actually draw lines at, from social media minimums through alcohol and gambling to pensioner concessions.

Compare that with the PID, the person identification data credential, which is the wallet's identity document and carries a name, a birth date and more. You can work out whether someone is over 18 from a PID, but doing it means receiving their date of birth. The age attestation exists precisely so you do not have to, and as issued today it is the only EU credential that carries age_over_N without also carrying identity attributes.

Why this is a stronger privacy claim than a retention promise

Most age verification vendors offer some version of "we delete it afterwards". That is a promise about behaviour, and the only way to audit it is to trust them. This is different in kind. The credential does not contain the data, so there is nothing to delete, nothing to breach, and nothing to produce when someone files a subject access request. You cannot leak what the protocol never sends you.

And because the credential has this property and the vendor does not need to promise it, you do not have to take anyone's word for it. The doctype is public, the contents are specified, and you can go and look.

The part that trips people up

Asking for a claim is not the same as asking for a value. DCQL, the query language OpenID4VP uses, lets you request the age_over_18 attribute. It does not let you require that the attribute be true. A wallet holding age_over_18 = false can satisfy that request perfectly well, and the response it sends back will verify: the signature is good, the disclosure is genuine, the credential is authentic. The answer is simply no.

So a verifier that treats "verified successfully" as "over 18" is wrong, and it is wrong in the direction that fails open, which is the worst way for an age check to be wrong. You have to read the value that was actually disclosed. We know because we made this mistake in our own implementation and only caught it against a real wallet.

There is a third outcome worth designing for as well. If a credential verifies but did not disclose the boolean you asked for, you do not have a yes and you do not have a no. You have a check that cannot be answered. Folding that into "no" is tempting and it is also wrong: it is a different thing, and it deserves its own state in your data model and your retry logic.

Check one yourself

Reading about it only gets you so far. These are free validators in Tessio Labs, no signup, and nothing you paste is stored.

Why it matters

If you're writing the integration: read the disclosed boolean. Verification succeeding is a different fact, and give the unanswerable case its own state next to yes and no. Those two decisions are the difference between an age check and an age check that quietly lets everyone through.

If you're choosing a vendor: ask which credential they request. A vendor reading a birth date out of a PID is holding personal data about your users, whatever their retention policy says about it afterwards.

Frequently asked

Does the service learn my date of birth?

Not from this credential, because it doesn't contain one. It holds a set of yes or no answers about age thresholds, and the service receives the one it asked for after you approve it in your wallet.

Can I ask for a threshold other than 18?

Yes, any of the thirteen the attestation defines. In practice, sandboxes and mock wallets tend to mint only age_over_18, so the other thresholds are meaningful against a real wallet rather than in a test environment.

Is this the same as a mobile driving licence?

No. An mDL is a driving licence and carries identity attributes including a name and usually a portrait. Both are mdocs, so they travel over the same protocol, but the age attestation is deliberately empty of everything except age answers.

What about users who do not have a wallet?

Then this credential can't answer for them, and you need another route for that traffic. That's a real gap during the rollout, and no verifier can close it, and any vendor telling you the wallet covers all your users today is describing a future rather than the present.

Primary sources

This is a plain language explanation, not legal advice. Specifications and dates in this area move, so check anything you are going to rely on against the primary sources.

Keep reading

Age verification API

Or skip the theory and build against it

Tessio proves someone is over 18 from their EU Digital Identity Wallet and stores no personal data. Access is invite only while we onboard design partners. Tell us what you're building.