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 and optional disclosures
  • Reject a presentation request without submitting a VP token
  • Present credentials that aren't stored in the wallet (supplied inline)
  • Inspect intermediate results with the advanced resolve/match endpoints

For holder consent UIs, start with preview: one call resolves and validates the request, matches wallet credentials, and returns verifier metadata plus selectable credentials and claim options. After the user chooses, call build-vp-token, then send-response. To decline, call reject with the same requestUrl. Resolve and match remain available when you already have an authorization request or credentials outside the wallet.

All endpoints live under /v2/{target}/wallet-service-api/.

1. Preview Presentation  →  2. Build VP Token  →  3. Send Response
                              ↘ Reject (with the same requestUrl)

Preview is stateless: the wallet does not retain a preview handle. Keep the original requestUrl and pass it to build-vp-token, send-response, and reject. The wallet re-resolves and revalidates that URL on each sensitive step. The preview authorizationRequest is for display / technical details only.

Any verifier trust configuration you've supplied to the wallet — X.509 trust anchors, verifier-attestation issuers, and pre-registered clients — is applied consistently across preview, build-vp-token, send-response, and reject, so the isolated flow validates the verifier the same way the full flow does.


Step 1 — Preview Presentation

Resolve and validate the OpenID4VP request, match the wallet's stored credentials, and return everything a consent UI needs.

CURL

Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/preview

Example Request

curl -X POST \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/preview' \
  -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

  • requestUrl String — The OID4VP authorization request URL.
  • keyReference (optional) String — Full resource path of the signing key used for wallet capability advertisement and request validation during preview. Defaults to the first key in the wallet's linked KMS (or the service's configured static key). The Ready response echoes back the effective library keyId — pass the same key selection to Step 2 — Build VP Token so preview and build agree on key capabilities.
  • key (optional) Object — Inline serialized key ({ "type": "jwk", "jwk": { … } }). Takes precedence over keyReference.

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",
          "claims": [
            { "path": ["given_name"] },
            { "path": ["family_name"] }
          ]
        }
      ]
    }
  },
  "valid": true,
  "keyId": "waltid.tenant1.kms.wallet_key",
  "clientId": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0",
  "verifier": {
    "display": {
      "name": "Example Verifier",
      "locale": "en-US",
      "logoUri": "https://verifier.example.org/logo.png"
    },
    "clientUri": "https://verifier.example.org",
    "policyUri": null,
    "termsOfServiceUri": null
  },
  "responseUri": "https://verifier.example.org/response",
  "state": "af0ifjsldkj",
  "nonce": "n-0S6_WzA2Mj",
  "responseEncryption": {
    "required": false
  },
  "transactionData": [],
  "credentialOptions": [
    {
      "queryId": "identity_credential",
      "credentialId": "550e8400-e29b-41d4-a716-446655440000",
      "multiple": false,
      "format": "dc+sd-jwt",
      "issuer": "https://issuer.example.org",
      "subject": "did:jwk:…",
      "label": null,
      "credentialData": {
        "given_name": "Ada",
        "family_name": "Lovelace",
        "vct": "https://issuer.example.org/identity_credential"
      },
      "disclosures": [
        {
          "path": "$.given_name",
          "name": "given_name",
          "value": "Ada",
          "selectivelyDisclosable": true,
          "required": true,
          "selectable": false
        },
        {
          "path": "$.family_name",
          "name": "family_name",
          "value": "Lovelace",
          "selectivelyDisclosable": true,
          "required": false,
          "selectable": true
        }
      ]
    }
  ],
  "credentialRequirements": [],
  "error": null
}
  • authorizationRequest — Resolved authorization request for display / technical details only.
  • valid — Whether the request passed wallet validation. When false, inspect error and optionally reject.
  • keyId (Ready only) — Resolved signing key path the preview used for wallet capability advertisement and validation. Pass the same key selection back to Step 2 — Build VP Token via keyReference (or key) so preview and build stay consistent.
  • clientId — The verifier's client identifier.
  • verifier (optional) — Verifier display metadata (display with name / locale / logoUri, plus clientUri, policyUri, termsOfServiceUri). Supplied by the verifier — not evidence of trust on its own.
  • responseUri / state / nonce — Request metadata projected for the UI.
  • responseEncryption — Whether response encryption is required and related algorithms/keys when present.
  • transactionData — Decoded transaction_data items (type, credentialQueryIds, rawJson, details).
  • credentialOptions — Wallet credentials that satisfy a DCQL credential query. Each entry includes queryId, credentialId, format/issuer/subject/label, credentialData, and disclosures (path, name, value, selectivelyDisclosable, required, selectable).
  • credentialRequirements — When DCQL credential sets apply: each item has options (query-id combinations); at least one combination must be satisfied.
  • error (optional) — When valid is false: code and optional message.

Response Codes

  • 200 — Preview completed (check valid).
  • 400 — The request URL/object is malformed and cannot be parsed.

Step 2 — Build VP Token

After the user consents, build and sign a vp_token from the selected stored credentials and optional claim disclosures. The wallet re-resolves and revalidates requestUrl first, then uses that fresh authorization request for DCQL matching and signing. This step does not submit anything to the verifier.

CURL

Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/build-vp-token

Example Request (after preview)

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 '{
  "requestUrl": "openid4vp://authorize?client_id=did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0&request_uri=https://verifier.example.org/request/abc123",
  "selectedCredentialOptions": [
    {
      "queryId": "identity_credential",
      "credentialId": "550e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "selectedDisclosureOptions": [
    {
      "queryId": "identity_credential",
      "credentialId": "550e8400-e29b-41d4-a716-446655440000",
      "path": "$.family_name"
    }
  ]
}'

Example Request (legacy IDs after match)

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 '{
  "requestUrl": "openid4vp://authorize?client_id=did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0&request_uri=https://verifier.example.org/request/abc123",
  "selectedCredentialIds": {
    "identity_credential": ["550e8400-e29b-41d4-a716-446655440000"]
  }
}'

Body Parameters

  • requestUrl String — The same OID4VP authorization request URL used for Step 1 (or Resolve VP Request). Re-resolved and revalidated on every call.
  • selectedCredentialOptions (optional) Array — Preferred for consent UIs. List of { queryId, credentialId } from credentialOptions. When non-empty, takes precedence over selectedCredentialIds.
  • selectedDisclosureOptions (optional) Array — Selectively disclosable claim paths to include: { queryId, credentialId, path }. Omit to keep the default disclosure set for the selection. When provided as an empty list, optional selectively disclosable claims are not added beyond what the protocol still requires.
  • selectedCredentialIds (optional) Object — Legacy map of DCQL query ID to wallet credential IDs (from match). Used when selectedCredentialOptions is empty.
  • key (optional) Object — Inline key used to sign the VP token. Takes precedence over keyReference.
  • keyReference (optional) String — Full resource path of the key to sign the VP token. Defaults to the first key in the wallet's linked KMS or the service's configured static key. When you started the flow through Step 1 — Preview with a specific keyReference, pass the same value here so preview and build use the same signing key. Referenced keys stay in the KMS, so PKCS#11 / HSM backends work in this flow.
  • 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 — Serialized vp_token JSON 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 3 — Send Response

Send the built VP token to the verifier. The wallet re-resolves and revalidates requestUrl first, then transmits according to the response mode / URI / encryption from that freshly resolved request.

CURL

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 '{
  "requestUrl": "openid4vp://authorize?client_id=did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0&request_uri=https://verifier.example.org/request/abc123",
  "vpToken": "{\"identity_credential\":\"eyJhbGciOiJFUzI1NiIs...\"}",
  "idToken": null
}'

Body Parameters

  • requestUrl String — The same OID4VP authorization request URL used for Step 1. Re-resolved and revalidated on every call.
  • vpToken String — The VP token returned by Step 2.
  • idToken (optional) String — The ID token returned by Step 2, 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.

Step 4 — Reject Presentation

Decline the presentation request. The wallet re-resolves the original requestUrl and returns an OpenID4VP error response to the verifier when a response channel is available. No prior preview is required.

CURL

Endpoint: POST /v2/{target}/wallet-service-api/credentials/present/reject

Example Request

curl -X POST \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{org}.{tenant}.{wallet-id}/wallet-service-api/credentials/present/reject' \
  -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",
  "errorCode": "access_denied",
  "errorDescription": "User declined"
}'

Body Parameters

  • requestUrl String — The same OID4VP authorization request URL used for preview.
  • errorCode (optional) String — OpenID4VP error code. Defaults to access_denied.
  • errorDescription (optional) String — Human-readable description sent to the verifier.

Example Response

Returns the same WalletPresentResult shape as Step 3 — Send Response.

Response Codes

  • 200 — Rejection transmitted (or redirect URL produced).
  • 400 — The request could not be resolved or no response channel is available.

Advanced: Resolve VP Request

Parse the verifier's authorization request without matching credentials. Prefer Step 1 — Preview for consent UIs.

CURL

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

  • requestUrl String — 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 — Resolved authorization request for inspection / display.
  • 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 a request_uri.
  • dcqlQuery — The verifier's DCQL query. Pass this to Match Credentials from Store or Match Inline Credentials.

Response Codes

  • 200 — Request resolved.
  • 400 — The request URL/object is malformed and cannot be parsed.
  • 500 — The request_uri could not be fetched (e.g. the verifier's request endpoint returned 404).

Advanced: Match Credentials from Store

DCQL-match the dcqlQuery returned by Resolve against the wallet's own stored credentials. For a full consent preview (verifier display + claim options), use Step 1 — Preview instead.

CURL

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

  • dcqlQuery Object — The DCQL query returned by Resolve.

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 2 via selectedCredentialIds.

Response Codes

  • 200 — Matching completed.
  • 400 — Invalid DCQL query.

Advanced: Match Inline Credentials

DCQL-match against credentials you supply inline — useful to preview matches for credentials that aren't stored in the wallet.

CURL

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

  • dcqlQuery Object — The DCQL query from the verifier's authorization request.
  • credentials Array — Credentials to match against, each with an id (a caller-assigned label) and a credential object. The credential's shape depends on the credential format (its type field is vc-sd_jwt, vc-w3c_2, or vc_mdocs) — it is not a { format, rawCredential } pair. The easiest way to obtain it is to copy the credential field from the wallet's GET /v2/{target}/wallet-service-api/credentials/{credentialId} response (abbreviated with above).

Example Response

Same structure as Match Credentials from Store.

Response Codes

  • 200 — Matching completed.
  • 400 — Invalid DCQL query or credentials.

Present Inline Credentials

Use the isolated endpoint below when the credentials are supplied inline rather than loaded from the wallet's stores.

CURL

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

  • requestUrl String — The OID4VP authorization request URL.
  • credentials Array — The credentials to present, each with an id (a caller-assigned label) and a credential object. The credential's shape depends on the credential format (its type field is vc-sd_jwt, vc-w3c_2, or vc_mdocs) — it is not a { format, rawCredential } pair. Copy it from the credential field of the wallet's GET /v2/{target}/wallet-service-api/credentials/{credentialId} response (abbreviated with above).
  • keyReference (optional) String — Full resource path 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).
  • key (optional) Object — Inline serialized key ({ "type": "jwk", "jwk": { … } }). Takes precedence over keyReference.
  • 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 responsetransmission_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 returns 200 with "transmission_success": false and the error in verifier_response.
  • 400 — The presentation request could not be resolved.

Next Steps

Last updated on August 18, 2026