Receiving W3C VC Credentials via OID4VCI
This guide shows you how to receive a W3C Verifiable Credential (JWT-signed) into a wallet using the walt.id Wallet API v2. You'll take a credential offer — for example one created with Issuer2 — and claim it into a wallet in a single call, then confirm it was stored.
W3C VC: The W3C Verifiable Credentials data model. Here the credential is delivered as a JWT (jwt_vc_json).
OID4VCI: The protocol used to deliver the credential from the issuer to the wallet.
This page follows the pre-authorized code flow. For the authorization-code flow and for driving each step yourself (custom UIs, deferred issuance), see Custom Flow.
Prerequisites
- Wallet API v2 running — Follow the Setup guide. The examples use
http://localhost:7005as the base URL. - A wallet holding a key and a DID — Create a wallet, then generate a key and create a
did:keyordid:jwkfrom it; both become the wallet's defaults. You'll need the wallet'swalletId. - A W3C VC credential offer — For a full end-to-end run, issue one with the Issuer2 W3C VC guide. The offer is an
openid-credential-offer://URL.
A W3C VC is bound to the holder's DID, and the wallet signs the OID4VCI proof with a key — so it needs both. Without a DID the issuer can't bind the credential subject and rejects the request with Cannot find in context: subjectDid. (SD-JWT VC and mDL need only a key.)
Running via Docker Compose? The quick-start stack exposes Wallet API v2 on http://localhost:7006 (Issuer2 on 7005, Verifier2 on 7004). Use that base URL instead.
Receive the Credential
Hand the offer to the wallet. This single call resolves the offer, requests a token, signs a proof of possession with the wallet's default key, fetches the credential, and stores it.
Endpoint: POST /wallet/{walletId}/credentials/receive | API Reference
Example Request
curl -X POST http://localhost:7005/wallet/{walletId}/credentials/receive \
-H 'Content-Type: application/json' \
-d '{
"offerUrl": "openid-credential-offer://?credential_offer_uri=http%3A%2F%2Flocalhost%3A7005%2Fopenid4vci%2Fcredential-offer%3Fid%3D48f1dc54-134c-4e10-b889-17f3c0a595bf"
}'
Path Parameters
- walletId: String (required) - The ID of the wallet that will receive the credential. Returned when you create a wallet; to look up an existing one, list your wallets with
GET /wallet.
Body Parameters
- Credential offer (required) — provide exactly one of:
- offerUrl: String - The OID4VCI credential offer URL, e.g. the
credentialOffervalue returned by Issuer2. - offerJson: Object - The credential offer as an inline JSON object, instead of a URL.
- offerUrl: String - The OID4VCI credential offer URL, e.g. the
- txCode: String (optional) - Transaction code (PIN) if the offer requires one. Check the offer, or resolve it first to see
txCodeRequired. - keyId: String (optional) - Key from the wallet's key store to bind the credential to. Defaults to the wallet's default key.
- did: String (optional) - DID for holder binding. Defaults to the wallet's default DID.
- clientId: String (optional) - The OAuth 2.0
client_idthe wallet presents to the issuer's authorization server. Defaults towallet-client, which works with issuers that don't require client registration.
Example Response
{
"credentialIds": ["6ba7b810-9dad-11d1-80b4-00c04fd430c8"],
"deferredTransactionIds": {}
}
Response Fields
- credentialIds: String - Wallet-assigned IDs of the credentials that were received and stored.
- deferredTransactionIds: Object - Map of credential configuration ID →
transaction_id, present only when the issuer defers issuance. See deferred issuance.
🎉 You've received a W3C VC credential into the wallet.
Confirm It Was Stored
List the wallet's credentials to see the one you just received. This returns metadata only — no raw credential data.
Endpoint: GET /wallet/{walletId}/credentials | API Reference
Example Request
curl http://localhost:7005/wallet/{walletId}/credentials
Path Parameters
- walletId: String (required) - The ID of the wallet whose credentials you want to list. Returned when you create a wallet; to look up an existing one, list your wallets with
GET /wallet.
Example Response
[
{
"id": "5ea45ef4-19aa-40bc-9746-96bb9d3893ec",
"format": "jwt_vc_json",
"issuer": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Ii...",
"subject": "did:key:zDnaeyvVjxHZwnWBb8gLxnHhJLM6TcRZfyCzs35DHMGk39V4f",
"addedAt": "2026-07-08T20:35:24.653545720Z"
}
]
Response Fields
- id: String - The stored credential's ID. Use it with Credential Management to view or delete it.
- format: String - Credential format,
jwt_vc_jsonfor a JWT-signed W3C VC. - issuer: String (optional) - Issuer identifier — here the issuer's
did:jwk. - subject: String (optional) - Holder DID the credential is bound to. For a W3C VC this is your wallet's DID from the prerequisites above.
- label: String (optional) - User-assigned label, when set.
- addedAt: String - ISO-8601 timestamp of when the credential was stored.
Optional fields (issuer, subject, label) are omitted when not present.
Doing It Step by Step
The full-flow call above is the fastest path. If you're building a wallet UI and need to show intermediate state — resolve the offer, preview what's being issued, prompt for a PIN, handle the authorization-code flow — drive the isolated endpoints yourself. Each step and its request/response shape is documented in Custom Flow.
Next Steps
- Present the credential you just received — Try Presenting W3C VC Credentials to share it with Verifier2.
- Receive other credential types — SD-JWT VC or mDL.
- Manage stored credentials — List, view, and delete credentials in the wallet.
