Skip to Content
API and DashboardVerify Credentials

Verify credentials

Just like it says on the tin, verifiable credentials are verifiable by design. They contain all the proof needed to enable the owner to answer verification requests directly, without third parties.

Once issued the holder of a credential can accept incoming proof requests to present information to a verifying party. In the same way that you would show a loyalty card to prove you are part of a loyalty program in the physical world, verifiable credentials can be used to prove information in a digital interaction.

This guide contains everything you need to know about verifying credentials with Paradym. It will show you how to to:

  1. Set up a trusted entity.
  2. Create a presentation template
  3. Set up a webhook subscription.
  4. Use a presentation template to verify a credential
  5. View your ongoing verifications and their status

Let’s get started! 🚀

Set up a trusted entity

Create a trusted entity following the trusted entities docs.

Create a presentation template

Create a presentation template following the presentation template docs.

Set up a webhook subscription

To listen for the result of a verification, you can set up a webhook subscription. Whenever an event happens within Paradym, we will notify your application by sending the event to the URL of the webhook subscription.

We recommend listening one of the the following event sets, depending on the credential format you’re using:

  • didcomm.verification.data and didcomm.verification.failed (When verifying Anoncreds credentials)
  • openid4vc.verification.data and openid4vc.verification.failed (When verifying SD-JWT VC and mDoc credentials)

You can read more on configuring webhook subscriptions in the webhook documentation. This covers how to create a webhook subscription, as well as verifying a webhook (which is important to ensure the webhook was sent by Paradym).

💡

You can use a site such as Webhook.site  or Request Baskets  to get a temporary endpoint to receive events and play around with.

Note that webhook events contain sensitive information, and these services do not verify the HMAC of the events. You should not use these services for production webhooks.

Use a presentation template to verify a credential

The Paradym API currently supports issuing SD-JWT credentials and verifying SD-JWT and mDoc credentials over OpenID4VC, as well as issuance and verification of Anoncreds credentials over DIDComm. Read more about the different standards and protocols.

Based on the credential format you selected in the presentation template, you should either request a presentation over DIDComm or OpenID4VC:

💡

When using Paradym as your verifier, revocation status is checked automatically for SD-JWT VC, mDoc, and AnonCreds credentials. Learn more about revocation.

To verify an sd-jwt-vc and/or mDoc credential using OpenID4VCI based on the created template, we will make a POST request to https://api.paradym.id/v1/wallets/{walletId}/openid4vc/verification/request. For detailed information on the endpoint, refer to create OpenID4VC verification request  in the API reference.

In the payload below we create a verification request, based on the presentation template we created earlier. Make sure to update the presentationTemplateId to the ID of the presentation template.

Response encryption is enabled by default. You can change that by providing the requireResponseEncryption parameter. We recommend enabling response encryption.

Optionally, you can opt into the experimental redirect flow by providing a redirectUri together with a client-generated challenge. The wallet then sends the user-agent back to this URI after submitting the authorization response, so you can finish the interaction in the browser instead of relying on polling or webhooks. The result is withheld until your client exchanges the challenge for the single-use response_code Paradym puts on the redirect, which binds it to the device that started the flow. See the OpenID4VP redirect flow guide for the full round trip with a worked example.

🔒

The redirect-based same-device flow is the more secure option and is what we recommend when assurance matters. A cross-device flow (for example scanning a QR code with a wallet on another device) that does not use this redirect binding is prone to session fixation  / cross-device replay, because there is no binding between the device that started the flow and the device that presents the credential. See Assurance level and security model for how to choose the right setup (and note that DC API  support for secure cross-device flows is coming soon).

POST/v1/wallets/{walletId}/openid4vc/verification/requestOpen in Reference
{
  "presentationTemplateId": "clu129ps200043eghxvhz22m1",
  "requireResponseEncryption": true,
  "redirectUri": "https://your-app.example.com/verify/callback",
  "challenge": "RANDOM_128_BIT_VALUE"
}

The payload that is returned will look as follows:

{ "id": "clu6p64z80001c7xudvp2uvts", "createdVia": "api", "authorizationRequestUri": "https://paradym.id/invitation?request_uri=https%3A%2F%2Fparadym.id%2Foid4vp%2Fa744eefb-f7e6-444d-8a38-24c0136e893e%2Fauthorization-requests%2F215d6f1a-14fd-47bc-86c8-2b707124718d", "authorizationRequestQrUri": "https://paradym.id/invitation/eyJxIjoxfQ?request_uri=https%3A%2F%2Fparadym.id%2Foid4vp%2Fa744eefb-f7e6-444d-8a38-24c0136e893e%2Fauthorization-requests%2F215d6f1a-14fd-47bc-86c8-2b707124718d", "authorizationRequestDeeplinkUri": "openid4vp://?request_uri=https%3A%2F%2Fparadym.id%2Foid4vp%2Fa744eefb-f7e6-444d-8a38-24c0136e893e%2Fauthorization-requests%2F215d6f1a-14fd-47bc-86c8-2b707124718d&client_id=x509_san_dns%3Averifier.example.com", "authorizationRequestDeeplinkQrUri": "https://paradym.id/invitation/eyJxIjoxLCJkIjoxfQ?request_uri=https%3A%2F%2Fparadym.id%2Foid4vp%2Fa744eefb-f7e6-444d-8a38-24c0136e893e%2Fauthorization-requests%2F215d6f1a-14fd-47bc-86c8-2b707124718d", "status": "requested", "presentationTemplateId": "clu129ps200043eghxvhz22m1", "credentials": [], "createdAt": "2023-01-03T00:00:00.000Z", "updatedAt": "2023-01-03T00:00:00.000Z", "error": null }

The authorizationRequestUri renders the Paradym invitation page (with a QR code) that deeplinks into a supported wallet, and authorizationRequestQrUri returns that QR code directly as an image so you can embed it on your own website or application. A verification started by Verification with OpenID Connect has createdVia set to oauthAuthorization and no request URIs, since its request belongs to the hosted sign-in page.

To link straight into a wallet without the invitation page use authorizationRequestDeeplinkUri. This is a plain openid4vp:// deeplink you can place behind a button or render yourself. authorizationRequestDeeplinkQrUri returns a QR code image whose contents are that deeplink (instead of the universal link).

Authorizing a transaction

Experimental: this may still change before it is stable.

If the presentation template defines transactions, every verification request must provide the values for each of them in transactionData. Entries are matched to the template by type, and attributes must hold exactly the attributes declared by that transaction. Paradym builds the OpenID4VP transaction_data entry from the template, so nothing else is accepted.

POST/v1/wallets/{walletId}/openid4vc/verification/requestOpen in Reference
{
  "presentationTemplateId": "clu129ps200043eghxvhz22m1",
  "transactionData": [
    {
      "type": "example.payment",
      "attributes": {
        "amount": "45000",
        "currency": "ZAR",
        "payee": "Woolworths"
      }
    }
  ]
}

The verification returns the transaction data from creation on. encoded is the exact base64url entry from the authorization request, which is what the wallet hashes and signs, and decoded is its parsed form, with the attributes next to type as OpenID4VP defines it. The credential_ids field Paradym adds to it is the OpenID4VP envelope and can be ignored. Once the status is verified, the holder has signed every entry, and credentialId names the credential of the template that signed it. The signing credential is the one in credentials with that id. encoded and decoded follow the wallet’s verificationDataAccess setting the same way the presented credentials do, while type and credentialId remain.

A response that does not sign every entry fails the verification. The details of the error on the failed verification say whether the holder did not sign a hash of the transaction, signed a hash that does not match the requested transaction, or hashed it with an algorithm the request does not allow.

{ "id": "clu6p64z80001c7xudvp2uvts", "status": "verified", "presentationTemplateId": "clu129ps200043eghxvhz22m1", "transactionData": [ { "type": "example.payment", "encoded": "eyJ0eXBlIjoiZXhhbXBsZS5wYXltZW50Iiwi...", "decoded": { "type": "example.payment", "credential_ids": ["payment-account"], "amount": "45000", "currency": "ZAR", "payee": "Woolworths" }, "credentialId": "payment-account" } ], "credentials": [ { "exchange": "openid4vc", "format": "sd-jwt-vc", "isValid": true, "id": "payment-account", "type": "https://metadata.paradym.id/types/28dc88-PaymentAccount" } ] }

View the credential data from the verification

If you configured a webhook, your application should have received either an openid4vc.verification.data or an didcomm.verification.data event which includes the credential data received in the verification. In case the verification failed, it’s also possible your application received an openid4vc.verification.failed or a didcomm.verification.failed event.

The payload of the openId4VcVerification attribute in the openid4vc.verification.data event, or the didcommVerification attribute in the didcomm.verification.data event, matches the structure from the API.

Each credential in credentials has the id of the presentation template credential it was presented for, and a type. For mdoc and AnonCreds credentials, type is the doctype or schema id from the template. For an SD-JWT VC it is the vct of the presented credential, which can be a type that extends the type in the template. Both remain after the credential data is removed.

That’s it! 🚀

That concludes this guide. You now know how to:

  1. Create a presentation template
  2. Use a presentation template to verify a credential
  3. View your ongoing verifications and their status

If you have any suggestions, remarks or questions, join the Paradym Community  and let us know. Happy building!

Verification data access

The verification data access of a wallet decides how you receive the credential data from a verification, and how long Paradym keeps it, unless the flow releases the data itself (see below). Set it in the dashboard under Settings → Wallet , or with verificationDataAccess when you create or update a wallet through the API.

OptionHow you receive the dataHow long Paradym keeps it
webhook (default)In the openid4vc.verification.data or didcomm.verification.data webhook. Not through the API.Until the webhook is delivered to your endpoints, at most 15 minutes
onceThrough the “Retrieve verification session by id” endpoint, once.Until you retrieve it, at most 15 minutes
15minThrough the API, as often as you need.15 minutes
  • webhook delivers the data without an extra request. A webhook that fails is retried within 15 minutes, and is lost after that, so your endpoint has to be reachable. The data is in the webhook body: verify the signature, and make sure your server and any proxy in front of it don’t log request bodies.
  • once keeps the credential data out of webhooks. You get the openid4vc.verification.verified or didcomm.verification.verified webhook, and fetch the data with your API key. The first successful request removes the data, so if your application fails after that request, the data can’t be retrieved again.
  • 15min works like once, but you can retrieve the data again within 15 minutes, for example when processing failed.

We recommend webhook when your endpoint is reliably reachable. Use once if you prefer to fetch the data yourself or can’t receive webhooks, and 15min if you need to retrieve it more than once. With once, the data is only returned by the “Retrieve verification session by id” endpoints, not by the paginated “Retrieve verification sessions” endpoints.

The 1week and indefinite options are deprecated and can no longer be set. Wallets that still use one of them keep it until you switch, but creating or updating a wallet with 1week or indefinite is rejected.

Two flows release the data themselves, once, and this setting does not apply to them. Neither emits the openid4vc.verification.data webhook:

Last updated on