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.
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_uriis accepted as an alias. Omitting both fails422, since that's where the wallet sends the holder back.
- Name
requestUriMethod- Type
- string
- Description
get(default) orpost. Some mdoc DCQL queries requirepost— don't forcegeton an mdoc verification profile.
- Name
launchScheme- Type
- string
- Description
openid4vporhaip-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.
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.
In Intermediary mode (the hosted service), LutraID keeps no shared values: sharedClaims is null, and resultDelivery says whether the result reached your system. The values go to your result endpoint instead. See Receive check results.
What the request object contains
| Query language | DCQL only — presentation_definition is not implemented |
| Request delivery | always by reference (request_uri), signed as an RFC 9101 JWT |
client_id prefixes | x509_san_dns: and x509_hash: |
response_mode | direct_post and direct_post.jwt |
response_type | vp_token |
Errors: 400, 404, 422 — see Errors.