Skip to main content
Version: v0.25.0 (Latest)

OID4VCI Issuer Service

The OID4VCI Issuer service implements the OpenID for Verifiable Credential Issuance specification. It handles the server-side protocol for issuing verifiable credentials to holder wallets. The service supports SD-JWT, mDoc (ISO 18013-5), and JWT VC JSON credential formats, multiple grant types (authorization code and pre-authorized code), deferred issuance for asynchronous workflows, and batch issuance for requesting multiple credentials in one round trip. In practice it exposes both holder-facing OID4VCI endpoints and a separate issuer-integration surface used by issuer backend apps and web apps to create and manage issuance flows.

Holder (Wallet) Endpoints

MethodPathDescription
GET/.well-known/openid-credential-issuerIssuer metadata. Returns a JSON document describing the issuer's capabilities: supported credential types, endpoints, proof types, display information, and encryption settings. Wallets fetch this to discover how to interact with the issuer.
GET/credential-offers/{offerId}Retrieve a credential offer by ID. When the issuer creates an offer, it generates a URI that points here. The wallet resolves the offer to learn which credentials are available and which grant types to use.
POST/nonceIssue a c_nonce for proof generation. The wallet calls this to get a fresh nonce before creating the proof-of-possession JWT it includes in the credential request.
POST/credentialHandle credential requests. The wallet submits an access token and a proof-of-possession JWT. The service validates the token, verifies the proof, resolves credential attributes via registered CredentialAttributeContributor instances, and delegates to the appropriate CredentialFormatHandler to build the signed credential.
POST/deferred-credentialHandle deferred credential polling. When immediate issuance is not possible (pending approval, background checks), the issuer returns a transactionId from /credential. The wallet polls this endpoint with that ID until the credential is ready.
POST/notificationReceive holder notifications. After storing a credential, the wallet can notify the issuer whether it was accepted, rejected, or deleted. Processed idempotently, so duplicate deliveries from retries are safe.

Issuer Integration Endpoints

These are the three issuer-facing endpoints used by issuer backend apps or web apps to initiate and manage issuance sessions:

MethodPathDescription
POST/oid4vci/backend/credential/offersCreate a credential offer session. Accepts credential_configuration_ids, optional pre-seeded credential_subject_data, optional grants, issuer_id, correlation_id, deeplink scheme, QR code options, callback configuration, opaque state, and ttl_seconds. Returns the correlation_id, offer_uri, optional status_uri, optional QR data URI, and optional tx_code.
GET/oid4vci/backend/credential/offers/{correlationId}Get credential offer session status for the issuer app. Returns credential_offer_created, credential_offer_retrieved, token_requested, credential_requested, credential_issued, or error, plus issuance data when a credential has been issued.
DELETE/oid4vci/backend/credential/offers/{correlationId}Delete a credential offer session created through the issuer-facing backend API. Useful when an issuance flow is cancelled or should be cleaned up explicitly before TTL expiry.

These issuer-integration endpoints are separate from the holder-facing OID4VCI spec endpoints. They are deployment-specific because they depend on your issuer UX and business process, while the holder-facing routes remain standardized for wallet interoperability.

Including in Your Server

build.gradle.kts
dependencies {
implementation("com.sphereon.idk:services-oid4vci-issuer-rest:0.25.0")
}

Configuration

The issuer is configured through the issuer metadata DSL or YAML. There are two main approaches:

DSL approach: build issuer metadata programmatically in Kotlin:

val metadata = issuerMetadata("https://issuer.example.com") {
authorizationServer("https://auth.example.com")

credentialConfiguration("IdentityCredential", CredentialFormat.SD_JWT_DC) {
vct = "https://issuer.example.com/identity"
scope = "identity"
bindingMethod("jwk")
signingAlg(JwaAlgorithm.ES256)
proofType(ProofType.JWT, JwaAlgorithm.ES256)

display {
name = "Identity Credential"
locale = "en-US"
}
}
}

YAML approach: define everything in configuration files:

oid4vci:
issuer:
identifier: https://issuer.example.com
authorizationServers: https://auth.example.com
credentialConfigurationIds: IdentityCredential
credentials:
"[IdentityCredential]":
format: dc+sd-jwt
scope: identity
signingAlgorithms: ES256
bindingMethods: jwk
proofTypes:
jwt:
signingAlgorithms: ES256
display:
name: Identity Credential
locale: en-US

Additional configuration points include:

  • Config provider: Choose between ConfigDrivenOid4vciIssuerConfigProvider (reads from YAML/env vars) or DesignBackedOid4vciIssuerConfigProvider (reads from the credential design store).
  • Per-credential issuance policy: Control grant types, nonce TTL, deferred retry intervals, encryption requirements, and Interactive Authorization Exchange (IAE) settings per credential type.
  • Extension points: Register custom CredentialFormatHandler implementations for additional credential formats, CredentialAttributeContributor implementations to supply claims from your business logic, and an Oid4vciAuthorizationServerBridge to connect to your OAuth2 infrastructure.

See OID4VCI Issuer for the full guide, including credential offer creation, attribute contributors, deferred handling, and signed metadata.

Docker

Each service ships with a Dockerfile and docker-compose configuration in its container/ directory.

Building the image

# Build the fat JAR first
./gradlew :services-oid4vci-issuer-rest:buildFatJar

# Build the Docker image
docker compose -f services/oid4vci-issuer/container/docker-compose.yaml build

Running with Docker Compose

docker compose -f services/oid4vci-issuer/container/docker-compose.yaml up

The service starts on port 8080. Configuration is loaded from container/config/ inside the image. Override settings via environment variables in a .env file next to the docker-compose.yaml.

Image details

PropertyValue
Base imageeclipse-temurin:21-jre
Docker imagesphereon/idk-oid4vci-issuer:latest
Exposed port8080
Config location/app/config/

Next Steps

  • Services Overview for an introduction to all available IDK services and the CommandBackedHttpAdapter pattern
  • OID4VCI Issuer Guide for the full guide on credential offer creation, issuer integration, attribute contributors, and deferred handling
  • OID4VCI Holder for the wallet-side credential acquisition service
  • OAuth2 Authorization Server for the authorization server that works alongside the issuer
  • Ktor Integration for details on installing and configuring the KotlinInjectPlugin