Verify a credential

One call turns a verification profile into a wallet launch URI. The wallet returns a presentation, LutraID validates it, and you read the result back by ID.

POST/verifiers/{verifierId}/initiate-verification

Initiate a verification

Request

curl "$LUTRAID_API/v1/organizations/$ORG/verifiers/$VERIFIER/initiate-verification" \
  -H "X-API-Key: $LUTRAID_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "verificationProfileId": "b21c…",
    "metadata": { "redirectUri": "https://your-app.example/verify/done" }
  }'
{
  "verificationUri": "openid4vp://?client_id=x509_san_dns%3A…&request_uri=https%3A%2F%2F…",
  "verificationId": "0f7a…"
}
  • Name
    verificationProfileId
    Type
    uuid
    Description

    Required. Carries the DCQL query and the assurance policy.

  • Name
    metadata.redirectUri
    Type
    uri
    Description

    Required — redirect_uri is accepted as an alias. Omitting both fails 422, since that's where the wallet sends the holder back.

  • Name
    requestUriMethod
    Type
    string
    Description

    get (default) or post. Some mdoc DCQL queries require post — don't force get on an mdoc verification profile.

  • Name
    launchScheme
    Type
    string
    Description

    openid4vp or haip-vp. Omit to use the profile default.

A request expires 15 minutes after creation. Present verificationUri to the holder as a QR code or deep link, the same as a credential offer.

Reading the redirect

When the wallet finishes, it redirects the holder's browser to your redirectUri with #response_code=… appended as a URL fragment — not a query parameter. Fragments aren't sent to your server, so read it client-side and pair it with your own verificationId tracking.

GET/verifiers/{verifierId}/verifications/{verificationId}

Read the result

Request

curl "$LUTRAID_API/v1/organizations/$ORG/verifiers/$VERIFIER/verifications/$VERIFICATION" \
  -H "X-API-Key: $LUTRAID_KEY"
{
  "id": "…",
  "status": "success",
  "failureReason": null,
  "sharedClaims": {
    "credential_results": [
      {
        "credential_query_id": "…",
        "credential_format": "dc+sd-jwt",
        "valid": true,
        "validated_claims": { }
      }
    ]
  },
  "responseCode": "…",
  "expiresAt": "2026-09-10T12:15:00.000Z",
  "completedAt": "2026-09-10T12:00:30.000Z"
}

status is initiated, success, failure or expired. On success, sharedClaims.credential_results[] carries one entry per requested credential; on failure read failureReason. Poll until status leaves initiated or expiresAt passes.

sharedClaims's exact shape follows the verification profile's DCQL query and is intentionally returned as-is rather than fully typed — treat unfamiliar fields under it as additive.

What the request object contains

Query languageDCQL only — presentation_definition is not implemented
Request deliveryalways by reference (request_uri), signed as an RFC 9101 JWT
client_id prefixesx509_san_dns: and x509_hash:
response_modedirect_post and direct_post.jwt
response_typevp_token

Errors: 400, 404, 422 — see Errors.

Was this page helpful?