Docs
Verification

DC API (Browser Verification)

Verify credentials directly in the browser using the Digital Credentials API

The Digital Credentials (DC) API lets you verify credentials directly in the browser without QR codes or deep links. The browser handles the wallet interaction natively, similar to how WebAuthn works for authentication.

DC API supports four request protocols:

ProtocolValueUse case
OpenID4VP (unsigned)openid4vpDCQL queries for mDoc and SD-JWT credentials
OpenID4VP (HAIP, signed)haipSigned OpenID4VP request per HAIP
ISO mDociso-mdocDirect mdoc presentation (ISO 18013-7 Annex C, HPKE-encrypted)
ISO mDoc (signed)iso-mdoc-signedSame as iso-mdoc with reader authentication

These values are what you send in the request body. The request.protocol string returned by the API (and passed to the browser) differs: openid4vp returns openid4vp-v1-unsigned, haip returns openid4vp-v1-signed, and both iso-mdoc variants return org-iso-mdoc.

Session flow

  1. Your backend creates a DC API session with the desired protocol and query.
  2. Your frontend calls navigator.credentials.get() with the session data.
  3. The browser prompts the user to select a credential from their wallet.
  4. Your frontend submits the wallet response to your backend for verification.

1. Create a session

POST /verifier/dc-api/request

Authentication: API key (publishable or secret)

Request body (OpenID4VP)

{
  "protocol": "openid4vp",
  "origin": "https://your-site.example.com",
  "dcql_query": {
    "credentials": [{
      "id": "pid",
      "format": "mso_mdoc",
      "meta": { "doctype_value": "eu.europa.ec.eudi.pid.1" },
      "claims": [
        { "path": ["eu.europa.ec.eudi.pid.1", "given_name"] },
        { "path": ["eu.europa.ec.eudi.pid.1", "family_name"] }
      ]
    }]
  }
}

Request body (ISO mDoc)

{
  "protocol": "iso-mdoc",
  "origin": "https://your-site.example.com",
  "namespace": "eu.europa.ec.eudi.pid.1",
  "attributes": ["given_name", "family_name", "birth_date"]
}
FieldTypeRequiredDescription
protocol"openid4vp" | "haip" | "iso-mdoc" | "iso-mdoc-signed"YesWhich DC API protocol to use
originstring (URL)YesOrigin of the page that will call navigator.credentials.get()
dcql_queryobjectNoDCQL query
namespacestringNomDoc namespace for simple queries
attributesstring[]NoAttributes to request (with namespace)
claimsarrayNoPer-attribute namespace claims: { namespace, id, intent_to_retain }
skip_trusted_authoritiesbooleanNoSkip trusted authority enrichment

Response 200

{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "request": {
    "protocol": "openid4vp-v1-unsigned",
    "data": { ... }
  }
}

The nested request object (request.protocol and request.data) is what you pass to navigator.credentials.get().


2. Call the browser API

Use the session response to invoke the Digital Credentials API:

async function requestCredential(session: {
  sessionId: string
  request: { protocol: string; data: object }
}) {
  // Check browser support
  if (!('digital' in navigator.credentials)) {
    throw new Error('Digital Credentials API not supported in this browser')
  }

  const credential = await navigator.credentials.get({
    digital: {
      requests: [{
        protocol: session.request.protocol,
        data: session.request.data
      }]
    }
  })

  return credential
}

The browser displays a native credential picker. The user selects which credential (and which claims) to share.


3. Submit the response

POST /verifier/dc-api/response

Send the wallet response to your backend for cryptographic verification.

Authentication: API key (publishable or secret)

Request body

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "response": { ... }
}
FieldTypeRequiredDescription
session_idstring (UUID)YesThe sessionId from the create-session response (note the snake_case)
responseobjectNoThe credential response from navigator.credentials.get() (may be omitted on cancel)

Response 200

{
  "status": "valid",
  "verification": {
    "ISSUER_AUTH": [
      { "status": "✅", "check": "Issuer certificate must be valid" }
    ],
    "DEVICE_AUTH": [
      { "status": "✅", "check": "Device signature must be valid" }
    ]
  },
  "presentation": {
    "eu.europa.ec.eudi.pid.1": {
      "given_name": "Marie",
      "family_name": "Dupont"
    }
  }
}

4. Poll for status (alternative)

If you prefer a polling approach instead of submitting the response directly:

GET /verifier/dc-api/status/:session_id

Authentication: API key (publishable or secret)

Response 200

{
  "status": "valid",
  "presentation": { ... }
}
StatusMeaning
pendingSession exists, waiting for wallet response
validCredential verified successfully
failedVerification failed
expiredSession is gone (no response within the 300 second session TTL, or the result's 60 second retention elapsed)

Full integration example

<button id="verify-btn">Verify Identity</button>

<script type="module">
  const API_BASE = 'https://api.verifiables.com'
  const API_KEY = 'oat_your_publishable_key'

  document.getElementById('verify-btn').addEventListener('click', async () => {
    // 1. Create session
    const sessionRes = await fetch(`${API_BASE}/verifier/dc-api/request`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        protocol: 'openid4vp',
        origin: window.location.origin,
        dcql_query: {
          credentials: [{
            id: 'pid',
            format: 'mso_mdoc',
            meta: { doctype_value: 'eu.europa.ec.eudi.pid.1' },
            claims: [
              { path: ['eu.europa.ec.eudi.pid.1', 'given_name'] },
              { path: ['eu.europa.ec.eudi.pid.1', 'family_name'] }
            ]
          }]
        }
      })
    })
    const session = await sessionRes.json()

    // 2. Invoke browser API
    const credential = await navigator.credentials.get({
      digital: {
        requests: [{
          protocol: session.request.protocol,
          data: session.request.data
        }]
      }
    })

    // 3. Submit for verification
    const verifyRes = await fetch(`${API_BASE}/verifier/dc-api/response`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        session_id: session.sessionId,
        response: credential
      })
    })
    const result = await verifyRes.json()

    console.log('Verified:', result.presentation)
  })
</script>

Browser support

The Digital Credentials API is available in Chrome 128+ on Android and desktop (behind a flag in earlier versions). Safari and Firefox support is in development. For browsers without DC API support, fall back to QR code verification.