Run Credential Verify using a callback

This tutorial will allow you to perform a Credential Verify flow and obtain important information from the presentation including all the claims, the subject identifiers and whether the credential has been fully verified or not.

This guide intends to show the raw workings of the presentation request during a Verify flow, some orchestration of events is required and the exact integration patterns with your infrastructure will need to be carefully designed.


You need access to the MATTR Platform APIs. If you’re experiencing any difficulties, contact us.

In order to complete this tutorial you will need the following:

Sample app

If you prefer a hands-on approach to getting this working the verify credentials using presentation request callbacks tutorial will step you through creating a simple Node.js Express server to orchestrate many of the steps below.

Invoke the presentation request

The create a presentation request should be called every time a presentation is requested.

Copy to clipboard.
2  "challenge": "GW8FGpP6jhFrl37yQZIM6w",
3  "did": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
4  "templateId": "f95e71b0-9bdf-11ea-aec9-3b5c35fc28c8",
5  "callbackUrl": "",
6  "expiresTime": 1638836401000

challenge is a unique identifier per transaction, this can be used as an interaction id. However, because the challenge is a part of the signed presentation request and it is not encrypted, it should not be considered to be secure.

The did must be available on the tenant and have a key type that is able to be used for messaging (e.g. of a ed25519 key type).

Provide the ID of the templateId that exists on the tenant, this will define the response of the Presentation Request.

The callbackUrl is the exact path that you want the Platform to message once it has received and processed the presentation from the mobile wallet holder, it will be in the form of a JSON body.

The callback URL is not present directly in the signed presentation request JWS. We recommend to include a secret unique identifier in the URL to create a unique secured callback URL for each request which would make it hard to guess. Additionally, we also recommend for the callback endpoint to always respond with 404 header to unsuccessful calls.

expiresTime is optional, with a default of 5 mins, once the time (in Unix Epoch time in milliseconds) is reached the Presentation corresponding to the challenge will not be accepted by the Platform. For the purpose of testing you may wish to extend this out.


Copy to clipboard.
2  "id": "38d9ca70-8b61-48c3-9efc-b67d97410743",
3  "callbackUrl": "",
4  "request": {
5    "id": "38d9ca70-8b61-48c3-9efc-b67d97410743",
6    "type": "",
7    "from": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
8    "created_time": 1607300517863,
9    "expires_time": 1638836401000,
10    "reply_url": "",
11    "reply_to": [
12      "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5"
13    ],
14    "body": {
15      "id": "5770506c-9ce9-4d54-b295-68ad2bb12a4c",
16      "name": "certificate-presentation",
17      "domain": "",
18      "query": [
19        {
20          "type": "QueryByExample",
21          "credentialQuery": [
22            {
23              "reason": "Please provide your certificate.",
24              "example": {
25                "type": "CourseCredential",
26                "@context": [
27                  ""
28                ],
29                "trustedIssuer": [
30                  {
31                    "issuer": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
32                    "required": true
33                  }
34                ]
35              },
36              "required": true
37            }
38          ]
39        }
40      ],
41      "challenge": "GW8FGpP6jhFrl37yQZIM6w"
42    }
43  }

The id and callback are used on the Platform, the body of the request payload is required by the Mobile Wallet client.

Sign and encode the presentation request body

In order for the mobile wallet to have assurance about the presentation request, it must be signed using the authentication key from the Verifier DID.

The Create a JWS with a DID endpoint is available for this purpose.

First find the authentication key from the same Verifier DID using in the presentation request using the Resolve a DID endpoint. This also known as a DIDUrl.


Copy to clipboard.

Response snippet from the 

Copy to clipboard.
2  "didDocument": {
3    "@context": "",
4    "id": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
5    "authentication": [
6      "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5"
7    ]
8  }}


Use the didUrl and copy the body from the request field from the Presentation Request response to the body of the payload.

Copy to clipboard.
2    "didUrl": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
3    "payload": {
4    "id": "38d9ca70-8b61-48c3-9efc-b67d97410743",
5    "type": "",
6    "from": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
7    "created_time": 1607300517863,
8    "expires_time": 1638836401000,
9    "reply_url": "",
10    "reply_to": [
11      "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5"
12    ],
13    "body": {
14      "id": "5770506c-9ce9-4d54-b295-68ad2bb12a4c",
15      "name": "certificate-presentation",
16      "domain": "",
17      "query": [
18        {
19          "type": "QueryByExample",
20          "credentialQuery": [
21            {
22              "reason": "Please provide your certificate.",
23              "example": {
24                "type": "CourseCredential",
25                "@context": [
26                  ""
27                ],
28                "trustedIssuer": [
29                  {
30                    "issuer": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
31                    "required": true
32                  }
33                ]
34              },
35              "required": true
36            }
37          ]
38        }
39      ],
40      "challenge": "GW8FGpP6jhFrl37yQZIM6w"
41    }
42  }


The response is a JWS

Copy to clipboard.

Send to the mobile wallet App

The mobile wallet app has three main methods for reading presentation requests

  1. Scanning a QR code

  2. Opening a deep link

  3. Send a notification

Both scanning a QR code and the deep link methods require the URL to be constructed in this format, where {jws} is the entire JWS from the Create a JWS with a DID endpoint.


The notification method requires knowledge of the subject DID, which may be obtained from a prior verification request or using a DID auth method.

Scanning a QR code

QR codes are a useful way to transfer information from desktop application to mobile apps using the phone’s camera.

Given the nature of the presentation request and resulting size of the JWS, pushing that much information into a QR code can be problematic for the mobile device reading the QR code.

Using a QR code API service results in something like this:

Copy to clipboard.

You might find a high-end phone with decent camera will read the QR code but many lower-end phones will never be able to.

Redirect URLs

For this reason the MATTR Mobile Wallet app will follow a 302 redirect if it is constructed in the way show below, this allows for the QR code code to be much smaller in size and therefore faster and easier to read by a wide range of devices.

Copy to clipboard.

The redirectUrl will need to return a 302 with the location of your tenant URL with the query param of ?request={jws}

Check out the “verify-callback-express” app as part of the MATTR Sample Apps to see a working example of this method.

The MATTR Wallet is set up to register global.mattr.wallet as a scheme and requires accept/ in the path.

In order to provide the didcomm://.. URL via a deep link, we need a further step to base64 encode that URL before being appended to the deep link URL.

Either programmatically or use or an online tool to base64 with URL encoding to encode the constructed URL.

Copy to clipboard.
1base64url "didcomm://"

Add this base64url string to the Deeplink URL


Then send this URL inside a standard message that can be opened on the mobile device (e.g. in an email or Slack message etc).

Send notification

All notification sent to subject holder should be encrypted to protect the privacy of the MATTR Wallet users.
Use the Presentation request response with the Encrypt message endpoint with the known subject DID as the recipientDidUrl.
Send a message can determine the service endpoint of the known Subject DID which the encrypted payload will be forwarded to, which will result in a push notification to the MATTR Wallet mobile app user.

Further details can be found at the messaging overview page.

Action the request

Fulfilling the request (scan QR, click on deeplink or open notification) will result in the MATTR Mobile Wallet opening, you’ll need to authenticate, then the ‘Verification request’ screen should open.

The presentation is sent in this format

Copy to clipboard.
2  "presentation": {
3    "@context": [
4      ""
5    ],
6    "type": [
7      "VerifiablePresentation"
8    ],
9    "verifiableCredential": [
10      {
11    "@context": [
12      "",
13      ""
14    ],
15    "type": [
16      "VerifiableCredential",
17      "CourseCredential"
18    ],
19    "issuer": {
20      "id": "did:key:z6MkndAHigYrXNpape7jgaC7jHiWwxzB3chuKUGXJg2b5RSj",
21      "name": "tenant"
22    },
23    "issuanceDate": "2020-08-30T23:24:54.876Z",
24    "credentialSubject": {
25      "id": "did:key:z6MkfxQU7dy8eKxyHpG267FV23agZQu9zmokd8BprepfHALi",
26      "givenName": "Chris",
27      "familyName": "Shin",
28      "educationalCredentialAwarded": "Certificate Name"
29    },
30    "proof": {
31      "type": "Ed25519Signature2018",
32      "created": "2020-08-30T23:24:55Z",
33      "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..BSHdalZrYml0slwgAXFVF5uAcg2DbPMfwatturKs8TnuxBxylQDnS3JkORORVmO73Ruh7h8KJvVvHO4pE5NsCQ",
34      "proofPurpose": "assertionMethod",
35      "verificationMethod": "did:key:z6MkndAHigYrXNpape7jgaC7jHiWwxzB3chuKUGXJg2b5RSj#z6MkndAHigYrXNpape7jgaC7jHiWwxzB3chuKUGXJg2b5RSj"
36    }
37  }
38    ],
39    "id": "335032c1-0f8b-405e-9740-44928237e06c",
40    "holder": "did:key:z6MksMYtQSvQsfk3KZy7vxBzjcBJhx1i7TaLuvJePPPDEAjH",
41    "proof": {
42      "type": "Ed25519Signature2018",
43      "created": "2020-12-08T20:12:18Z",
44      "challenge": "e1b35ae0-9e0e-11ea-9bbf-a387b27c9e60",
45      "domain": "global.mattr.wallet",
46      "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..MfVEy102c4oZA_G98G6HUxEmfXFLEIWbX9C36zF_cA-D8EF8jRMQWc5eJpMn3hgLm2xhPw8GzTrnwPHa37L-DA",
47      "proofPurpose": "authentication",
48      "verificationMethod": "did:key:z6MksMYtQSvQsfk3KZy7vxBzjcBJhx1i7TaLuvJePPPDEAjH#z6MksMYtQSvQsfk3KZy7vxBzjcBJhx1i7TaLuvJePPPDEAjH"
49    }
50  }

Receiving the credential presentation callback

Once a valid presentation is sent by the mobile app, the Platform will perform some checks to ensure the validity of the presentation:

  • Issuer DID of each credential can be resolved

  • JSON-LD context is valid for subject claims

  • Proof is valid & the credential has not been tampered with

  • If present check any credential against its RevocationList2020 status

These checks will inform the verified boolean and where the contents is mapped to these fields and presented to the callback URL as an application/json body.

As recommended, the callback URL should contain a secret unique identifier provided by your application and your application logic should at this point verify its authenticity.

Copy to clipboard.
2  "presentationType": "QueryByExample",
3  "challengeId": "GW8FGpP6jhFrl37yQZIM6w",
4  "claims": {
5      "id": "did:key:z6MkfxQU7dy8eKxyHpG267FV23agZQu9zmokd8BprepfHALi",
6      "givenName": "Chris",
7      "familyName": "Shin",
8      "educationalCredentialAwarded": "Certificate Name"
9  },
10  "verified": true,
11  "holder": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d"


The steps outlined above give a detail explanation of the steps and some of the challenges involved in setting up an end to end experience. For a more hands-on step through, checkout the verify credentials using presentation request callbacks sample app, alternatively verify using the OIDC Bridge is another approach to obtaining verified credential data.