Submit a presentation
Learn how to present credentials.
The presentation workflow has four steps:
Handle invitation
Get proof request
Get presentation definition
Respond to the request
This page explains each step and how to complete them. Samples for the Desk API (for organizational wallets) and for the SDK (for mobile devices) are provided.
Presentation workflow
Handle invitation
The verifier will share an invitation URL, often encoded as a QR code. The URL is generated from the verification protocol; your system must support the verification protocol used to be able to parse the offer correctly.
Related guide: Configure OID4VP for wallets
Use the "Handle invitation" endpoint to parse the invitation URL:
- API
- React Native
curl -L -X POST '/api/interaction/v1/handle-invitation' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"url": "https://example.com/invitation"
}'
const invitationUrl = 'https://example.com/invitation' // Get invitation URL from QR code or deep link
const invitation = await core.handleInvitation(invitationUrl, organisationId),
Transport array
If you have multiple transport protocols configured you can use this array to override the default protocol. For wallets, this is generally only the case for mobile devices.
Get proof request
- API
- React Native
curl -L '/api/proof-request/v1/{proofRequestId}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
const { interactionId, proofId } = invitation;
const proofDetails = await core.getProof(proofId);
Get presentation definition
This function takes a proof request and searches your held credentials, returning those which are applicable to the request.
- API
- React Native
curl -L '/api/proof-request/v1/{proofRequestId}/presentation-definition' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
const presentationDefinition = await core.getPresentationDefinition(proofId);
// refresh revocation status of the applicable credentials
const credentialIds = new Set<string>(
definition.requestGroups.flatMap(({ requestedCredentials }) =>
requestedCredentials.flatMap(
({ applicableCredentials }) => applicableCredentials,
),
),
);
await core.checkRevocation(Array.from(credentialIds));
Submit (or reject) proof
- API
- React Native
curl -L '/api/interaction/v1/presentation-submit' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"identifierId": "{{UUID-OF-IDENTIFIER}}",
"interactionId": "{{FROM-HANDLE-INVITATION}}",
"submitCredentials": {
"input_0": {
"credentialId": "{{CHOOSE-A-CREDENTIAL}}",
"submitClaims": [
"{{UUID-OF-CLAIM}}",
"{{UUID-OF-OTHER-CLAIM}}"
]
},
"input_1": {
"credentialId": "{{SECOND-CREDENTIAL}}",
"submitClaims": [
"{{UUID-OF-CLAIM}}"
]
},
}
}'
submitCredentials
is a selection of the credentials and claims returned
from presentation definition. The input_{{#}}
value comes from
requestGroups.requestedCredentials.id
for the applicable credential.
const credentials: Record<
PresentationDefinitionRequestedCredential['id'],
PresentationSubmitCredentialRequest | undefined
> = {};
presentationDefinition.requestGroups.forEach((group) =>
group.requestedCredentials.forEach((credential) => {
const credentialId =
allCredentials.find(
({ id, state }) =>
state === CredentialStateEnum.ACCEPTED &&
credential.applicableCredentials.includes(id),
)?.id ?? credential.applicableCredentials[0];
if (!credentialId) {
credentials[credential.id] = undefined;
return;
}
const requiredClaims = credential.fields
.filter((field) => field.required)
.map((field) => field.id);
credentials[credential.id] = {
credentialId,
submitClaims: requiredClaims,
};
}),
);
await core.holderSubmitProof(interactionId, credentials, identifierId, keyId);
Reject proof
- API
- React Native
curl -L -X POST '/api/interaction/v1/presentation-reject' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"interactionId":"{{FROM-HANDLE-INVITATION}}"
}'
await core.holderRejectProof(interactionId);