Issuing W3C VC Credentials via OID4VCI
This guide shows you how to issue a W3C Verifiable Credential (JWT-signed) end to end with the Enterprise Issuer2 Service. You'll create a minimal credential profile, turn it into a credential offer, and claim it in a wallet.
W3C VC: The W3C Verifiable Credentials data model, here signed as a JWT (jwt_vc_json).
OID4VCI: The protocol used to deliver the credential to a wallet.
Prerequisites
Before you begin, ensure you have:
- An Issuer2 service with a W3C credential configuration (format
jwt_vc_json, with acredential_definition). See Setup. - A KMS key for signing, in a KMS Service under the same tenant.
- An issuer DID created via the DID Service.
- A wallet to receive into — an Enterprise Stack Wallet Service (see Receiving a W3C VC), or any OID4VCI-compatible wallet app.
Throughout this guide:
- orgID is your organization ID, e.g.
test.enterprise-sandbox.waltid.dev. - target is your issuer service,
{organizationID}.{tenantID}.{issuerServiceID}, e.g.waltid.tenant1.issuer1.
Flow Overview
- Create a profile — Bind the
UniversityDegree_jwt_vc_jsontype to your key, DID, and a data template. - Create an offer — Generate an OID4VCI offer URL from the profile.
- Claim the offer — Hand the URL to a wallet.
Step 1: Create a Profile
Endpoint: POST /v2/{target}.{profileId}/issuer-service-api/credentials/profiles | API Reference
The new profile's own ID is the last path segment of the target (like service creation), i.e. {target}.{profileId} — here waltid.tenant1.issuer1.profile-def456.
Example Request
curl -X 'POST' \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{target}.profile-def456/issuer-service-api/credentials/profiles' \
-H 'accept: application/json' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"name": "University Degree Profile",
"credentialConfigurationId": "UniversityDegree_jwt_vc_json",
"issuerKeyId": "waltid.tenant1.kms1.key1",
"issuerDid": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
"w3cVersion": "W3CV2",
"credentialData": {
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://www.w3.org/ns/credentials/examples/v2"
],
"type": ["VerifiableCredential", "UniversityDegree"],
"credentialSubject": {
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
},
"mapping": {
"id": "<uuid>",
"issuer": { "id": "<issuerDid>" },
"credentialSubject": { "id": "<subjectDid>" },
"validFrom": "<timestamp>",
"validUntil": "<timestamp-in:365d>"
}
}'
Body Parameters
- credentialConfigurationId: String (required) - Must match a W3C (
jwt_vc_json) configuration on your issuer service. - issuerKeyId: resourceIdentifier (required) - A key in your KMS.
- issuerDid: String (optional) - The issuer DID.
- credentialData: Object (required) - The credential body (
@context,type,credentialSubject). - w3cVersion: String (optional) -
W3CV11orW3CV2. Controls which VCDM version is issued and whetherissuanceDate/expirationDateorvalidFrom/validUntilare used.
See Create a Profile for every field.
When w3cVersion is W3CV2, the service applies the correct VCDM v2 @context and uses validFrom/validUntil in place of issuanceDate/expirationDate, regardless of what you put in credentialData.
Example Response
{
"profileId": "profile-def456",
"name": "University Degree Profile",
"version": 1,
"credentialConfigurationId": "UniversityDegree_jwt_vc_json",
"createdAt": 1704067200000,
"updatedAt": 1704067200000
}
The example above is shortened — the response also echoes your submitted issuerDid, issuerKeyId, credentialData, mapping, and w3cVersion.
Response Fields
- profileId: String - The profile's own ID (the last path segment you chose), e.g.
profile-def456. The offer in Step 2 targets the full path{target}.{profileId}, e.g.waltid.tenant1.issuer1.profile-def456.
Response Codes
201— Profile created.400— Invalid request body.401— Invalid or missing authentication token.
Step 2: Create a Credential Offer
Create the offer against the profile as the target — the profileId from Step 1.
Endpoint: POST /v2/{profileTarget}/issuer-service-api/credentials/offers | API Reference
Example Request
curl -X 'POST' \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/waltid.tenant1.issuer1.profile-def456/issuer-service-api/credentials/offers' \
-H 'accept: application/json' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"authMethod": "PRE_AUTHORIZED"
}'
Body Parameters
- authMethod: String (required) -
PRE_AUTHORIZEDrequires no user login. See Protocol Flows for the alternative.
See Create an Offer for PIN, expiration, delivery mode, and runtime overrides.
Example Response
{
"offerId": "abc123-def456-ghi789",
"profileId": "profile-def456",
"profileVersion": 1,
"authMethod": "PRE_AUTHORIZED",
"expiresAt": 1704067500000,
"credentialOffer": "openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fwaltid.enterprise-sandbox.waltid.dev%2Fv2%2Fwaltid.tenant1.issuer1%2Fissuer-service-api%2Fopenid4vci%2Fcredential-offer%3Fid%3Dabc123-def456-ghi789"
}
Response Fields
- credentialOffer: String - The OID4VCI credential offer URL. Turn it into a QR code or deep link for the wallet.
Claiming the Offer
Hand the credentialOffer URL to a wallet to receive the credential.
With the Enterprise Stack Wallet Service this is a single API call — see Receiving a W3C VC, which passes the offer URL to the wallet's OID4VCI receive endpoint (Wallet API v2), resolves the offer, requests the credential, and stores it.
To claim it in any other wallet: generate a QR code from the credentialOffer URL which the wallet can scan or open it as a deep link.
Using the Authorization Code Flow Instead
The example above used PRE_AUTHORIZED — no user login. To have the holder authenticate against an external IdP and map ID-token claims into the credential via idTokenClaimsMapping, use authMethod: "AUTHORIZED". See Authorization Code Flow.
Next Steps
- Verify the credential you just issued — Try Verifying W3C Credentials.
- Populate credentials dynamically — See Data Functions.
- Track issuance events — See Notifications & Session Events.
- Issue other credential types — Try SD-JWT VC or mDL.
