Skip to main content
Version: v0.13

DCQL (Digital Credentials Query Language)

DCQL (Digital Credentials Query Language) is the query language for specifying credential requirements in OID4VP 1.0. It provides a structured way for verifiers to express what credentials and claims they need from a holder's wallet.

Overview

DCQL enables verifiers to precisely describe their credential requirements:

  • Which credential types and formats are needed
  • What specific claims must be disclosed
  • Alternative credentials that can satisfy a requirement
  • Trusted authorities for issuer validation

Query Structure

A DCQL query has a hierarchical structure:

DCQL Query Structure

Basic Query

import com.sphereon.openid.oid4vp.dcql.*

// Create a DCQL query for an mDL credential
val dcqlQuery = DcqlQuery(
credentials = listOf(
DcqlCredentialQuery(
id = "mDL",
format = "mso_mdoc",
meta = buildJsonObject {
put("doctype_value", "org.iso.18013.5.1.mDL")
},
claims = listOf(
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "family_name")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "given_name")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "birth_date")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "portrait"))
)
)
)
)

JSON Representation

The above query serializes to:

{
"credentials": [
{
"id": "mDL",
"format": "mso_mdoc",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL"
},
"claims": [
{ "path": ["org.iso.18013.5.1", "family_name"] },
{ "path": ["org.iso.18013.5.1", "given_name"] },
{ "path": ["org.iso.18013.5.1", "birth_date"] },
{ "path": ["org.iso.18013.5.1", "portrait"] }
]
}
]
}

Credential Formats

DCQL supports multiple credential formats:

FormatDescriptionMeta Fields
mso_mdocISO 18013-5 mobile documentsdoctype_value, namespace_values
dc+sd-jwtSD-JWT Verifiable Credentialsvct_values, sd_jwt_alg_values, kb_jwt_alg_values
jwt_vc_jsonJWT-encoded W3C VCtype_values, alg_values
ldp_vcJSON-LD W3C VCtype_values, proof_type_values

mDoc Credentials

For ISO 18013-5 mDoc credentials, specify the document type and namespaced claims:

val mdocQuery = DcqlCredentialQuery(
id = "driving_license",
format = "mso_mdoc",
meta = buildJsonObject {
put("doctype_value", "org.iso.18013.5.1.mDL")
},
claims = listOf(
// Claims use namespace as first path element
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "family_name")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "given_name")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "birth_date")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "issue_date")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "expiry_date")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "issuing_country")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "document_number")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "portrait")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "driving_privileges")),

// Age attestation claims
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "age_over_18")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "age_over_21")),

// Domestic namespace (country-specific)
DcqlClaimQuery(path = listOf("org.iso.18013.5.1.US", "domestic_category"))
)
)

SD-JWT Credentials

For SD-JWT Verifiable Credentials, specify the credential type and claim paths:

val sdJwtQuery = DcqlCredentialQuery(
id = "identity_credential",
format = "dc+sd-jwt",
meta = buildJsonObject {
putJsonArray("vct_values") {
add("https://credentials.example.com/identity")
}
},
claims = listOf(
// Top-level claims
DcqlClaimQuery(path = listOf("family_name")),
DcqlClaimQuery(path = listOf("given_name")),
DcqlClaimQuery(path = listOf("birth_date")),
DcqlClaimQuery(path = listOf("nationalities")),

// Nested claims using path
DcqlClaimQuery(path = listOf("address", "street_address")),
DcqlClaimQuery(path = listOf("address", "locality")),
DcqlClaimQuery(path = listOf("address", "country"))
)
)

Claim Configuration

Value Constraints

Request claims with specific expected values:

val claims = listOf(
// Claim must have this exact value
DcqlClaimQuery(
path = listOf("org.iso.18013.5.1", "issuing_country"),
values = listOf(JsonPrimitive("US"))
),

// Claim must be one of these values
DcqlClaimQuery(
path = listOf("org.iso.18013.5.1", "vehicle_category_code"),
values = listOf(JsonPrimitive("A"), JsonPrimitive("B"), JsonPrimitive("C"))
),

// Boolean value constraint
DcqlClaimQuery(
path = listOf("org.iso.18013.5.1", "age_over_21"),
values = listOf(JsonPrimitive(true))
)
)

Intent to Retain

Indicate whether the verifier intends to store the claim:

val claims = listOf(
// Verifier will store this claim
DcqlClaimQuery(
path = listOf("email"),
intent_to_retain = true
),

// Verifier will only use for verification, not store
DcqlClaimQuery(
path = listOf("age_over_21"),
intent_to_retain = false
)
)

Multiple Credentials

Request multiple credentials in a single query:

val dcqlQuery = DcqlQuery(
credentials = listOf(
// Request mDL for identity verification
DcqlCredentialQuery(
id = "identity",
format = "mso_mdoc",
meta = buildJsonObject {
put("doctype_value", "org.iso.18013.5.1.mDL")
},
claims = listOf(
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "family_name")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "given_name")),
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "portrait"))
)
),

// Request proof of address
DcqlCredentialQuery(
id = "address_proof",
format = "dc+sd-jwt",
meta = buildJsonObject {
putJsonArray("vct_values") { add("AddressCredential") }
},
claims = listOf(
DcqlClaimQuery(path = listOf("street_address")),
DcqlClaimQuery(path = listOf("locality")),
DcqlClaimQuery(path = listOf("postal_code")),
DcqlClaimQuery(path = listOf("country"))
)
),

// Request employment verification
DcqlCredentialQuery(
id = "employment",
format = "dc+sd-jwt",
meta = buildJsonObject {
putJsonArray("vct_values") { add("EmploymentCredential") }
},
claims = listOf(
DcqlClaimQuery(path = listOf("employer_name")),
DcqlClaimQuery(path = listOf("job_title")),
DcqlClaimQuery(path = listOf("employment_status"))
)
)
)
)

Credential Sets (Alternatives)

When multiple credential types can satisfy the same requirement, use credential sets to define alternatives:

val dcqlQuery = DcqlQuery(
credentials = listOf(
DcqlCredentialQuery(
id = "mdl_age",
format = "mso_mdoc",
meta = buildJsonObject {
put("doctype_value", "org.iso.18013.5.1.mDL")
},
claims = listOf(
DcqlClaimQuery(path = listOf("org.iso.18013.5.1", "age_over_21"))
)
),
DcqlCredentialQuery(
id = "age_attestation",
format = "dc+sd-jwt",
meta = buildJsonObject {
putJsonArray("vct_values") { add("AgeAttestation") }
},
claims = listOf(
DcqlClaimQuery(path = listOf("age_over_21"))
)
),
DcqlCredentialQuery(
id = "national_id_age",
format = "mso_mdoc",
meta = buildJsonObject {
put("doctype_value", "org.iso.23220.1.nid")
},
claims = listOf(
DcqlClaimQuery(path = listOf("org.iso.23220.1", "age_over_21"))
)
)
),
// Group them as alternatives - holder provides any ONE
credential_sets = listOf(
DcqlCredentialSetQuery(
required = true,
options = listOf(
DcqlCredentialSetOption(credential_ids = listOf("mdl_age")),
DcqlCredentialSetOption(credential_ids = listOf("age_attestation")),
DcqlCredentialSetOption(credential_ids = listOf("national_id_age"))
)
)
)
)

Combined Credentials Per Option

An option can require multiple credentials together:

// Either present diploma OR (university_id AND transcript together)
val credentialSets = listOf(
DcqlCredentialSetQuery(
required = true,
options = listOf(
// Option 1: Just a diploma
DcqlCredentialSetOption(credential_ids = listOf("diploma")),
// Option 2: University ID AND transcript together
DcqlCredentialSetOption(credential_ids = listOf("university_id", "transcript"))
)
)
)

Trusted Authorities

Constrain which issuers are acceptable:

val credentialQuery = DcqlCredentialQuery(
id = "identity",
format = "dc+sd-jwt",
trusted_authorities = listOf(
// OpenID Federation trust anchor
DcqlTrustedAuthority(
type = DcqlTrustedAuthority.TYPE_OPENID_FEDERATION,
values = listOf("https://federation.example.com")
),
// ETSI Trusted List
DcqlTrustedAuthority(
type = DcqlTrustedAuthority.TYPE_ETSI_TRUSTED_LIST,
values = listOf("https://eidas.europa.eu/TL/EN_TL.xml")
),
// X.509 Authority Key Identifier
DcqlTrustedAuthority(
type = DcqlTrustedAuthority.TYPE_AUTHORITY_KEY_IDENTIFIER,
values = listOf("0a1b2c3d4e5f...")
)
),
claims = listOf(
DcqlClaimQuery(path = listOf("family_name"))
)
)

Holder Binding

Require cryptographic proof that the presenter controls the credential:

val credentialQuery = DcqlCredentialQuery(
id = "identity",
format = "dc+sd-jwt",
require_cryptographic_holder_binding = true, // Default is true
claims = listOf(
DcqlClaimQuery(path = listOf("family_name"))
)
)

Scope-to-DCQL Mapping

The IDK supports mapping OAuth scopes to DCQL queries for simplified request flows:

import com.sphereon.openid.oid4vp.common.ScopeRegistry
import com.sphereon.openid.oid4vp.common.ScopeDefinition
import com.sphereon.openid.oid4vp.common.ScopeResolver

// Define scope mappings
val registry = ScopeRegistry(
definitions = listOf(
ScopeDefinition(
scopeValue = "com.example.identity",
description = "Basic identity information",
dcqlQuery = DcqlQuery(
credentials = listOf(
DcqlCredentialQuery(
id = "identity",
format = "dc+sd-jwt",
claims = listOf(
DcqlClaimQuery(path = listOf("given_name")),
DcqlClaimQuery(path = listOf("family_name"))
)
)
)
)
),
ScopeDefinition(
scopeValue = "com.example.age_verification",
description = "Age verification",
dcqlQuery = DcqlQuery(
credentials = listOf(
DcqlCredentialQuery(
id = "age",
format = "dc+sd-jwt",
claims = listOf(
DcqlClaimQuery(path = listOf("age_over_18"))
)
)
)
)
)
)
)

// Resolve scopes to DCQL
val resolver = ScopeResolver(registry)
val result = resolver.resolve("com.example.identity com.example.age_verification")

if (result.fullyResolved) {
// Use merged DCQL query
val dcqlQuery = result.dcqlQuery
println("Resolved scopes: ${result.resolvedScopes}")
} else {
println("Unresolved scopes: ${result.unresolvedScopes}")
}

Using DCQL in Authorization Requests

Pass the DCQL query when creating an OID4VP authorization request:

val rp = session.component.oid4vpRpService

val createResult = rp.createAuthorizationRequest(
CreateAuthorizationRequestArgs(
dcqlQuery = dcqlQuery,
clientId = "https://verifier.example.com",
responseUri = "https://verifier.example.com/callback",
responseMode = ResponseMode.DIRECT_POST,
nonce = generateSecureNonce()
)
)

when (createResult) {
is IdkResult.Success -> {
val request = createResult.value.request
// Build URI for QR code or redirect
}
is IdkResult.Failure -> {
handleError(createResult.error)
}
}

Data Types Reference

DcqlQuery

data class DcqlQuery(
val credentials: List<DcqlCredentialQuery>? = null,
val credential_sets: List<DcqlCredentialSetQuery>? = null
)
// At least one of credentials or credential_sets must be present

DcqlCredentialQuery

data class DcqlCredentialQuery(
val id: String,
val format: String? = null,
val meta: JsonObject? = null,
val claims: List<DcqlClaimQuery>? = null,
val claim_sets: List<DcqlClaimSet>? = null,
val require_cryptographic_holder_binding: Boolean = true,
val multiple: Boolean = false,
val trusted_authorities: List<DcqlTrustedAuthority>? = null
)

DcqlClaimQuery

data class DcqlClaimQuery(
val path: List<String>,
val values: List<JsonElement>? = null,
val intent_to_retain: Boolean? = null
)

DcqlCredentialSetQuery

data class DcqlCredentialSetQuery(
val required: Boolean = false,
val options: List<DcqlCredentialSetOption>
)

data class DcqlCredentialSetOption(
val credential_ids: List<String>
)

DcqlTrustedAuthority

data class DcqlTrustedAuthority(
val type: String,
val values: List<String>
) {
companion object {
const val TYPE_AUTHORITY_KEY_IDENTIFIER = "authority_key_identifier"
const val TYPE_ETSI_TRUSTED_LIST = "etsi_trusted_list"
const val TYPE_OPENID_FEDERATION = "openid_federation"
}
}

Best Practices

Request only necessary claims. Minimize data collection by requesting only the claims you actually need. This respects user privacy and simplifies consent.

Use meaningful credential IDs. Choose descriptive IDs that help with debugging and logging, such as identity_verification rather than cred1.

Use intent_to_retain appropriately. Be transparent about whether claims will be stored, helping users make informed consent decisions.

Provide alternatives with credential_sets. When multiple credential types can satisfy a requirement, define alternatives to maximize compatibility with different wallets.

Validate queries. Use the DCQL validation utilities to catch errors before sending requests to wallets.