light-mode-image
Learn
Credential offer

Credential offer

Overview

Once all the required workflow components are configured, you can initiate an OID4VCI workflow by creating a Credential offer.

This is achieved by making an API request to a specific MATTR VII endpoint. The request defines the credential configurations that will be used as well as additional request parameters to support the issuance workflow.

MATTR VII responds with an offer URI, which can be shared with intended holders as a QR code, deep-link or wallet notification. This enables the digital wallet to present the offer to the holder and request for consent to initiate the issuance workflow.

MATTR VII exposes two different endpoints to create a credential offer, depending on the authentication flow you are using:

  • Authorization Code flow: The returned offer URI will be used by the wallet to redirect the holder to the configured Authentication provider for authentication. Once the holder is authenticated, they are redirected back to the issuance workflow.
  • Pre-authorized Code flow: The returned offer URI already includes the pre-authorized code, which is used to authenticate the holder. This code is passed to the wallet and used to request an access token from the issuer’s token endpoint. The access token is then used to request the credential from the issuance endpoint.

The OID4VCI specification supports issuing multiple credentials in a single workflow, so you can reference multiple credential configurations in the same offer. When a multi-format credential offer is created, the generated URI offer is used to issue all the credential formats in a single workflow.

Controlling how credentials are claimed

When generating a credential offer, you can influence which wallet applications are able to claim the offer and how the user experience flows. This involves both user experience considerations and security, privacy, or commercial requirements where you want a specific app to claim the credential.

The mechanism for controlling this is through URI schemes. When you generate a credential offer (for either Authorization Code or Pre-authorized Code flows), you apply a URI scheme that determines which apps can handle the offer and what the user experience will be.

Understanding URI schemes

A URI scheme is the protocol part at the beginning of a URI (such as https://, mailto:, or custom schemes like openid-credential-offer://). The URI scheme you choose affects:

  • Which apps can handle the offer - Some schemes allow any compatible app, while others ensure only your specific app can handle the offer.
  • User experience - How smoothly users can claim credentials and whether they see app selection prompts.
  • Security and control - Your level of control over the claiming process and ability to prevent unintended apps from intercepting offers.

There are three main URI scheme types available:

  1. Standard OpenID4VCI custom scheme (openid-credential-offer://) - The baseline scheme for maximum interoperability.
  2. Private-use URI scheme (com.example.wallet://) - A unique custom scheme for better targeting.
  3. Claimed HTTPS scheme (https://example.com/wallet/...) - Domain-verified links for maximum security and control.

Choosing the right URI scheme

Select a URI scheme based on your requirements:

RequirementRecommended Scheme
Maximum interoperability - work with any compatible walletStandard OpenID4VCI scheme
Target a specific wallet without domain verificationPrivate-use URI scheme
Ensure only your specific app can handle offersClaimed HTTPS scheme
Production deployment with strict security requirementsClaimed HTTPS scheme
Development and testingStandard OpenID4VCI scheme

Implementing URI schemes

The following sections show you how to implement each URI scheme type as an issuer.

Standard OpenID4VCI custom scheme

The OpenID4VCI specification defines the openid-credential-offer:// scheme as the baseline for credential offers. This scheme provides maximum interoperability, allowing any wallet app that supports the standard to claim your offers.

When to use:

  • You want to support multiple wallet applications in your ecosystem.
  • Interoperability is more important than controlling which specific app handles the offer.
  • You're developing or testing and need a simple, standards-compliant approach.

How it works:

MATTR VII generates credential offers using this scheme by default. No additional configuration is required from the issuer.

Implementation:

  1. Create a credential offer using the MATTR VII API.
  2. Use the returned uri field directly:
Authorization Code flow offer response
{
  "uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftenant.vii.mattr.global%22%2C%22credentials%22%3A%5B%2220d6bbe6-a978-447c-b5bd-f33b6dca19e2%22%5D%7D"
}
Pre-authorized Code flow offer response
{
  "id": "6f9d3c7e-2a14-4e5e-9c0b-6a3c4b2f9d81",
  "userId": "c8e7b6a2-4d3f-4e3b-9d1a-2f6b1c9e5a42",
  "uri": "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Ftenant.vii.mattr.global%22%2C%22credentials%22%3A%5B%2220d6bbe6-a978-447c-b5bd-f33b6dca19e2%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22eyJhbGc...%22%7D%7D%7D",
  "expiresAt": "2025-05-01T00:01:00.000Z",
  "transactionCode": 493536
}
  1. Present the offer to users as a QR code or deep link.

Trade-offs:

  • ✅ Works with any compatible wallet application.
  • ✅ No additional implementation required.
  • ✅ Standards-compliant and widely supported.
  • ⚠️ Users may see multiple wallet options if they have several installed.
  • ⚠️ Cannot guarantee which specific app will handle the offer.

Requirements for wallet applications:

For wallet apps to claim these offers, they must be configured to handle the openid-credential-offer:// scheme. See the holder guide for implementation details.

Private-use URI scheme

A private-use URI scheme uses reverse-domain notation (e.g., com.example.wallet://) to target a specific wallet application. While this doesn't prevent other apps from registering the same scheme, it makes conflicts less likely due to the unique nature of reverse-domain names.

When to use:

  • You want to target a specific wallet app you control or partner with.
  • You need better targeting than the standard scheme but don't require the security guarantees of domain verification.
  • You're operating in a controlled environment where you know which apps users have installed.

How it works:

You transform the MATTR VII generated offer URI by encoding it within a custom URI scheme that your wallet app is configured to handle.

Implementation:

  1. Create a credential offer using the MATTR VII API.
  2. Extract the uri field from the response.
  3. Transform the URI to use your custom scheme:
Transform to custom scheme
// Original URI from MATTR VII
const originalUri = "openid-credential-offer://?credential_offer=%7B%22credential_issuer...";

// Base64 URL encode the original URI
const encodedOffer = btoa(originalUri)
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '');

// Construct custom scheme URI
const customUri = `com.example.wallet://accept/${encodedOffer}`;
// Result: com.example.wallet://accept/b3BlbmlkLWNyZWRlbnRpYWwtb2ZmZXI6Ly8_Y3JlZGVudGlhbF9vZmZlcj0lN0IlMjJjcmVkZW50aWFsX2lzc3Vlci4uLg

Or using a query parameter:

Transform to custom scheme with query parameter
const customUri = `com.example.wallet://accept?offer=${encodedOffer}`;
// Result: com.example.wallet://accept?offer=b3BlbmlkLWNyZWRlbnRpYWwtb2ZmZXI6Ly8_Y3JlZGVudGlhbF9vZmZlcj0lN0IlMjJjcmVkZW50aWFsX2lzc3Vlci4uLg
  1. Present the transformed URI to users as a QR code or deep link.

Trade-offs:

  • ✅ Better targeting of a specific wallet app.
  • ✅ Less likely to conflict with other apps than the standard scheme.
  • ✅ Relatively simple to implement.
  • ⚠️ Doesn't prevent other apps from registering the same scheme.
  • ⚠️ Requires coordination with the wallet app developer to know their custom scheme.
  • ⚠️ Not as secure as domain-verified HTTPS schemes.

Requirements for wallet applications:

The wallet app must:

  • Register to handle your custom URI scheme (e.g., com.example.wallet://).
  • Decode the base64 URL-encoded offer and extract the original OpenID4VCI offer URI.
  • Process the offer using the standard OID4VCI flow.

See the holder guide for implementation details.

HTTPS schemes use domain-verified App Links (Android) or Universal Links (iOS) to ensure that only your specific app can handle credential offers from your domain. This provides the highest level of security and control.

When to use:

  • You need to guarantee that only your specific wallet app can handle offers.
  • You're deploying in production with strict security requirements.
  • You want the smoothest user experience with no app selection prompts.
  • You control both the issuing system and the wallet application.

How it works:

You transform the MATTR VII generated offer URI into an HTTPS URL pointing to your domain. The operating system verifies that your domain is registered to open your specific app through association files hosted on your web server.

Implementation:

Step 1: Set up domain verification files

Host the following verification files on your web server:

For iOS (Universal Links):

Create apple-app-site-association (no file extension) at https://yourdomain.com/.well-known/apple-app-site-association:

apple-app-site-association
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAM_ID.com.example.wallet",
        "paths": ["/wallet/*"]
      }
    ]
  }
}

Replace TEAM_ID and com.example.wallet with your Apple Developer Team ID and the wallet app's bundle identifier.

For Android (App Links):

Create assetlinks.json at https://yourdomain.com/.well-known/assetlinks.json:

assetlinks.json
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.wallet",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
      ]
    }
  }
]

Replace com.example.wallet and the fingerprint with your wallet app's package name and SHA-256 certificate fingerprint.

Ensure both files:

  • Are served with Content-Type: application/json.
  • Are accessible via HTTPS without redirects.
  • Have correct permissions (readable by web servers).

Step 2: Transform credential offers

  1. Create a credential offer using the MATTR VII API.
  2. Extract the uri field from the response.
  3. Transform the URI to use your HTTPS domain:
Transform to HTTPS scheme
// Original URI from MATTR VII
const originalUri = "openid-credential-offer://?credential_offer=%7B%22credential_issuer...";

// Base64 URL encode the original URI
const encodedOffer = btoa(originalUri)
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '');

// Construct HTTPS URI
const httpsUri = `https://yourdomain.com/wallet/accept?offer=${encodedOffer}`;
// Result: https://yourdomain.com/wallet/accept?offer=b3BlbmlkLWNyZWRlbnRpYWwtb2ZmZXI6Ly8_Y3JlZGVudGlhbF9vZmZlcj0lN0IlMjJjcmVkZW50aWFsX2lzc3Vlci4uLg
  1. Present the transformed URI to users as a QR code or deep link.

Step 3: Verify setup

Test that the domain verification is working:

iOS:

  • Install your app on a physical device (Universal Links don't work in the simulator).
  • Open the HTTPS link in Safari.
  • The app should open automatically without showing a browser page.

Android:

  • Install your app on a device.
  • Run: adb shell pm get-app-links com.example.wallet
  • Verify the domain shows as "verified".

Trade-offs:

  • ✅ Guarantees only your specific app can handle offers from your domain.
  • ✅ Smoothest user experience with no app selection prompts.
  • ✅ Highest level of security and control.
  • ✅ Professional appearance with branded domain links.
  • ⚠️ Requires domain ownership and web server configuration.
  • ⚠️ More complex setup than custom schemes.
  • ⚠️ Requires coordination between issuing and wallet teams.

Requirements for wallet applications:

The wallet app must:

  • Be configured with Associated Domains (iOS) or intent filters with autoVerify="true" (Android).
  • Handle HTTPS URLs from your domain.
  • Decode the base64 URL-encoded offer and extract the original OpenID4VCI offer URI.
  • Process the offer using the standard OID4VCI flow.

See the holder guide for implementation details.

Security considerations

Understanding the security implications of each URI scheme is important for choosing the right approach:

URI scheme limitations:

Using a specific URI scheme (custom or private-use) does not prevent other applications from claiming the offer entirely. A technically sophisticated user or malicious app could still extract the offer URI from a QR code or deep link and claim it with a different wallet application.

URI schemes are primarily a user experience mechanism to:

  • Make it easier to scan a QR code or follow a deep link and have the OS open the intended app.
  • Reduce confusion by limiting which apps are presented as options.
  • For HTTPS schemes, provide verified domain ownership to ensure your app is the default handler.

Enforcing app-level security:

Currently the only way to fully restrict which app can claim a credential offer is through the OID4VCI Authorization Code flow with restricted redirect URIs. This involves:

  1. Configuring allowed redirect URIs on the MATTR VII side when setting up your tenant.
  2. Specifying a redirect URI that only your app can handle (typically using a claimed HTTPS scheme).
  3. During authentication, the user is redirected back to this URI, which only your app can intercept.

This ensures that only your app can complete the issuance process, even if another app intercepts the initial offer.

Client attestation is a tech-preview feature that enables credential issuers to restrict credential issuance to trusted wallet applications only. It requires the wallet application to present a valid client attestation token during the issuance process. The issuer can verify this token to ensure the request is coming from a trusted source. This provides a much stronger security guarantee than relying on URI schemes alone, as it prevents unauthorized applications from claiming credentials even if they can intercept the offer URI.

Please contact us if you need help configuring restricted redirect URIs for your tenant.

Presenting credential offers to users

After generating a credential offer (regardless of which URI scheme you've chosen), there are two common ways to present the offer to users. Consider offering both options to accommodate different user contexts and preferences.

The URI scheme you chose determines how the offer is handled once the user interacts with it. The presentation method determines where and how the user first encounters the offer.

For situations where you expect the offer to be presented on the user's mobile device (where they would likely have the holder app installed), you can treat the offer as a hyperlink or button that will invoke your app to handle the offer.

Best practices:

  • Make the button or link visually prominent and clearly labeled (e.g., "Add to Wallet", "Claim Credential").
  • Consider providing a fallback QR code option for users who encounter issues with the direct link.

Displayed as a QR code (cross-device)

For situations where you expect the offer to be presented on a screen other than the user's mobile device (such as a desktop computer or even a printed leaflet), you can display a QR code that can be scanned by the user's device to pick up the offer.

Best practices:

  • Ensure the QR code is large enough to be easily scanned (200px square is generally sufficient).
  • Consider providing instructions or visual cues to guide users on how to scan the code with their wallet app.
  • For users already on a mobile device, consider also displaying a button or link as an alternative to scanning.

How would you rate this page?

On this page