Protocols

W3C Digital Credentials API (DC API)

The W3C Digital Credentials API lets a web page ask the browser for a credential through navigator.credentials.get, and the operating system then shows its own picker so the person can choose a wallet and approve. It replaces the redirect to a custom URL scheme, and the EU age verification profile names it as the preferred way to present a proof of age.

Also called: DC API, Digital Credentials API, navigator.credentials.get, digital credentials

At a glance

Called from
navigator.credentials.get, in the page
Browser support
Chrome 141 by default, Safari 26 on macOS, iOS and iPadOS, Firefox behind a flag
In the AV profile
The preferred transport, with OpenID4VP as the fallback
Specified by
ISO/IEC 18013-7 Annex C, not OpenID4VP's own DC API appendix
Response protection
HPKE, per RFC 9180
Session binding
[null, null, ["dcapi", SHA-256 of the encryption info and the origin]]

What it replaces, and why that is better

Without it, a verifier hands over a link. The browser leaves your page, an app opens, the person approves, and something has to bring them back. That round trip is where checks are abandoned, it behaves differently on every platform, and any app that registered the same URL scheme can intercept the link.

With it, the page stays put. Your site calls navigator.credentials.get with the request, the browser and the operating system take over the picker, and the response comes back as a value in the same JavaScript call. The person never leaves, and the browser tells the wallet which website is asking, which closes off a class of impersonation that a custom scheme cannot.

Support arrived quickly. It is on by default in Chrome from version 141, it works in Safari from version 26 on macOS, iOS and iPadOS, and Firefox has it behind a preference. That matters for anyone planning around it: the population of browsers that need the older redirect is shrinking rather than growing.

The version confusion that costs people a week

There are two different things called "DC API" in this space and they are not variants of each other. OpenID4VP defines its own way of travelling over the Digital Credentials API, with an authorization request, a DCQL query and an origin based client identifier. The EU age verification profile means something else: the mechanism specified in ISO/IEC 18013-7 Annex C, which has no authorization request and no DCQL at all. It sends a CBOR encoded device request and an encryption key, and gets back a device response encrypted with HPKE.

Reading one and implementing the other produces code that looks right, matches the examples you were reading, and cannot talk to the app. The question to settle before writing anything is which of the two the profile you are following actually cites.

The construction the public documents leave out

An mdoc presentation is bound to its session by a session transcript, which is what stops a response captured in one exchange being replayed into another. Annex A prints the transcript for its Digital Credentials API example, and its shape is a three element array ending in the pair "dcapi" and a 32 byte hash. What Annex A does not say anywhere is what that hash is taken over, because that part lives in ISO/IEC 18013-7, which is a paid standard.

It can be recovered without buying it. Multipaz, the Apache-2.0 implementation the Commission's own reference DC API backend is built on, computes the preimage as a two element array of the base64url encoded encryption info and the origin, hashed with SHA-256. Running Annex A's own published example through that construction, with the origin of the Commission's hosted reference verifier, reproduces the hash Annex A prints, byte for byte.

That is worth spelling out because it is the difference between an assumption and a fact. Two independent sources agree, the profile's own test vector confirms it, and the paid standard is therefore not on the critical path for building this. We publish it because the next person will otherwise spend the same afternoon on it that we did.

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: settle which "DC API" your profile cites before you write a line. The two mechanisms share a name, share nothing else, and the examples for the wrong one will look convincing all the way to the point where the app refuses you.

If you're choosing a vendor: ask which transports they implement. Supporting only the fallback works everywhere and gives a worse experience on browsers that have the newer API, and that gap widens as support spreads.

Frequently asked

Is the Digital Credentials API required for EU age verification?

The normative profile does not require it. Annex A lists only the OpenID4VP presentation flow in scope, states that implementations need not implement every flow, and attaches no requirements at all to the Digital Credentials API section. The developer guide for verifiers takes the other view and says a verifier should support every combination. They disagree, so it is worth being explicit about which you are quoting.

Does it work across devices?

Yes, that is part of the design. The browser can offer a wallet on a nearby phone as well as one on the same device, so a desktop visitor is not shut out.

Does Tessio support it?

Not yet. We verify over OpenID4VP, which the profile designates as the fallback and which works with every browser. The Digital Credentials API is the better experience where it exists, and it is on our list rather than in the product.

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. Signup is open and a test project works as soon as you create one. Tell us what you're building.