Isolated Flow
The full-flow endpoint (credentials/present) matches, signs, and submits in one call. The isolated flow lets you drive each step yourself when you need to:
- Preview which credentials (and claims) will be shared, and show a consent screen
- Let the user choose between multiple matching credentials
- Present credentials that aren't stored in the wallet (supplied inline)
- Inspect intermediate results
All endpoints live under /v2/{target}/wallet-service-api/.
Flow Overview
1. Resolve VP Request → parse request URL → authorizationRequest, nonce, clientId, responseUri, dcqlQuery
2. Match Credentials → DCQL match (from store or inline) → matched credential IDs
── show user consent screen ──
3. Build VP Token → sign selected stored credentials → vpToken
4. Send Response → submit vpToken → WalletPresentResult
Step 1 — Resolve VP Request
Parse the verifier's authorization request to extract its metadata.
Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/resolve-request
Example Request
curl -X POST \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/resolve-request' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"requestUrl": "openid4vp://authorize?client_id=did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0&request_uri=https://verifier.example.org/request/abc123"
}'
Body Parameters
requestUrlString — The OID4VP authorization request URL.
Example Response
{
"authorizationRequest": {
"nonce": "n-0S6_WzA2Mj",
"client_id": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0",
"response_uri": "https://verifier.example.org/response",
"dcql_query": {
"credentials": [
{
"id": "identity_credential",
"format": "dc+sd-jwt"
}
]
}
},
"nonce": "n-0S6_WzA2Mj",
"clientId": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0",
"responseUri": "https://verifier.example.org/response",
"hasRequestUri": true,
"dcqlQuery": {
"credentials": [
{
"id": "identity_credential",
"format": "dc+sd-jwt"
}
]
}
}
authorizationRequest— Complete resolved authorization request. Pass it to Step 3 — Build VP Token and Step 4 — Send Response.nonce— The verifier's nonce for replay protection.clientId— The verifier's client identifier (typically a DID).responseUri— The URL the presentation is submitted to.hasRequestUri— Whether the request was fetched from arequest_uri.dcqlQuery— The verifier's DCQL query. Pass this to Step 2a or Step 2b.
Response Codes
200— Request resolved.400— The request URL/object is malformed and cannot be parsed.500— Therequest_uricould not be fetched (e.g. the verifier's request endpoint returned 404).
Step 2a — Match Credentials from Store
DCQL-match the dcqlQuery returned by Step 1 against the wallet's own stored credentials. This is the recommended matching step when credentials live in the wallet's Credential Store.
Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/match-credentials-from-store
Example Request
curl -X POST \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/match-credentials-from-store' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"dcqlQuery": {
"credentials": [
{
"id": "identity_credential",
"format": "dc+sd-jwt",
"meta": { "vct_values": ["https://issuer.example.org/identity_credential"] },
"claims": [
{ "path": ["given_name"] },
{ "path": ["family_name"] }
]
}
]
}
}'
Body Parameters
dcqlQueryObject — The DCQL query returned by Step 1.
Example Response
{
"matchedQueryIds": ["identity_credential"],
"matchCount": 1,
"matchedCredentialIds": {
"identity_credential": ["550e8400-e29b-41d4-a716-446655440000"]
}
}
matchedQueryIds— DCQL query IDs for which at least one credential matched.matchCount— Total number of matches across all query IDs.matchedCredentialIds— For each matched query ID, the wallet-assigned credential IDs. Use these IDs in Step 3.
Response Codes
200— Matching completed.400— Invalid DCQL query.
Step 2b — Match Inline Credentials
DCQL-match against credentials you supply inline — useful to preview matches for credentials that aren't stored in the wallet.
Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/match-credentials
Example Request
curl -X POST \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/match-credentials' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"dcqlQuery": {
"credentials": [
{
"id": "identity_credential",
"format": "dc+sd-jwt",
"meta": { "vct_values": ["https://issuer.example.org/identity_credential"] },
"claims": [ { "path": ["given_name"] } ]
}
]
},
"credentials": [
{
"id": "cred-1",
"credential": {
"type": "vc-sd_jwt",
"format": "dc+sd-jwt",
"credentialData": {
"given_name": "John",
"family_name": "Doe",
"vct": "https://issuer.example.org/identity_credential",
"cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "…" } }
},
"disclosures": [
{ "salt": "…", "name": "birthdate", "value": "1990-01-15", "location": ["birthdate"], "encoded": "…" }
],
"signed": "eyJraWQiOiJHMzdKaUdyM3B0bEQ…",
"signature": { "type": "signature-sd_jwt", "jwtHeader": { "typ": "dc+sd-jwt", "alg": "EdDSA" } }
}
}
]
}'
Body Parameters
dcqlQueryObject — The DCQL query from the verifier's authorization request.credentialsArray — Credentials to match against, each with anid(a caller-assigned label) and acredentialobject. Thecredential's shape depends on the credential format (itstypefield isvc-sd_jwt,vc-w3c_2, orvc_mdocs) — it is not a{ format, rawCredential }pair. The easiest way to obtain it is to copy thecredentialfield from the wallet'sGET /v2/{target}/wallet-service-api/credentials/{credentialId}response (abbreviated with…above).
Example Response
Same structure as Step 2a.
Response Codes
200— Matching completed.400— Invalid DCQL query or credentials.
Step 3 — Build VP Token
After the user consents, build and sign a vp_token from the selected stored credentials. This step does not submit anything to the verifier.
Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/build-vp-token
Example Request
curl -X POST \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/build-vp-token' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"authorizationRequest": {
"nonce": "n-0S6_WzA2Mj",
"client_id": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0",
"response_uri": "https://verifier.example.org/response",
"dcql_query": {
"credentials": [
{ "id": "identity_credential", "format": "dc+sd-jwt" }
]
}
},
"selectedCredentialIds": {
"identity_credential": ["550e8400-e29b-41d4-a716-446655440000"]
}
}'
Body Parameters
authorizationRequestObject — The resolved authorization request returned by Step 1.selectedCredentialIdsObject — Map of DCQL query ID to wallet credential IDs selected by the user.key(optional) Object — Inline key used to sign the VP token. Takes precedence overkeyId.keyId(optional) String — Key used to sign the VP token. Defaults to the first key in the wallet's linked KMS or the service's configured static key.did(optional) String — DID for holder binding. Defaults to the first DID in the linked DID Store or the configured static DID.
Example Response
{
"vpToken": "{\"identity_credential\":\"eyJhbGciOiJFUzI1NiIs...\"}",
"idToken": null
}
vpToken— Serializedvp_tokenJSON string.idToken(optional) — Self-issued ID token for SIOPv2 flows, when one is required.
Response Codes
200— VP token built.400— Selected credentials do not satisfy the authorization request.
Step 4 — Send Response
Send the built VP token to the verifier according to the response mode in the resolved authorization request.
Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/send-response
Example Request
curl -X POST \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/send-response' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"authorizationRequest": {
"nonce": "n-0S6_WzA2Mj",
"client_id": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0",
"response_uri": "https://verifier.example.org/response"
},
"vpToken": "{\"identity_credential\":\"eyJhbGciOiJFUzI1NiIs...\"}",
"idToken": null
}'
Body Parameters
authorizationRequestObject — The resolved authorization request returned by Step 1.vpTokenString — The VP token returned by Step 3.idToken(optional) String — The ID token returned by Step 3, when present.
Example Response
Returns the same WalletPresentResult shape as the full-flow response.
Response Codes
200— Authorization response transmitted.400— The response could not be sent to the verifier.
Present Inline Credentials
Use the isolated endpoint below when the credentials are supplied inline rather than loaded from the wallet's stores.
Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/isolated
Example Request
curl -X POST \
'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/isolated' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"requestUrl": "openid4vp://authorize?client_id=did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0&request_uri=https://verifier.example.org/request/abc123",
"credentials": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"credential": {
"type": "vc-sd_jwt",
"format": "dc+sd-jwt",
"credentialData": {
"given_name": "John",
"family_name": "Doe",
"vct": "https://issuer.example.org/identity_credential",
"cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "…" } }
},
"disclosures": [
{ "salt": "…", "name": "birthdate", "value": "1990-01-15", "location": ["birthdate"], "encoded": "…" }
],
"signed": "eyJraWQiOiJHMzdKaUdyM3B0bEQ…",
"signature": { "type": "signature-sd_jwt", "jwtHeader": { "typ": "dc+sd-jwt", "alg": "EdDSA" } }
}
}
]
}'
Body Parameters
requestUrlString — The OID4VP authorization request URL.credentialsArray — The credentials to present, each with anid(a caller-assigned label) and acredentialobject. Thecredential's shape depends on the credential format (itstypefield isvc-sd_jwt,vc-w3c_2, orvc_mdocs) — it is not a{ format, rawCredential }pair. Copy it from thecredentialfield of the wallet'sGET /v2/{target}/wallet-service-api/credentials/{credentialId}response (abbreviated with…above).keyId(optional) String — ID of the key to sign with. Defaults to the first key in the wallet's linked KMS (or the service's configured static key if the KMS has none).did(optional) String — DID to present as. Defaults to the first DID in the wallet's linked DID Store (or the configured static DID).
Example Response
{
"transmission_success": true,
"verifier_response": {
"status": "received",
"message": "Presentation received and is being processed."
}
}
WalletPresentResult fields are the same as the full-flow response — transmission_success, verifier_response, and (only when the verifier redirects) redirect_to / get_url / form_post_html.
Response Codes
200— Presentation built and submitted. If no supplied credential satisfied the DCQL query, this still returns200with"transmission_success": falseand the error inverifier_response.400— The presentation request could not be resolved.
Next Steps
- Present with the full flow — the per-format guides (SD-JWT VC, W3C VC, mDL) use the single-call
credentials/presentendpoint. - Receive a credential — Credential Receiving.
