Custom Flow

The full-flow endpoint (credentials/present) matches, signs, and submits in one call. The custom 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 → nonce, clientId, responseUri
2. Match Credentials      →  DCQL match (from store or inline) → matched credential IDs
   ── show user consent screen ──
3. Present Credentials    →  sign and submit → WalletPresentResult

Step 1 — Resolve VP Request

Parse the verifier's authorization request to extract its metadata.

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

  • Presentation request (required) — provide exactly one of:
    • requestUrl String — The OID4VP authorization request URL.
    • requestObject Object — The authorization request as JSON, instead of a URL.

Example Response

{
  "nonce": "n-0S6_WzA2Mj",
  "clientId": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5In0",
  "responseUri": "https://verifier.example.org/response",
  "hasRequestUri": true
}
  • 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.

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).

resolve-request returns the request's metadata but not the DCQL query itself. To match credentials in the next step you need the query — see Where the DCQL Query Comes From below.


Where the DCQL Query Comes From

The next step matches credentials against a DCQL query — the machine-readable statement of what the verifier wants. The DCQL query travels inside the verifier's authorization request as the dcql_query parameter, and the wallet reads it out of that request.

Given only the openid4vp:// request URL, extract the query from the request object:

  • Inline request — the query is the dcql_query parameter in the URL itself. Read it directly from the query string.
  • request_uri request (the common case) — the URL carries a request_uri pointing to the request object rather than the query. Fetch that URL and read the dcql_query claim out of what it returns.
    • By default, Verifier2 returns a plain JSON object (Content-Type: application/json) — read dcql_query directly from it. This is what you get unless the verification session was created with signed_request: true.
    • If the session set signed_request: true, the request object is instead a signed JWT (Content-Type: application/oauth-authz-req+jwt, fetched via POST) — dcql_query is in its payload, so decode the JWT payload to read it.

The wallet has no endpoint that hands you the query directly — resolve-request deliberately returns only the request metadata (nonce, client, response URI), so you extract dcql_query from the request object yourself as above.


Step 2a — Match Credentials from Store

DCQL-match against the wallet's own stored credentials. This is the recommended matching step when credentials live in the wallet's Credential Store.

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 from the verifier's authorization request. See DCQL Query Structure.

Example Response

{
  "matchedQueryIds": ["identity_credential"],
  "matchCount": 1,
  "matchedCredentialIds": {
    "identity_credential": ["0"]
  }
}
  • 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 identifiers of the matching credentials.

For the from-store variant these identifiers are positional indices ("0", "1", …) into the wallet's matched-credential set for this call — not the stored credential UUIDs. They currently cannot be used directly to fetch or select a specific stored credential. (The inline match-credentials variant instead echoes back the id you supplied.) If you need to present a specific stored credential, drive the full flow with a DCQL query narrow enough to match only that credential.

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.

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 Step 2a.

Response Codes

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

Step 3 — Present

After the user consents, complete the presentation. Use the full-flow endpoint (credentials/present) to present from the wallet's stores, or the isolated endpoint below to present credentials supplied inline.

Present Inline Credentials

Present credentials that are supplied inline rather than matched 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

  • Presentation request (required) — provide exactly one of:
    • requestUrl String — The OID4VP authorization request URL.
    • requestObject Object — The authorization request as JSON, instead of a 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).
  • 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 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 July 13, 2026