---
title: "How to issue Mobile Driver’s Licenses (ISO/IEC 18013-5 mDL) via OID4VCI with walt.id"
description: "Developer guide to issue a mobile driving license (mDL) with OID4VCI with walt.id—onboard IACA/DS, sign mdoc, and share a credential offer."
stack: "Community Stack (open source) — version 0.21.0"
stack_version: "0.21.0"
stack_comparison: https://docs.walt.id/community-vs-enterprise.md
canonical_url: https://docs.walt.id/community-stack/issuer/credential-issuance/mdl-oid4vc
generated: 2026-07-20
---
# How to Issue Mobile Driver’s Licenses (ISO/IEC 18013-5 mDLs) via OID4VCI with walt.id

**TL;DR**

**What you’ll learn:**

- Generate IACA root certificates and keys that anchor trust for mDL issuance.
- Provision document signer certificates with EKU and CRL metadata required by ISO/IEC 18013-5.
- Populate mandatory mDL claims and invoke the walt.id Issuer API’s OID4VCI endpoint to sign the mdoc.
- Construct and distribute credential offer URLs so compliant wallets can request the issued credential.

**Relevant concepts:**

- [Mobile Driver's License (mDL)](https://docs.walt.id/concepts/digital-credentials/mdoc-mdl-iso.md) – ISO/IEC 18013-5/7 standard for mobile driver's licenses.
- [OID4VCI](https://docs.walt.id/concepts/data-exchange-protocols/openid4vci.md) – protocol for issuing verifiable credentials to wallets.

Video: https://youtu.be/aRoBfjJvqEI

This guide provides a comprehensive walkthrough for issuing an mDL credential based on
the [ISO/IEC 18103-5 standard](https://www.iso.org/standard/69084.html) using the walt.id Issuer API.
The issuance process will utilize the
[OID4VCI protocol](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html).

## Setup

See how to access to the issuer API below.

- [Deployed (Testing Only)](https://issuer.demo.walt.id/swagger/index.html) - Use our deployed version for testing.
- [Local](https://docs.walt.id/community-stack/issuer/setup.md) - Run the API in your environment with our open-source setup.

## Preparing for Issuance: Key Components

The mobile driver's license (mDL) ecosystem builds on established PKI principles that employ X.509 digital 
certificates, while introducing domain-specific roles and data models. Its two main components are:

1. **IACA — Issuing Authority Certification Authority**: This is an authority that serves as the root of trust 
   vouching for authorized credential issuers and is represented by a self-signed X.509 digital certificate that is 
   published to verifiers.

2. **DS — Document Signer (Credential Issuer)**: This is the entity that issues and cryptographically signs mDL 
   credential(s). It holds a private key and an X.509 digital certificate issued by an IACA. 
   Typically, DS certificates are short-lived and are rotated periodically.

3. **The mDL Claims**: The mDL is a verifiable credential that contains claims about the subject (holder), such as 
   name, birthdate and driving privileges. The [ISO/IEC 18103-5 standard document](https://www.iso.org/standard/69084.html)
   defines a set of mandatory fields (e.g., `family_name`, `birth_date`) that must be included in every mDL, as well 
   as a set of optional fields (e.g., `age_over_NN` attestations, `height`, `weight`, `sex`).
   
### Example mDL Credential in JSON format:

```json
{
  "family_name": "Doe",
  "given_name": "John",
  "birth_date": "1986-03-22",
  "issue_date": "2019-10-20",
  "expiry_date": "2024-10-20",
  "issuing_country": "AT",
  "issuing_authority": "AT DMV",
  "document_number": "123456789",
  "portrait": [ 141, 12 ],
  "driving_privileges": [
    {
      "vehicle_category_code": "A",
      "issue_date": "2018-08-09",
      "expiry_date": "2024-10-20"
    },
    {
      "vehicle_category_code": "B",
      "issue_date": "2017-02-23",
      "expiry_date": "2024-10-20"
    }
  ],
  "un_distinguishing_sign": "AT"
}
```

**Note**: mDLs are encoded in CBOR, which is a binary format and is shown in JSON representation above for readability 
purposes. Claims values that are encoded as JSON integer arrays, e.g., the `portrait` claim's value, represent byte
arrays.

## Issuing an mDL

In this section, we'll walk through the steps required to successfully issue an mDL by generating signing keys (for 
the IACA & the DS) which we store ourselves. For production
environments, we recommend using an external KMS provider for key management due to the enhanced security.
Learn more about the different types of keys and the storage options [here](https://docs.walt.id/community-stack/issuer/key-management/overview.md).

**Note**: At the moment, you can only use `secp256r1` keys to onboard IACAs and DSs and to issue mDLs.

### Step 1: Onboard an Issuing Authority Certification Authority (IACA)

Every mobile driver’s license ecosystem starts with a trusted root certificate of an IACA. The IACA serves as the
cryptographic authority that issues DS certificates that are deemed trustworthy.

**:wrench: What this step does**

- Create a self-signed X.509 root certificate (CA=true).
- Encode in it identity info (e.g., country, organization) for the IACA.
- Include optional extensions (e.g., IACA alternative names, CRL distribution URIs).

#### IACA Onboarding Request

**Option: CURL**

**Endpoint:** `/onboard/iso-mdl/iacas` | [API Reference](https://issuer.demo.walt.id/swagger/index.html#/Onboarding%20Service/post_onboard_iso_mdl_iacas)

**Example Request**

```bash
curl -X 'POST' \
  'http://0.0.0.0:7002/onboard/iso-mdl/iacas' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "certificateData": {
    "country": "US",
    "commonName": "Example IACA",
    "issuerAlternativeNameConf": {
      "uri": "https://iaca.example.com"
    }
  }
}'
```

**Body**

```json
{
  "certificateData": {
    "country": "US",
    "commonName": "Example IACA",
    "issuerAlternativeNameConf": {
      "uri": "https://iaca.example.com"
    }
  }
}
```

**Body Parameters**

- `certificateData`: _JSON_ - Data that will be encoded in the generated certificate of the IACA.
    - `country`: _String_ - Two-letter ISO 3166-1 alpha-2 country code (e.g., `US`, `DE`).
    - `commonName`: _String_ - Human-readable name of the issuing authority (e.g., `Ministry of Transport`).
      - `issuerAlternativeNameConf`: _JSON_ - Metadata for the IACA; can include uri and/or email.
        - `uri`: _String_ - Uniform resource identifier regarding the IACA's contact information.
        - `email`: _String_ - RFC822 name for contacting the IACA.
---

**Example Response**

The IACA onboarding endpoint returns an object with:

- **`iacaKey`** – generated signing key of the IACA in JWK format.  
- **`certificatePEM`** – PEM-encoded, JSON-stringified self-signed X.509 certificate of the IACA.  
- **`certificateData`** – object containing the data that is encoded in the generated certificate (for convenience – useful for the DS onboarding endpoint below).

```json
{
  "iacaKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "u-UvsghdzpSXv5HmG5ngvm4Dv8yyRYw9fKA6mdp1KWs",
      "crv": "P-256",
      "kid": "R_E_QZ-Ea6etoAdWfUHSjjexRYz447ffnnfIO9kxn_Y",
      "x": "n_b1GmZTSEhioK3z8MGqcb7nxXqyjFaLR-OfKOnspwU",
      "y": "nGRVvuHTtEAZ1HjgdLaLZnYxrkiRV_e4V2Wz0qVWa-M"
    }
  },
  "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIIBtTCCAVqgAwIBAgIUNlgkpoam39UxORhMNRkwuFzD9pQwCgYIKoZIzj0EAwIwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMMDEV4YW1wbGUgSUFDQTAeFw0yNTA1MjgxMjIzMDFaFw00MDA1MjQxMjIzMDFaMCQxCzAJBgNVBAYTAlVTMRUwEwYDVQQDDAxFeGFtcGxlIElBQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASf9vUaZlNISGKgrfPwwapxvufFerKMVotH458o6eynBZxkVb7h07RAGdR44HS2i2Z2Ma5IkVf3uFdls9KlVmvjo2owaDAdBgNVHQ4EFgQUjCMRsfolTeK5Ds6MqOWj5Nx01BQwEgYDVR0TAQH/BAgwBgEB/wIBADAjBgNVHRIEHDAahhhodHRwczovL2lhY2EuZXhhbXBsZS5jb20wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0kAMEYCIQCnUfp3OyxcaPCT34SQ4dTNyNN0qgxKWpWIDeUXkrs7HwIhALFYrMrINeAats4ZWRxZMK6bykb9dcOwkmBCv96MoZVi\n-----END CERTIFICATE-----\n",
  "certificateData": {
    "country": "US",
    "commonName": "Example IACA",
    "issuerAlternativeNameConf": {
      "uri": "https://iaca.example.com"
    }
  }
}
```

**Defaults**:
- A local (`jwk`) secp256r1 key is automatically created.
- Validity defaults to 15 years, unless otherwise specified.

**Note:**

  As we've used the local (`jwk`) key type, it's important to note that we need to save the returned values ourselves
  for future reference. The API doesn't save any information about created keys.

**Note:**

  Remember to save the value of the IACA's PEM-encoded X.509 certificate (`certificatePEM` field in the response) as
  it is required for verification of
  [mDLs](../../../3.verifier/1.api/3.credential-verification/4.mdl-oid4vc.md#example-verification-request).

### Step 2: Onboard a Document Signer

Document signers are the entities responsible for signing mDL credentials. These entities are identified by an X.509
digital certificate that is signed by an IACA and contains, among others, a special purpose extension that marks it
as valid for mDL issuance.

**:wrench: What this step does**

- Issue a DS certificate signed by the input IACA private key.
- Embed an Extended Key Usage (EKU) value marking it as suitable for mDL signing.
- Include CRL distribution info and various other optional fields.

#### DS Onboarding Request

**Option: CURL**

**Endpoint:** `/onboard/iso-mdl/document-signers` | [API Reference](https://issuer.demo.walt.id/swagger/index.html#/Onboarding%20Service/post_onboard_iso_mdl_document_signers)

**Example Request**

```bash
curl -X 'POST' \
  'http://0.0.0.0:7002/onboard/iso-mdl/document-signers' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "iacaSigner": {
    "iacaKey": {
      "type": "jwk",
      "jwk": {
        "kty": "EC",
        "crv": "P-256",
        "kid": "R_E_QZ-Ea6etoAdWfUHSjjexRYz447ffnnfIO9kxn_Y",
        "x": "n_b1GmZTSEhioK3z8MGqcb7nxXqyjFaLR-OfKOnspwU",
        "y": "nGRVvuHTtEAZ1HjgdLaLZnYxrkiRV_e4V2Wz0qVWa-M",
        "d": "u-UvsghdzpSXv5HmG5ngvm4Dv8yyRYw9fKA6mdp1KWs"
      }
    },
    "certificateData": {
      "country": "US",
      "commonName": "Example IACA",
      "notBefore": "2025-05-28T12:23:01Z",
      "notAfter": "2040-05-24T12:23:01Z",
      "issuerAlternativeNameConf": {
        "uri": "https://iaca.example.com"
      }
    }
  },
  "certificateData": {
    "country": "US",
    "commonName": "Example DS",
    "crlDistributionPointUri": "https://iaca.example.com/crl"
  }
}'
```

**Body**

```json
{
  "iacaSigner": {
    "iacaKey": {
      "type": "jwk",
      "jwk": {
        "kty": "EC",
        "crv": "P-256",
        "kid": "R_E_QZ-Ea6etoAdWfUHSjjexRYz447ffnnfIO9kxn_Y",
        "x": "n_b1GmZTSEhioK3z8MGqcb7nxXqyjFaLR-OfKOnspwU",
        "y": "nGRVvuHTtEAZ1HjgdLaLZnYxrkiRV_e4V2Wz0qVWa-M",
        "d": "u-UvsghdzpSXv5HmG5ngvm4Dv8yyRYw9fKA6mdp1KWs"
      }
    },
    "certificateData": {
      "country": "US",
      "commonName": "Example IACA",
      "notBefore": "2025-05-28T12:23:01Z",
      "notAfter": "2040-05-24T12:23:01Z",
      "issuerAlternativeNameConf": {
        "uri": "https://iaca.example.com"
      }
    }
  },
  "certificateData": {
    "country": "US",
    "commonName": "Example DS",
    "crlDistributionPointUri": "https://iaca.example.com/crl"
  }
}
```

**Body Parameters**
- `iacaSigner`: _JSON_ - Object containing necessary data related to the signing IACA.
    - `iacaKey`: _JSON_ - The JSON serialized signing key of the issuing IACA - used as is from the its respective 
      onboarding endpoint.
    - `certificateData`: _JSON_ - The data that is encoded in the IACA's X.509 certificate - used as is from its
      respective onboarding endpoint.
      - `notBefore`: _String_ - ISO 8601 timestamp defining the start of the certificate's validity period (e.g. `2025-05-28T12:23:01Z`). Required.
      - `notAfter`: _String_ - ISO 8601 timestamp defining the end of the certificate's validity period (e.g. `2040-05-24T12:23:01Z`). Required.
- `certificateData`: _JSON_ - Data that will be encoded in the generated certificate of the DS.
    - `country`: _String_ - Two-letter ISO 3166-1 alpha-2 country code (e.g., `US`, `DE`).
    - `commonName`: _String_ - Human-readable name of the issuing authority (e.g., `Ministry of Transport`).
    - `crlDistributionPointUri`: _String_ - URL where the relevant certificate revocation list (CRL) is published.

**Requirements**

- Must provide the IACA's signing key and certificate data (as obtained from the IACA onboarding endpoint).
- Countries and state/province fields must match between IACA and DS.
- CRL URI is *mandatory* for DS onboarding.

---

**Example Response**

The DS onboarding endpoint returns an object with:

- **`documentSignerKey`** – generated signing key of the DS in JWK format.  
- **`certificatePEM`** – PEM-encoded, JSON-stringified X.509 certificate of the DS signed by the IACA signing key in the request.  
- **`certificateData`** – object that contains the data encoded in the generated certificate of the DS (for convenience).

```json
{
  "documentSignerKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "ZSHgIcRvbwV9s224kHUaFqkEPShCAdwXocGl_w3M42Q",
      "crv": "P-256",
      "kid": "pX99OZjL2iNqM7OMkE1r1rYyuAObvPntewcDHdc2bMM",
      "x": "GWKpdL3jPoPJ5wKgSA-jxS2jgp-ZUDE6sIQbeB86vF0",
      "y": "F3xAwH96_xVciV7mFQslU_eRQgP-5pSZiNf8bjMoGfo"
    }
  },
  "certificatePEM": "-----BEGIN CERTIFICATE-----\nMIICCDCCAa2gAwIBAgIUDo8kr194t6sttt6KL3YcnMtcaYYwCgYIKoZIzj0EAwIwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMMDEV4YW1wbGUgSUFDQTAeFw0yNTA1MjkwNzE4MzlaFw0yNjA4MjkwNzE4MzlaMCIxCzAJBgNVBAYTAlVTMRMwEQYDVQQDDApFeGFtcGxlIERTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGWKpdL3jPoPJ5wKgSA+jxS2jgp+ZUDE6sIQbeB86vF0XfEDAf3r/FVyJXuYVCyVT95FCA/7mlJmI1/xuMygZ+qOBvjCBuzAfBgNVHSMEGDAWgBSMIxGx+iVN4rkOzoyo5aPk3HTUFDAdBgNVHQ4EFgQU7S49LSeg/e0onfT44FVbL/rSKnswDgYDVR0PAQH/BAQDAgeAMCMGA1UdEgQcMBqGGGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbTAVBgNVHSUBAf8ECzAJBgcogYxdBQECMC0GA1UdHwQmMCQwIqAgoB6GHGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIhAMuSq75BPBXXBWGtIMd57fhRqpKf3Yzl3ldDdoQsK2xEAiEA/dmWLMLiJPV3UzmQS5MUHtn611z0VlL/k3YAdaVJ51c=\n-----END CERTIFICATE-----\n",
  "certificateData": {
    "country": "US",
    "commonName": "Example DS",
    "crlDistributionPointUri": "https://iaca.example.com/crl"
  }
}
```

**Defaults**:
- A local (`jwk`) secp256r1 key is automatically created.
- Validity defaults to 457 days, unless otherwise specified.

**Note:**

  As we've used the local (`jwk`) key type, it's important to note that we need to save the returned values ourselves
  for future reference. The API doesn't save any information about created keys.

**Note:**

  Remember to save the value of the DS PEM-encoded X.509 certificate (`certificatePEM` field in the response) as
  it is required for the next (issuance) step.

### Step 3: Issue the mDL

To issue mDLs:

1. Use the document signer's key (obtained previously).  
2. Utilize the OID4VCI protocol to facilitate the issuance of the mDL from the document signer (issuer) to the holder.  
3. Generate a credential offer URL that can be accepted by any compliant wallet to receive the credential.

**Note:**

The credential offer URL specifies the credentials to be issued. This includes details such as
the URL of the issuer and information about the credential's format and type.

#### mDL Issuance Request

**Option: CURL**

**Endpoint:**
`/openid4vc/mdoc/issue` | [API Reference](https://issuer.portal.test.waltid.cloud/swagger/index.html#/Credential%20Issuance/post_openid4vc_mdoc_issue)

**Example Request**

```bash
curl -X 'POST' \
  'http://0.0.0.0:7002/openid4vc/mdoc/issue' \
  -H 'accept: */*' \
  -H 'statusCallbackUri: https://example.com/$id' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "ZSHgIcRvbwV9s224kHUaFqkEPShCAdwXocGl_w3M42Q",
      "crv": "P-256",
      "kid": "pX99OZjL2iNqM7OMkE1r1rYyuAObvPntewcDHdc2bMM",
      "x": "GWKpdL3jPoPJ5wKgSA-jxS2jgp-ZUDE6sIQbeB86vF0",
      "y": "F3xAwH96_xVciV7mFQslU_eRQgP-5pSZiNf8bjMoGfo"
    }
  },
  "credentialConfigurationId": "org.iso.18013.5.1.mDL",
  "mdocData": {
    "org.iso.18013.5.1": {
      "family_name": "Doe",
      "given_name": "John",
      "birth_date": "1986-03-22",
      "issue_date": "2019-10-20",
      "expiry_date": "2024-10-20",
      "issuing_country": "AT",
      "issuing_authority": "AT DMV",
      "document_number": "123456789",
      "portrait": [ 141, 182 ],
      "driving_privileges": [
        {
          "vehicle_category_code": "A",
          "issue_date": "2018-08-09",
          "expiry_date": "2024-10-20"
        },
        {
          "vehicle_category_code": "B",
          "issue_date": "2017-02-23",
          "expiry_date": "2024-10-20"
        }
      ],
      "un_distinguishing_sign": "AT"
    }
  },
  "x5Chain": [
    "-----BEGIN CERTIFICATE-----\nMIICCDCCAa2gAwIBAgIUDo8kr194t6sttt6KL3YcnMtcaYYwCgYIKoZIzj0EAwIwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMMDEV4YW1wbGUgSUFDQTAeFw0yNTA1MjkwNzE4MzlaFw0yNjA4MjkwNzE4MzlaMCIxCzAJBgNVBAYTAlVTMRMwEQYDVQQDDApFeGFtcGxlIERTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGWKpdL3jPoPJ5wKgSA+jxS2jgp+ZUDE6sIQbeB86vF0XfEDAf3r/FVyJXuYVCyVT95FCA/7mlJmI1/xuMygZ+qOBvjCBuzAfBgNVHSMEGDAWgBSMIxGx+iVN4rkOzoyo5aPk3HTUFDAdBgNVHQ4EFgQU7S49LSeg/e0onfT44FVbL/rSKnswDgYDVR0PAQH/BAQDAgeAMCMGA1UdEgQcMBqGGGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbTAVBgNVHSUBAf8ECzAJBgcogYxdBQECMC0GA1UdHwQmMCQwIqAgoB6GHGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIhAMuSq75BPBXXBWGtIMd57fhRqpKf3Yzl3ldDdoQsK2xEAiEA/dmWLMLiJPV3UzmQS5MUHtn611z0VlL/k3YAdaVJ51c=\n-----END CERTIFICATE-----\n"
  ]
}'
```
**Header Parameters**

- `statusCallbackUri`: _URL_ - Receive updates on the created issuance process, e.g. when a credential was successfully
  claimed. The parameter expects a URL which can accept a JSON POST request. The URL can also hold a `$id`, which will
  be
  replaced by the issuance session id. For example: `https://myurl.com/$id`, `https://myurl.com`
  or `https://myurl.com/test/$id`
  <br/>
  <br />
  <details><summary>Expand To Learn More</summary>

  <br /> 

  **Body**

  The data send to the provided URL will contain a JSON body:
    - `id` : _String_ - the issuance session id
    - `type`: _String_ - the event type
    - `data`: _JsonObject_ - the data for the event

  **Event Types**

  Possible events (event types) and their data are:
    - `resolved_credential_offer` with the credential offer as JSON (in our Web Wallet: called when the issuance offer is
      entered into the wallet, but not processing / accepted yet)
    - `requested_token` with the issuance request for the token as json object (called for the token required to receive the
      credentials)

  Credential issuance (called for every credential that's issued (= requested from wallet))
    - `jwt_issue` with `jwt` being the issued jwt
    - `sdjwt_issue` with `sdjwt` being the issued sdjwt
    - `batch_jwt_issue` with `jwt` being the issued jwt
    - `batch_sdjwt_issue` with `sdjwt` being the issued sdjwt
    - `generated_mdoc` with `mdoc` being the CBOR (HEX) of the signed mdoc

  To allow for secure business logic flows, if a callback URL is set, and it cannot be reached, the issuance will not
  commence further (after that point). If no callback URL is set, the issuance logic does not change in any way.

  </details>

**Body**

```json
{
  "issuerKey": {
    "type": "jwk",
    "jwk": {
      "kty": "EC",
      "d": "ZSHgIcRvbwV9s224kHUaFqkEPShCAdwXocGl_w3M42Q",
      "crv": "P-256",
      "kid": "pX99OZjL2iNqM7OMkE1r1rYyuAObvPntewcDHdc2bMM",
      "x": "GWKpdL3jPoPJ5wKgSA-jxS2jgp-ZUDE6sIQbeB86vF0",
      "y": "F3xAwH96_xVciV7mFQslU_eRQgP-5pSZiNf8bjMoGfo"
    }
  },
  "credentialConfigurationId": "org.iso.18013.5.1.mDL",
  "mdocData": {
    "org.iso.18013.5.1": {
      "family_name": "Doe",
      "given_name": "John",
      "birth_date": "1986-03-22",
      "issue_date": "2019-10-20",
      "expiry_date": "2024-10-20",
      "issuing_country": "AT",
      "issuing_authority": "AT DMV",
      "document_number": "123456789",
      "portrait": [ 141, 182 ],
      "driving_privileges": [
        {
          "vehicle_category_code": "A",
          "issue_date": "2018-08-09",
          "expiry_date": "2024-10-20"
        },
        {
          "vehicle_category_code": "B",
          "issue_date": "2017-02-23",
          "expiry_date": "2024-10-20"
        }
      ],
      "un_distinguishing_sign": "AT"
    }
  },
  "x5Chain": [
    "-----BEGIN CERTIFICATE-----\nMIICCDCCAa2gAwIBAgIUDo8kr194t6sttt6KL3YcnMtcaYYwCgYIKoZIzj0EAwIwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMMDEV4YW1wbGUgSUFDQTAeFw0yNTA1MjkwNzE4MzlaFw0yNjA4MjkwNzE4MzlaMCIxCzAJBgNVBAYTAlVTMRMwEQYDVQQDDApFeGFtcGxlIERTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGWKpdL3jPoPJ5wKgSA+jxS2jgp+ZUDE6sIQbeB86vF0XfEDAf3r/FVyJXuYVCyVT95FCA/7mlJmI1/xuMygZ+qOBvjCBuzAfBgNVHSMEGDAWgBSMIxGx+iVN4rkOzoyo5aPk3HTUFDAdBgNVHQ4EFgQU7S49LSeg/e0onfT44FVbL/rSKnswDgYDVR0PAQH/BAQDAgeAMCMGA1UdEgQcMBqGGGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbTAVBgNVHSUBAf8ECzAJBgcogYxdBQECMC0GA1UdHwQmMCQwIqAgoB6GHGh0dHBzOi8vaWFjYS5leGFtcGxlLmNvbS9jcmwwCgYIKoZIzj0EAwIDSQAwRgIhAMuSq75BPBXXBWGtIMd57fhRqpKf3Yzl3ldDdoQsK2xEAiEA/dmWLMLiJPV3UzmQS5MUHtn611z0VlL/k3YAdaVJ51c=\n-----END CERTIFICATE-----\n"
  ]
}
```

**Body Parameters**

- `issuerKey`: _JSON_ - A JWK or reference object to a key stored in an external KMS to sign the mDL with.
  Supported algorithms: secp256r1. This must be the **Document Signer (DS) key** obtained from the [DS onboarding step](#step-2-onboard-a-document-signer) (`documentSignerKey` field in the response). The private key here must correspond to the public key embedded in the DS certificate provided in `x5Chain`.

  **JWK Format:** `{"type": "jwk", "jwk": Here JSON Web Key Object}`. Can be provided as String Or JWK object to "
  issuerKey".

  **KMS Key**: Please refer to the [Key Management Section](https://docs.walt.id/community-stack/issuer/key-management/overview.md) and the KMS you want to
  use for more details on the structure
  of the reference object.
  <br />
- `credentialConfigurationId`: _String_ - org.iso.18013.5.1.mDL
- `mdocData`: _JSON_ - Claims to be added to the mDL. In the provided example above, all mandatory claims are included.
- `x5Chain`: _JSON Array_ - Must contain a single entry that contains the PEM-encoded, JSON stringified X.509
  certificate of the DS (as output by the respective onboarding endpoint).

---

**Example Response**

The issuer endpoint will respond with Credential Offer URL.

**Plain Response**

```
openid-credential-offer://issuer.portal.test.waltid.cloud/draft13/?credential_offer_uri=https%3A%2F%2Fissuer.portal.test.waltid.cloud%2Fdraft13%2FcredentialOffer%3Fid%3D52b19ff5-5b42-423a-ad16-63099760baea
```

**Decoded**

```json
{
  "credential_issuer": "https://issuer.portal.test.waltid.cloud/draft13",
  "grants": {
    "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
      "pre-authorized_code": "eyJ0eXBlIjoiand0IiwiYWxnIjoiRVMyNTYiLCJraWQiOiJ0R3pBbUthdE0tbzlicDM1Y291aDZUUXhxZHpVeFgzQWwtcU9iVXhZelh3In0.eyJzdWIiOiI1MmIxOWZmNS01YjQyLTQyM2EtYWQxNi02MzA5OTc2MGJhZWEiLCJpc3MiOiJodHRwczovL2lzc3Vlci5wb3J0YWwudGVzdC53YWx0aWQuY2xvdWQvZHJhZnQxMyIsImF1ZCI6IlRPS0VOIn0.aAgMH0XTq2YVTpsSGtwPIkinTai6Hr0iJyBttDOljPD2WV2kJolZR35K9hiqpz4ZBwGqUbHjmpYaywQqxb8Wfg"
    }
  },
  "credential_configuration_ids": [
    "org.iso.18013.5.1.mDL"
  ]
}
```

### Step 4: Receive the Credential Offer

The created credential offer can be used in two ways:

- **Scan as a QR code** – embed the offer into a QR code for users to scan with their mobile wallet.  
- **Paste manually** – paste the offer into the credential offer field of our [web wallet](https://wallet.demo.walt.id/).

To try it out with our web wallet:

1. Open the [web wallet](https://wallet.demo.walt.id/).
2. Log in.
3. Click the **request credential** button.
4. Paste the received Offer URL into the text field below the camera.

🎉 Congratulations, you've issued a mDL using OID4VCI! 🎉

To learn how to accept this credential offer programmatically using the Wallet API, see [How to Accept a Mobile Driver's License via OID4VCI](https://docs.walt.id/community-stack/wallet/credential-exchange/guides/accept-iso-18013-5-mdl-oid4vci.md).
