← All field notes

SD-JWT vs mdoc: picking a format

A practical comparison for teams deciding between the two dominant credential formats today.

{ }sd-jwtvs

If you're issuing credentials in 2026, two formats dominate the conversation: SD-JWT VC, coming out of the IETF and the OpenID4VC ecosystem, and mdoc, the ISO/IEC 18013-5 format behind mobile driving licences. They solve overlapping problems with very different engineering cultures, and picking between them is mostly about who needs to verify your credential.

What they actually are

An SD-JWT is a JSON Web Token where individual claims are replaced by salted hashes. The holder receives the JWT plus the disclosures, and reveals only the disclosures they choose at presentation time. If your team has ever handled a JWT, the mental model transfers directly.

An mdoc is a CBOR-encoded document with a COSE signature, designed for in-person presentation over NFC and BLE as much as online flows. It comes from the ISO world: precise, binary, and built with hardware wallets and government issuers in mind.

Where each one wins

  • Web-first flows (login, KYC reuse, B2B attestations): SD-JWT. It rides on OpenID4VP and existing JOSE tooling.
  • In-person and regulated identity (driving licences, national ID, age checks at a counter): mdoc. Reader hardware and the EU wallet reference implementation both speak it natively.
  • Selective disclosure: both support it, but the granularity model differs — SD-JWT discloses per claim, mdoc per namespace element.
  • Payload size: mdoc's CBOR encoding is tighter; SD-JWTs with many disclosures get long.

The honest answer: you'll need both

Most real deployments end up issuing the same underlying claims in more than one format, because verifier ecosystems don't converge — the airline's app wants an mdoc, the SaaS vendor's login wants an SD-JWT. This is exactly why Inflowra takes the same request shape for every format: you change one field, not your integration.

Same claims, either format
POST /v1/credentials
{
  "format": "vc+sd-jwt",   // or "mso_mdoc"
  "type": "EmployeeCredential",
  "holder": "did:key:z6Mkf...",
  "claims": {
    "role": "Backend Engineer",
    "clearance": "L2"
  }
}

Our advice: pick the format your first verifier already accepts, ship, and treat the second format as a config change rather than a project. That's the point of not marrying your data model to a wire format.