Request a Presentation
This is the integration itself: the calls your application makes to ask a holder for a credential, and what it does with the response. It covers steps 2, 4 and 6 of the verification flow — the parts that are yours to build. The wallet exchange in between is handled for you.
Requirements
- A running credential verification service. See Install the verification service.
- An API key, sent as a bearer token on every call below. See API keys.
- A verification query that already exists. You need its id, and a session cannot be started without one.
Start a session
Send a POST to /presentation/oid4vp/new-session. The body has one required field, verification_query_id:
curl -X POST 'https://verifier.example.org/presentation/oid4vp/new-session' \
-H 'Authorization: Bearer <your API key>' \
-H 'Content-Type: application/json' \
-d '{
"verification_query_id": "b841f44a-d09a-4605-9ef9-b5d99a987ef7"
}'
This request carries no personal information about the holder. It tells the service to prepare for an interaction, nothing more.
| Field | Required | What it does |
|---|---|---|
verification_query_id |
Yes | The verification query stating which credentials and claims to ask for. |
transaction_data |
No | Transaction data objects the holder must authorize as part of the presentation. Omit the field entirely — do not send an empty array — for a presentation that authorizes nothing beyond the verifier's identity. |
A missing or unknown verification_query_id returns 400 Bad Request.
Read the response
{
"session_id": "14eb99d399dda3264a0ad85b93b26eab5396ce17e50056968c2749049ebf6ec1",
"request_uri": "haip-vp://?request_uri=https%3A%2F%2Fverifier.example.org%2Fpresentation%2Foid4vp%2Fauthorization-request%2F14eb99d3...&client_id=x509_hash%3AVA7rWJULU4O_gDap9PCzgpjMyo8-R3dz7zaU-MRkaBs"
}
-
request_uriis what the wallet needs. Get it in front of the holder.It is a deep link, not a web address: the scheme opens the wallet, the nested URL is where it fetches the signed request from, and
client_ididentifies you by a hash of your signing certificate. Pass the whole value through unchanged — do not unwrap it, and do not build it yourself. -
session_ididentifies this verification for the rest of its life. Keep it: it appears in the result pushed to you, and it is what you pass to the status endpoint.
A verification query is reusable. Create it once, then start a session per interaction.
Get the request URI to the wallet
How you do this depends on where the holder is:
- On a desktop browser, render the
request_urias a QR code for the holder to scan with their phone. - On the same device as the wallet, present it as a link so tapping it opens the wallet directly.
The scheme of the URI determines which app opens it, and that scheme comes from uri_prefix on the verification
query —
it defaults to haip-vp://. Set it to match the wallets you are targeting; see
Verification queries.
The wallet has to reach the service
The request_uri is built from the verifier's baseUrl. A phone cannot resolve localhost, so for a real
wallet to complete the flow baseUrl must be an address that phone can reach. This is the single most common
reason a QR code scans and then nothing happens.
What happens next, without you
The wallet fetches the authorization request, validates that it comes from a verifier it trusts, shows the holder which claims are being requested, and on approval submits a presentation. The verification service checks the signatures on the presentation and on the credential inside it.
You do not implement any of this. If you want to see what goes over the wire, it is in OID4VP presentation protocol.
Receive the result
When the session completes, the verification service POSTs the result to the sessionCallback.targetUrl you
configured, with your bearerToken in the Authorization header:
curl -X POST 'https://example-application.com/verified-claims' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <the token you configured>' \
-d '{
"session_id": "14eb99d399dda3264a0ad85b93b26eab5396ce17e50056968c2749049ebf6ec1",
"verification_result": true,
"claims": {
"studentId": 200014762,
"dateOfBirth": "29-10-1969",
"enrollmentYear": "01-08-1988"
}
}'
Match it to the interaction you started using session_id. Setting this up — the endpoint, the token, and how
claim keys are named — is covered in Receive verified data.
What a successful result covers
See Receive verified data for exactly what
verification_result: true does and does not tell you.
Checking on a session
If you need to know where a session stands — a holder who has walked away, a timeout in your own UI — poll:
curl 'https://verifier.example.org/presentation/oid4vp/status/14eb99d3...' \
-H 'Authorization: Bearer <your API key>'
{
"status": "VERIFICATION_SUCCEEDED",
"error_message": null
}
status is VERIFICATION_STARTED, VERIFICATION_SUCCEEDED or VERIFICATION_FAILED. On a failure,
error_message says what went wrong — read it before anything else.
Use the callback as your primary path and this endpoint for reconciliation. Polling as the main mechanism means holding a session open in your own code for as long as the holder takes.
Presentations without a session
For a request that is not tied to one interaction — a poster in a reception area, a fixed terminal — you can
create a static presentation with POST /presentation/oid4vp/static-presentation. It returns a
static_presentation_id and a request_uri that stays valid until you withdraw it, so the same QR code serves
every holder. See the verifier API reference.
What's Next
- Receive verified data: Configure the callback in full.
- Verification queries: Change what you are asking for.
- Verifier API reference: Every endpoint and field.