Get started

Docs · Concepts

Templates and DCQL

A template names what you are asking a wallet for. Two ship built in; anything else is a DCQL query you write once and reuse by name.

How a template resolves

template in POST /v1/verifications resolves in this order:

  1. Built-ins — age_over_18 and basic_identity. Built-ins always win: a tenant can never shadow age_over_18 with a laxer query of its own, so the reserved keys mean one thing platform-wide.
  2. Your own templates — created on the dashboard's Templates page. The key must match ^[a-z0-9_]{1,64}$.
  3. Neither matches → 422 unknown_template.

You can also skip templates entirely and send a query inline on the create call. Exactly one of template and query is required.

Built-in templates

KeyAsks for
age_over_18The age_over_18 attribute of the age-verification attestation eu.europa.ec.av.1, as an mdoc.
basic_identityGiven name, family name and date of birth from the PID — requested in both mdoc and SD-JWT VC form, so a wallet holding either can satisfy it.
age_over_18, as DCQL
{
  "credentials": [
    {
      "id": "age_over_18_mdoc",
      "format": "mso_mdoc",
      "meta": { "doctype_value": "eu.europa.ec.av.1" },
      "claims": [
        { "path": ["eu.europa.ec.av.1", "age_over_18"], "intent_to_retain": false }
      ]
    }
  ]
}

Writing your own query

A DCQL query lists the credentials you will accept and the claims you want from each. credential_sets expresses alternatives — the query below is satisfied by either the mdoc PID or the SD-JWT VC PID, which is how basic_identity works:

basic_identity, as DCQL
{
  "credentials": [
    {
      "id": "pid_mdoc",
      "format": "mso_mdoc",
      "meta": { "doctype_value": "eu.europa.ec.eudi.pid.1" },
      "claims": [
        { "path": ["eu.europa.ec.eudi.pid.1", "given_name"], "intent_to_retain": false },
        { "path": ["eu.europa.ec.eudi.pid.1", "family_name"], "intent_to_retain": false },
        { "path": ["eu.europa.ec.eudi.pid.1", "birth_date"], "intent_to_retain": false }
      ]
    },
    {
      "id": "pid_sdjwt",
      "format": "dc+sd-jwt",
      "meta": { "vct_values": ["urn:eudi:pid:1"] },
      "claims": [
        { "path": ["given_name"] },
        { "path": ["family_name"] },
        { "path": ["birthdate"] }
      ]
    }
  ],
  "credential_sets": [{ "options": [["pid_mdoc"], ["pid_sdjwt"]] }]
}

The query is validated when you save the template and again by the verification engine when a session is created, so an invalid query cannot reach a wallet however it got into the database.