How to Issue a Verifiable Credential
A guide on issuing an EUDI Verifiable Credential.
This guide walks you through the process of creating a Verifiable Credential using the FortID Issuer Service API. The full workflow involves:
- Creating a FortID Issuer
- Assigning a schema to the issuer
- Initiating credential issuance
- Checking issuance session status
- Retrieving issuer details
Prerequisites
Before issuing a credential, make sure the user has an EUDI-compliant wallet application installed on their device.
If you're unsure whether this requirement is met, refer to this guide.
Introduction
Issuing a Verifiable Credential (VC) with FortID involves calling its FortID Issuer API. The process includes:
- Preparing the credential data.
- Creating an issuer identity with valid metadata.
- Assigning a credential schema that defines the structure and semantics of the credential.
- Initiating a credential issuance session using OpenID4VCI.
- Monitoring issuance status and retrieving results.
FortID supports two credential formats:
- MSO-MDOC - a COSE-based binary format used for mobile credentials, enabling selective disclosure through structured, offline-friendly mechanisms.
- SD-JWT - a JWT-based format that embeds selective disclosure directly, making it ideal for web and app integrations using JSON.
The currently supported Verifiable Credentials include Personal Identification Data (PID) and Mobile Driving Licence (mDL).
Well known credential schemas
Two primary credential types currently recognized in the EUDI architecture are the Mobile Driving Licence (mDL) and Personal Identification Data (PID). These schemas serve as trusted and interoperable foundations within the EUDI ecosystem. By standardizing the structure and format of identity-related credentials, they help ensure cross-border recognition, legal compliance, and technical consistency across various European stakeholders. To issue credentials through EUDI-compatible wallets, Credential Issuers must explicitly declare support for one or more of these schemas in their metadata.
Personal Identification Data (PID)
The PID schema represents a standardized structure for issuing core identity attributes, such as name, date of birth, nationality and document id. It is compliant with specifications defined in the Architecture Reference Framework (ARF) for EUDI.
Key characteristics:
- Schema ID:
"eu.europa.ec.eudi.pid_vc_sd_jwt"and"eu.europa.ec.eudi.pid_mdoc" - Credential Formats: Supports both
vc+sd-jwtandmso_mdoc - Use Case: Designed for general-purpose digital identification, suitable for eGovernment services, private sector onboarding and cross-border interactions
- Privacy Features: Enables selective disclosure of attributes and binding to the holder's wallet keys
Mobile Driving Licence (mDL)
The mDL schema is based on the international standard ISO/IEC 18013-5:2021, which defines the data model and communication protocol for mobile driving licences. In the EUDI context, credentials issued using the mDL schema contain verified driving license data in a digital format suitable for presentation and verification.
Key characteristics:
- Schema ID:
"org.iso.18013.5.1.mDL" - Credential Format:
mso_mdoc - Use Case: Provides a digital equivalent of the physical driving licence, enabling use in both online and offline scenarios
- Data Protection: Supports selective disclosure and cryptographic proof of authenticity
Issuer’s .well-known endpoint
The .well-known/openid-credential-issuer endpoint is a standardized location defined by the OpenID
for Verifiable Credential Issuance (OpenID4VCI) specification, and adopted in the EUDI framework.
It allows wallets and other relying parties to automatically discover key metadata about a Credential Issuer.
This metadata is published as a JSON document and provides crucial details needed for interoperability, including:
-
Credential Types – A list of verifiable credentials the issuer supports.
-
Credential Formats – Supported data formats such as SD-JWT or mso_mdoc.
-
Authorization Server – Information about the OAuth 2.0 authorization server used for authentication and credential issuance.
-
Display Information – Optional branding details such as names, logos or localized descriptions for a better user experience.
By exposing this information at a well-known, machine-readable location, issuers make it easier for wallets to understand how to interact with them. This approach ensures transparency, automation, and consistent integration across different systems within the EUDI ecosystem.
1. Create an Issuer
Issuer is an entity that performs identity verification (or relies on existing verification), formats user data according to a recognized schema, and issues a digitally signed credential that can be presented and verified later.
An Issuer's identity includes a cryptographic key pair and optional metadata used for display and trust configuration.
When a new Issuer is registered, the Issuer Service performs several key actions automatically:
-
Generates a cryptographic identity by creating a public-private key pair. This identity is used to sign all credentials issued by this issuer.
-
Establishes metadata endpoints that describe the issuer's identity and capabilities in a standardized format.
Note: To streamline the developer experience when testing and exploring the API, all examples in this guide include the predefined FortID Issuer and Verifier Playground API keys. To learn more about API key authentication and how to obtain your own API key, see our Using the API-KEY guide.
To register a new issuer, submit a POST request to the FortID Issuer Service like in this example:
curl -X POST "https://eis.fortid.com/control/issuer" \
-H "Content-Type: application/json" \
-H "X-API-KEY: HCLN4ZKnWYJAfyNkDnQ57gEAHuejD6MN" \
-d '{
"issuerId": "university-issuer",
"issuerMetadata": {
"display": [
{
"name": "UniversityIssuer",
"locale": "en-US",
"background_color": "#12807c",
"text_color": "#FFFFFF"
}
]
}
}'The issuerId is used as a unique identifier in all operations related with this issuer instance:
Assign a schema for the issuer,
Initiate a Credential issuance with given issuer
and Retrieving details for given issuer
This ID will be published in the .well-known endpoint.
If you do not specify an issuerId, the service will automatically generate a random UUID.
You may also provide optional Credential Issuer Metadata using the issuerMetadata object.
This metadata will be published via the standard OpenID4VCI .well-known endpoint for the issuer.
issuerMetadata.display is an optional field. It is an array of objects describing how the issuer should appear in wallets and user interfaces. Each object may include:
-
name— A human-readable name for the issuer -
locale— A language tag (e.g., "en-US") -
background_color,text_color— Custom branding colors
These fields follow the official OID4VCI display specification. If these optional fields are omitted, the issuer will still be registered with the minimum required metadata.
Issuer registration response example
The response includes the issuer identifier, a public key, and a metadata endpoint url.
{
"issuerId": "university-issuer",
"issuerPublicKey": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1MtHIxlGP5TARqBccrddNm1FnYH1Fp+o\nnETz5KbXPSeG5FGwKMUXGfAmSZJq2gENULFewwymt+9bTXkjBZhh8A==\n-----END PUBLIC KEY-----",
"issuerMetadataEndpoint": "https://eis.fortid.com/protocol/oid4vci/issuer/university-issuer/.well-known/openid-credential-issuer"
}issuerIdis a unique identifier for each issuer registered with the FortID Issuer Service.issuerPublicKeyis the public part of the cryptographic key used by the FortID Issuer Service to sign all credentials it issues.issuerMetadataEndpointcontains display properties of the Credential Issuer, including both language-specific elements (such as localized names) and language-independent elements (such as logos or brand colors), as provided during issuer registration.
2. Assign a Schema to the FortID Issuer
Before a FortID Issuer can issue a specific type of Verifiable Credential, a corresponding credential schema must be assigned to it.
This is done by sending a POST request to the /control/issuer/issuerId/add-schema endpoint.
The request must include a schemaId — a unique identifier of a credential schema recognized by the Issuer Service.
Currently, the FortID Issuer Service supports the following well-known schemas described in chapter Issuer’s well-known schemas.
Now that we covered all the basics let's see a concrete example how to actually assign schema to the issuer by using the following curl command:
curl -X POST "https://eis.fortid.com/control/issuer/fortid_issuer/add-schema" \
-H "Content-Type: application/json" \
-H "X-API-KEY: HCLN4ZKnWYJAfyNkDnQ57gEAHuejD6MN" \
-d '{
"schemaId": "org.iso.18013.5.1.mDL",
"credentialMetadata": {
"credential_configuration_id": "org.iso.18013.5.1.mDL.HR",
"display": [
{
"name": "Croatian Driving License",
"locale": "en-US",
"background_color": "#12107c",
"text_color": "#FFFFFF",
"logo": {
"uri": "https://mup.hr/public/logo.png",
"alt_text": "a square logo"
}
}
]
}
}'Assign Schema to Issuer Response
If the request is successful, an HTTP 200 response is returned. Otherwise, an appropriate HTTP error code is provided.
Upon successful schema assignment:
- The FortID Issuer will be able to issue credentials that conform to the specified schema.
- The FortID Issuer’s metadata is automatically updated:
- A new entry is added to the
credential_configurations_supportedsection of the Credential Issuer Metadata, which is exposed via the standard.well-knownendpoint.
- A new entry is added to the
3. Initiate a VC issuance
Once both the FortID Issuer and the credential schema are set up, you can initiate a credential issuance session, resulting in a Credential Offer ready to be delivered to a wallet.
The following steps demonstrate how to initiate a credential issuance session with an external wallet (holder) using the pre-authorized code flow of the OpenID for Verifiable Credential Issuance (OpenID4VCI) protocol.
To begin the issuance session, the caller provides the credentialData object.
Required Fields in credentialData:
credentialConfigurationIdis a unique ID of the credential configuration assigned to the issuer. This configuration defines the structure and format of the credential.claims— essentially, these are the user data that will be included in the issued credential.
Note: Fields that expect byte array values must be encoded in Base64.
To customize the behavior of the issuance protocol, you can optionally include a protocolConfig object in the request.
If omitted, the Issuer Service defaults to settings compatible with the EUDI Android Reference Wallet.
Currently, the only supported field in protocolConfig is grant_type.
Allowed values are "PreAuthorizedCode" and "PreAuthorizedCodeWithoutTxCode".
If not specified, the default value is "PreAuthorizedCode".
Now that everything is in place, we can show an example of an issuance request to the Issue Credential endpoint, using the following curl request:
curl -X POST "https://eis.fortid.com/control/issuer/fortid_issuer/initiate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: HCLN4ZKnWYJAfyNkDnQ57gEAHuejD6MN" \
-d '{
"credentialData": {
"credentialConfigurationId": "eu.europa.ec.eudi.pid_mdoc",
"claims": {
"eu.europa.ec.eudi.pid.1": {
"birth_date": "1980-05-23",
"birth_place": "DE",
"expiry_date": "2026-05-23T12:12:12Z",
"family_name": "Doe",
"given_name": "John",
"issuance_date": "2024-05-23T12:12:12Z",
"issuing_authority": "DE",
"issuing_country": "DE",
"nationality": [
"FR"
],
"resident_city": "Rome",
"resident_country": "IT",
"resident_postal_code": "00100",
"resident_street": "123 via Appia",
"sex": 5
}
}
},
"protocolConfig": {
"grant_type": "PreAuthorizedCode"
}
}'Completing the Credential Issuance
To complete the issuance process, the user must continue the flow using their wallet.
- The
credentialOfferUrimust be shared with the wallet — typically via a QR code. - The wallet scans or opens the
credentialOfferUri. - The wallet communicates directly with the FortID Issuer Service through the protocol API (OID4VCI) to complete the credential issuance.
Initiate response example
On success, the Issuer Service returns:
{
"sessionId": "36dcdb3e-35bd-4362-bf67-472efa0cf744",
"vpTokenOrPreAuth": {
"grant_type": "urn:ietf:params:oauth:grant-type:pre-authorized_code",
"pre-authorized_code": "abc123",
"tx_code": "xyz789"
},
"credentialOfferUri": "openid-credential-offer%3A%2F%2F%3Fcredential_offer_uri%3D..."
}-
sessionIdis a unique identifier for the newly created issuance session. -
vpTokenOrPreAuth- It shows how a credential issuance flow can be initialized using the Pre-Authorized Code grant type.grant_typeindicates that the pre-authorized flow is being used, meaning the client has already been approved to obtain a credential using a pre-issued code.pre-authorized_codeis a one-time code that the wallet sends to the Issuer’s token endpoint to request a credential.tx_codeis an optional second factor secret (usually in the form of a PIN or a transaction code) exchanged between the user and the FortID Issuer through a second channel (e.g., an SMS or e-mail)
-
credentialOfferUriis a URI that wallets use to retrieve the Credential Offer, typically shared as a QR code. The Credential Offer provides the wallet with all required details, including the FortID Issuer Service URL, claim specifications, and associated metadata.
Note: Wallet interaction happens after you present
credentialOfferUrito the user.
4. Check Issuance Status
Once the session has been initiated, the result of the credential issuance session is checked using
the following curl command:
curl -X POST "https://eis.fortid.com/control/issuer/fortid_issuer/get-session-status/{sessionId}" \
-H "Content-Type: application/json" \
-H "X-API-KEY: HCLN4ZKnWYJAfyNkDnQ57gEAHuejD6MN" Path Parameters:
sessionIdis the unique identifier (UUID) of the issuance session. It can be obtained from the response of the previous initiate credential request.
Note: Replace
sessionIdin the URL with the value received in the initiate credential issuance response.
Status response example
Response of the status check will contain issuanceSessionStatus indicating current status of the credential issuance session
and the description dependent on the status of issuance session.
Possible Statuses
There are three possible statuses which you can expect to receive under issuanceSessionStatus:
IN_PROGRESSThe issuance process is still ongoing.ISSUANCE_FAILEDIssuance has failed. The description field explains why.ISSUANCE_SUCCEEDEDCredential was successfully issued and received by the wallet.
Besides the usual http response codes (e.g. 200 OK) these are the two more interesting session status response codes:
410- Session expired. A temporary state. The VS was issued but not claimed, and the session will soon be deleted.404- Session not found. The session either never existed, or has timed out and deleted.
{
"issuanceSessionStatus": "ISSUANCE_FAILED",
"description": "Obtained invalid proof from wallet (signature verification failed)."
}Notes:
- Issuance sessions automatically expire after a configured timeout. For example, an issuance session might expire 10 minutes after it is created if the wallet does not complete the process in time.
5. Retrieve Issuer Details
This guide explains how to retrieve details about a specific already registered
Issuer from the Issuer Service using their unique identifier issuerId.
curl -X GET "https://eis.fortid.com/control/issuer/fortid_issuer" \
-H "Content-Type: application/json" \
-H "X-API-KEY: HCLN4ZKnWYJAfyNkDnQ57gEAHuejD6MN"Retrieve Issuer Details Example Response
The response will include the FortID Issuer identifier, public key, and metadata endpoint URL. It is identical to the Register Issuer response. For more details, see the section Issuer registration response example.
{
"issuerId": "university-issuer",
"issuerPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
"issuerMetadataEndpoint": "https://eis.fortid.com/protocol/oid4vci/issuer/university-issuer/.well-known/openid-credential-issuer"
}Need additional help? Visit the full Issuer API reference.